Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/viro/vfs-2.6
* 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/viro/vfs-2.6: (41 commits)
fs: add documentation on fallocate hole punching
Gfs2: fail if we try to use hole punch
Btrfs: fail if we try to use hole punch
Ext4: fail if we try to use hole punch
Ocfs2: handle hole punching via fallocate properly
XFS: handle hole punching via fallocate properly
fs: add hole punching to fallocate
vfs: pass struct file to do_truncate on O_TRUNC opens (try #2)
fix signedness mess in rw_verify_area() on 64bit architectures
fs: fix kernel-doc for dcache::prepend_path
fs: fix kernel-doc for dcache::d_validate
sanitize ecryptfs ->mount()
switch afs
move internal-only parts of ncpfs headers to fs/ncpfs
switch ncpfs
switch 9p
pass default dentry_operations to mount_pseudo()
switch hostfs
switch affs
switch configfs
...
diff --git a/CREDITS b/CREDITS
index 494b6e4..1d39a6d 100644
--- a/CREDITS
+++ b/CREDITS
@@ -2811,8 +2811,8 @@
N: Stelian Pop
E: stelian@popies.net
P: 1024D/EDBB6147 7B36 0E07 04BC 11DC A7A0 D3F7 7185 9E7A EDBB 6147
-D: sonypi, meye drivers, mct_u232 usb serial hacks
-S: Paris, France
+D: random kernel hacks
+S: Paimpont, France
N: Pete Popov
E: pete_popov@yahoo.com
diff --git a/Documentation/ABI/testing/sysfs-class-led b/Documentation/ABI/testing/sysfs-class-led
index 9e4541d..edff663 100644
--- a/Documentation/ABI/testing/sysfs-class-led
+++ b/Documentation/ABI/testing/sysfs-class-led
@@ -26,3 +26,12 @@
scheduler is chosen. Trigger specific parameters can appear in
/sys/class/leds/<led> once a given trigger is selected.
+What: /sys/class/leds/<led>/inverted
+Date: January 2011
+KernelVersion: 2.6.38
+Contact: Richard Purdie <rpurdie@rpsys.net>
+Description:
+ Invert the LED on/off state. This parameter is specific to
+ gpio and backlight triggers. In case of the backlight trigger,
+ it is usefull when driving a LED which is intended to indicate
+ a device in a standby like state.
diff --git a/Documentation/DocBook/mtdnand.tmpl b/Documentation/DocBook/mtdnand.tmpl
index 020ac80..620eb3f 100644
--- a/Documentation/DocBook/mtdnand.tmpl
+++ b/Documentation/DocBook/mtdnand.tmpl
@@ -250,7 +250,7 @@
<title>Device ready function</title>
<para>
If the hardware interface has the ready busy pin of the NAND chip connected to a
- GPIO or other accesible I/O pin, this function is used to read back the state of the
+ GPIO or other accessible I/O pin, this function is used to read back the state of the
pin. The function has no arguments and should return 0, if the device is busy (R/B pin
is low) and 1, if the device is ready (R/B pin is high).
If the hardware interface does not give access to the ready busy pin, then
diff --git a/Documentation/cgroups/cgroup_event_listener.c b/Documentation/cgroups/cgroup_event_listener.c
index 8c2bfc4..3e082f9 100644
--- a/Documentation/cgroups/cgroup_event_listener.c
+++ b/Documentation/cgroups/cgroup_event_listener.c
@@ -91,7 +91,7 @@
if (ret == -1) {
perror("cgroup.event_control "
- "is not accessable any more");
+ "is not accessible any more");
break;
}
diff --git a/Documentation/cgroups/cgroups.txt b/Documentation/cgroups/cgroups.txt
index 190018b..44b8b7a 100644
--- a/Documentation/cgroups/cgroups.txt
+++ b/Documentation/cgroups/cgroups.txt
@@ -355,13 +355,13 @@
To change the set of subsystems bound to a mounted hierarchy, just
remount with different options:
-# mount -o remount,cpuset,ns hier1 /dev/cgroup
+# mount -o remount,cpuset,blkio hier1 /dev/cgroup
-Now memory is removed from the hierarchy and ns is added.
+Now memory is removed from the hierarchy and blkio is added.
-Note this will add ns to the hierarchy but won't remove memory or
+Note this will add blkio to the hierarchy but won't remove memory or
cpuset, because the new options are appended to the old ones:
-# mount -o remount,ns /dev/cgroup
+# mount -o remount,blkio /dev/cgroup
To Specify a hierarchy's release_agent:
# mount -t cgroup -o cpuset,release_agent="/sbin/cpuset_release_agent" \
diff --git a/Documentation/cgroups/memcg_test.txt b/Documentation/cgroups/memcg_test.txt
index b7eecec..fc8fa97 100644
--- a/Documentation/cgroups/memcg_test.txt
+++ b/Documentation/cgroups/memcg_test.txt
@@ -398,7 +398,7 @@
written to move_charge_at_immigrate.
9.10 Memory thresholds
- Memory controler implements memory thresholds using cgroups notification
+ Memory controller implements memory thresholds using cgroups notification
API. You can use Documentation/cgroups/cgroup_event_listener.c to test
it.
diff --git a/Documentation/email-clients.txt b/Documentation/email-clients.txt
index 945ff3f..a0b58e2 100644
--- a/Documentation/email-clients.txt
+++ b/Documentation/email-clients.txt
@@ -104,6 +104,13 @@
As an added bonus you can customise the message creation toolbar menu
and put the "insert file" icon there.
+Make the the composer window wide enough so that no lines wrap. As of
+KMail 1.13.5 (KDE 4.5.4), KMail will apply word wrapping when sending
+the email if the lines wrap in the composer window. Having word wrapping
+disabled in the Options menu isn't enough. Thus, if your patch has very
+long lines, you must make the composer window very wide before sending
+the email. See: https://bugs.kde.org/show_bug.cgi?id=174034
+
You can safely GPG sign attachments, but inlined text is preferred for
patches so do not GPG sign them. Signing patches that have been inserted
as inlined text will make them tricky to extract from their 7-bit encoding.
@@ -179,26 +186,8 @@
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Thunderbird (GUI)
-By default, thunderbird likes to mangle text, but there are ways to
-coerce it into being nice.
-
-- Under account settings, composition and addressing, uncheck "Compose
- messages in HTML format".
-
-- Edit your Thunderbird config settings to tell it not to wrap lines:
- user_pref("mailnews.wraplength", 0);
-
-- Edit your Thunderbird config settings so that it won't use format=flowed:
- user_pref("mailnews.send_plaintext_flowed", false);
-
-- You need to get Thunderbird into preformat mode:
-. If you compose HTML messages by default, it's not too hard. Just select
- "Preformat" from the drop-down box just under the subject line.
-. If you compose in text by default, you have to tell it to compose a new
- message in HTML (just as a one-off), and then force it from there back to
- text, else it will wrap lines. To do this, use shift-click on the Write
- icon to compose to get HTML compose mode, then select "Preformat" from
- the drop-down box just under the subject line.
+Thunderbird is an Outlook clone that likes to mangle text, but there are ways
+to coerce it into behaving.
- Allows use of an external editor:
The easiest thing to do with Thunderbird and patches is to use an
@@ -208,6 +197,27 @@
View->Toolbars->Customize... and finally just click on it when in the
Compose dialog.
+To beat some sense out of the internal editor, do this:
+
+- Under account settings, composition and addressing, uncheck "Compose
+ messages in HTML format".
+
+- Edit your Thunderbird config settings so that it won't use format=flowed.
+ Go to "edit->preferences->advanced->config editor" to bring up the
+ thunderbird's registry editor, and set "mailnews.send_plaintext_flowed" to
+ "false".
+
+- Enable "preformat" mode: Shft-click on the Write icon to bring up the HTML
+ composer, select "Preformat" from the drop-down box just under the subject
+ line, then close the message without saving. (This setting also applies to
+ the text composer, but the only control for it is in the HTML composer.)
+
+- Install the "toggle wordwrap" extension. Download the file from:
+ https://addons.mozilla.org/thunderbird/addon/2351/
+ Then go to "tools->add ons", select "install" at the bottom of the screen,
+ and browse to where you saved the .xul file. This adds an "Enable
+ Wordwrap" entry under the Options menu of the message composer.
+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
TkRat (GUI)
diff --git a/Documentation/feature-removal-schedule.txt b/Documentation/feature-removal-schedule.txt
index 22f1081..6cbbd20 100644
--- a/Documentation/feature-removal-schedule.txt
+++ b/Documentation/feature-removal-schedule.txt
@@ -193,6 +193,20 @@
---------------------------
+What: CS5535/CS5536 obsolete GPIO driver
+When: June 2011
+Files: drivers/staging/cs5535_gpio/*
+Check: drivers/staging/cs5535_gpio/cs5535_gpio.c
+Why: A newer driver replaces this; it is drivers/gpio/cs5535-gpio.c, and
+ integrates with the Linux GPIO subsystem. The old driver has been
+ moved to staging, and will be removed altogether around 2.6.40.
+ Please test the new driver, and ensure that the functionality you
+ need and any bugfixes from the old driver are available in the new
+ one.
+Who: Andres Salomon <dilinger@queued.net>
+
+--------------------------
+
What: remove EXPORT_SYMBOL(kernel_thread)
When: August 2006
Files: arch/*/kernel/*_ksyms.c
@@ -576,3 +590,13 @@
Who: Tejun Heo <tj@kernel.org>
----------------------------
+
+What: Legacy, non-standard chassis intrusion detection interface.
+When: June 2011
+Why: The adm9240, w83792d and w83793 hardware monitoring drivers have
+ legacy interfaces for chassis intrusion detection. A standard
+ interface has been added to each driver, so the legacy interface
+ can be removed.
+Who: Jean Delvare <khali@linux-fr.org>
+
+----------------------------
diff --git a/Documentation/filesystems/ntfs.txt b/Documentation/filesystems/ntfs.txt
index ac2a261..6ef8cf3 100644
--- a/Documentation/filesystems/ntfs.txt
+++ b/Documentation/filesystems/ntfs.txt
@@ -457,6 +457,9 @@
Note, a technical ChangeLog aimed at kernel hackers is in fs/ntfs/ChangeLog.
+2.1.30:
+ - Fix writev() (it kept writing the first segment over and over again
+ instead of moving onto subsequent segments).
2.1.29:
- Fix a deadlock when mounting read-write.
2.1.28:
diff --git a/Documentation/gpio.txt b/Documentation/gpio.txt
index 792faa3..a492d92 100644
--- a/Documentation/gpio.txt
+++ b/Documentation/gpio.txt
@@ -135,7 +135,7 @@
int gpio_direction_input(unsigned gpio);
int gpio_direction_output(unsigned gpio, int value);
-The return value is zero for success, else a negative errno. It should
+The return value is zero for success, else a negative errno. It must
be checked, since the get/set calls don't have error returns and since
misconfiguration is possible. You should normally issue these calls from
a task context. However, for spinlock-safe GPIOs it's OK to use them
diff --git a/Documentation/hwmon/adm9240 b/Documentation/hwmon/adm9240
index 2c6f1fe..36e8ec6 100644
--- a/Documentation/hwmon/adm9240
+++ b/Documentation/hwmon/adm9240
@@ -155,7 +155,7 @@
The ADM9240 provides an internal open drain on this line, and may output
a 20 ms active low pulse to reset an external Chassis Intrusion latch.
-Clear the CI latch by writing value 1 to the sysfs chassis_clear file.
+Clear the CI latch by writing value 0 to the sysfs intrusion0_alarm file.
Alarm flags reported as 16-bit word
diff --git a/Documentation/hwmon/ads7828 b/Documentation/hwmon/ads7828
index 75bc4be..2bbebe6 100644
--- a/Documentation/hwmon/ads7828
+++ b/Documentation/hwmon/ads7828
@@ -9,7 +9,7 @@
http://focus.ti.com/lit/ds/symlink/ads7828.pdf
Authors:
- Steve Hardy <steve@linuxrealtime.co.uk>
+ Steve Hardy <shardy@redhat.com>
Module Parameters
-----------------
diff --git a/Documentation/hwmon/dme1737 b/Documentation/hwmon/dme1737
index fc5df76..4d29351 100644
--- a/Documentation/hwmon/dme1737
+++ b/Documentation/hwmon/dme1737
@@ -42,7 +42,7 @@
This driver implements support for the hardware monitoring capabilities of the
SMSC DME1737 and Asus A8000 (which are the same), SMSC SCH5027, SCH311x,
and SCH5127 Super-I/O chips. These chips feature monitoring of 3 temp sensors
-temp[1-3] (2 remote diodes and 1 internal), 7 voltages in[0-6] (6 external and
+temp[1-3] (2 remote diodes and 1 internal), 8 voltages in[0-7] (7 external and
1 internal) and up to 6 fan speeds fan[1-6]. Additionally, the chips implement
up to 5 PWM outputs pwm[1-3,5-6] for controlling fan speeds both manually and
automatically.
@@ -105,6 +105,7 @@
in4: V1_IN 0V - 1.5V
in5: VTR (+3.3V standby) 0V - 4.38V
in6: Vbat (+3.0V) 0V - 4.38V
+ in7: Vtrip (+1.5V) 0V - 1.99V
Each voltage input has associated min and max limits which trigger an alarm
when crossed.
@@ -217,10 +218,10 @@
vrm RW Voltage regulator module version
number.
-in[0-6]_input RO Measured voltage in millivolts.
-in[0-6]_min RW Low limit for voltage input.
-in[0-6]_max RW High limit for voltage input.
-in[0-6]_alarm RO Voltage input alarm. Returns 1 if
+in[0-7]_input RO Measured voltage in millivolts.
+in[0-7]_min RW Low limit for voltage input.
+in[0-7]_max RW High limit for voltage input.
+in[0-7]_alarm RO Voltage input alarm. Returns 1 if
voltage input is or went outside the
associated min-max range, 0 otherwise.
@@ -324,3 +325,4 @@
pwm5 opt opt
fan6 opt opt
pwm6 opt opt
+in7 yes
diff --git a/Documentation/hwmon/w83627hf b/Documentation/hwmon/w83627hf
index fb145e5..8432e11 100644
--- a/Documentation/hwmon/w83627hf
+++ b/Documentation/hwmon/w83627hf
@@ -91,3 +91,25 @@
The above sequence assumes a Super-I/O config space at 0x2e/0x2f, but
0x4e/0x4f is also possible.
+
+Voltage pin mapping
+-------------------
+
+Here is a summary of the voltage pin mapping for the W83627THF. This
+can be useful to convert data provided by board manufacturers into
+working libsensors configuration statements.
+
+ W83627THF |
+ Pin | Name | Register | Sysfs attribute
+-----------------------------------------------------
+ 100 | CPUVCORE | 20h | in0
+ 99 | VIN0 | 21h | in1
+ 98 | VIN1 | 22h | in2
+ 97 | VIN2 | 24h | in4
+ 114 | AVCC | 23h | in3
+ 61 | 5VSB | 50h (bank 5) | in7
+ 74 | VBAT | 51h (bank 5) | in8
+
+For other supported devices, you'll have to take the hard path and
+look up the information in the datasheet yourself (and then add it
+to this document please.)
diff --git a/Documentation/hwmon/w83793 b/Documentation/hwmon/w83793
index 51171a8..6cc5f63 100644
--- a/Documentation/hwmon/w83793
+++ b/Documentation/hwmon/w83793
@@ -92,7 +92,7 @@
* Chassis
If the case open alarm triggers, it will stay in this state unless cleared
- by any write to the sysfs file "chassis".
+ by writing 0 to the sysfs file "intrusion0_alarm".
* VID and VRM
The VRM version is detected automatically, don't modify the it unless you
diff --git a/Documentation/input/ff.txt b/Documentation/input/ff.txt
index ded4d5f..b3867bf 100644
--- a/Documentation/input/ff.txt
+++ b/Documentation/input/ff.txt
@@ -49,7 +49,9 @@
#include <linux/input.h>
#include <sys/ioctl.h>
-unsigned long features[1 + FF_MAX/sizeof(unsigned long)];
+#define BITS_TO_LONGS(x) \
+ (((x) + 8 * sizeof (unsigned long) - 1) / (8 * sizeof (unsigned long)))
+unsigned long features[BITS_TO_LONGS(FF_CNT)];
int ioctl(int file_descriptor, int request, unsigned long *features);
"request" must be EVIOCGBIT(EV_FF, size of features array in bytes )
diff --git a/Documentation/ioctl/ioctl-number.txt b/Documentation/ioctl/ioctl-number.txt
index d6a63c7..ac293e9 100644
--- a/Documentation/ioctl/ioctl-number.txt
+++ b/Documentation/ioctl/ioctl-number.txt
@@ -247,7 +247,7 @@
'p' 40-7F linux/nvram.h
'p' 80-9F linux/ppdev.h user-space parport
<mailto:tim@cyberelk.net>
-'p' A1-A4 linux/pps.h LinuxPPS
+'p' A1-A5 linux/pps.h LinuxPPS
<mailto:giometti@linux.it>
'q' 00-1F linux/serio.h
'q' 80-FF linux/telephony.h Internet PhoneJACK, Internet LineJACK
diff --git a/Documentation/iostats.txt b/Documentation/iostats.txt
index 59a69ec..f6dece5 100644
--- a/Documentation/iostats.txt
+++ b/Documentation/iostats.txt
@@ -81,7 +81,7 @@
The only field that should go to zero. Incremented as requests are
given to appropriate struct request_queue and decremented as they finish.
Field 10 -- # of milliseconds spent doing I/Os
- This field is increases so long as field 9 is nonzero.
+ This field increases so long as field 9 is nonzero.
Field 11 -- weighted # of milliseconds spent doing I/Os
This field is incremented at each I/O start, I/O completion, I/O
merge, or read of these stats by the number of I/Os in progress
diff --git a/Documentation/kdump/kdump.txt b/Documentation/kdump/kdump.txt
index cab61d8..7a9e0b4 100644
--- a/Documentation/kdump/kdump.txt
+++ b/Documentation/kdump/kdump.txt
@@ -65,18 +65,21 @@
2) Download the kexec-tools user-space package from the following URL:
-http://www.kernel.org/pub/linux/kernel/people/horms/kexec-tools/kexec-tools.tar.gz
+http://kernel.org/pub/linux/utils/kernel/kexec/kexec-tools.tar.gz
This is a symlink to the latest version.
The latest kexec-tools git tree is available at:
-git://git.kernel.org/pub/scm/linux/kernel/git/horms/kexec-tools.git
-or
-http://www.kernel.org/git/?p=linux/kernel/git/horms/kexec-tools.git
+git://git.kernel.org/pub/scm/utils/kernel/kexec/kexec-tools.git
+and
+http://www.kernel.org/pub/scm/utils/kernel/kexec/kexec-tools.git
+
+There is also a gitweb interface available at
+http://www.kernel.org/git/?p=utils/kernel/kexec/kexec-tools.git
More information about kexec-tools can be found at
-http://www.kernel.org/pub/linux/kernel/people/horms/kexec-tools/README.html
+http://www.kernel.org/pub/linux/utils/kernel/kexec/README.html
3) Unpack the tarball with the tar command, as follows:
@@ -439,6 +442,6 @@
Contact
=======
-Vivek Goyal (vgoyal@in.ibm.com)
+Vivek Goyal (vgoyal@redhat.com)
Maneesh Soni (maneesh@in.ibm.com)
diff --git a/Documentation/kernel-parameters.txt b/Documentation/kernel-parameters.txt
index ed3708f..55fe759 100644
--- a/Documentation/kernel-parameters.txt
+++ b/Documentation/kernel-parameters.txt
@@ -659,11 +659,6 @@
dscc4.setup= [NET]
- dynamic_printk Enables pr_debug()/dev_dbg() calls if
- CONFIG_DYNAMIC_PRINTK_DEBUG has been enabled.
- These can also be switched on/off via
- <debugfs>/dynamic_printk/modules
-
earlycon= [KNL] Output early console device and options.
uart[8250],io,<addr>[,options]
uart[8250],mmio,<addr>[,options]
@@ -888,6 +883,7 @@
controller
i8042.nopnp [HW] Don't use ACPIPnP / PnPBIOS to discover KBD/AUX
controllers
+ i8042.notimeout [HW] Ignore timeout condition signalled by conroller
i8042.reset [HW] Reset the controller during init and cleanup
i8042.unlock [HW] Unlock (ignore) the keylock
@@ -1709,6 +1705,9 @@
no-kvmclock [X86,KVM] Disable paravirtualized KVM clock driver
+ no-kvmapf [X86,KVM] Disable paravirtualized asynchronous page
+ fault handling.
+
nolapic [X86-32,APIC] Do not enable or use the local APIC.
nolapic_timer [X86-32,APIC] Do not use the local APIC timer.
diff --git a/Documentation/ko_KR/HOWTO b/Documentation/ko_KR/HOWTO
index e3a55b6..ab5189a 100644
--- a/Documentation/ko_KR/HOWTO
+++ b/Documentation/ko_KR/HOWTO
@@ -391,8 +391,8 @@
bugme-janitor 메일링 리스트(bugzilla에 모든 변화들이 여기서 메일로 전해진다)
에 등록하면 된다.
- http://lists.osdl.org/mailman/listinfo/bugme-new
- http://lists.osdl.org/mailman/listinfo/bugme-janitors
+ https://lists.linux-foundation.org/mailman/listinfo/bugme-new
+ https://lists.linux-foundation.org/mailman/listinfo/bugme-janitors
diff --git a/Documentation/kprobes.txt b/Documentation/kprobes.txt
index 741fe66..0cfb00f 100644
--- a/Documentation/kprobes.txt
+++ b/Documentation/kprobes.txt
@@ -598,7 +598,7 @@
a) The instructions in DCR must be relocatable.
b) The instructions in DCR must not include a call instruction.
c) JTPR must not be targeted by any jump or call instruction.
-d) DCR must not straddle the border betweeen functions.
+d) DCR must not straddle the border between functions.
Anyway, these limitations are checked by the in-kernel instruction
decoder, so you don't need to worry about that.
diff --git a/Documentation/kvm/api.txt b/Documentation/kvm/api.txt
index b336266..ad85797 100644
--- a/Documentation/kvm/api.txt
+++ b/Documentation/kvm/api.txt
@@ -874,7 +874,7 @@
- KVM_MP_STATE_HALTED: the vcpu has executed a HLT instruction and
is waiting for an interrupt
- KVM_MP_STATE_SIPI_RECEIVED: the vcpu has just received a SIPI (vector
- accesible via KVM_GET_VCPU_EVENTS)
+ accessible via KVM_GET_VCPU_EVENTS)
This ioctl is only useful after KVM_CREATE_IRQCHIP. Without an in-kernel
irqchip, the multiprocessing state must be maintained by userspace.
@@ -1085,6 +1085,184 @@
If any additional field gets added to this structure later on, a bit for that
additional piece of information will be set in the flags bitmap.
+4.47 KVM_ASSIGN_PCI_DEVICE
+
+Capability: KVM_CAP_DEVICE_ASSIGNMENT
+Architectures: x86 ia64
+Type: vm ioctl
+Parameters: struct kvm_assigned_pci_dev (in)
+Returns: 0 on success, -1 on error
+
+Assigns a host PCI device to the VM.
+
+struct kvm_assigned_pci_dev {
+ __u32 assigned_dev_id;
+ __u32 busnr;
+ __u32 devfn;
+ __u32 flags;
+ __u32 segnr;
+ union {
+ __u32 reserved[11];
+ };
+};
+
+The PCI device is specified by the triple segnr, busnr, and devfn.
+Identification in succeeding service requests is done via assigned_dev_id. The
+following flags are specified:
+
+/* Depends on KVM_CAP_IOMMU */
+#define KVM_DEV_ASSIGN_ENABLE_IOMMU (1 << 0)
+
+4.48 KVM_DEASSIGN_PCI_DEVICE
+
+Capability: KVM_CAP_DEVICE_DEASSIGNMENT
+Architectures: x86 ia64
+Type: vm ioctl
+Parameters: struct kvm_assigned_pci_dev (in)
+Returns: 0 on success, -1 on error
+
+Ends PCI device assignment, releasing all associated resources.
+
+See KVM_CAP_DEVICE_ASSIGNMENT for the data structure. Only assigned_dev_id is
+used in kvm_assigned_pci_dev to identify the device.
+
+4.49 KVM_ASSIGN_DEV_IRQ
+
+Capability: KVM_CAP_ASSIGN_DEV_IRQ
+Architectures: x86 ia64
+Type: vm ioctl
+Parameters: struct kvm_assigned_irq (in)
+Returns: 0 on success, -1 on error
+
+Assigns an IRQ to a passed-through device.
+
+struct kvm_assigned_irq {
+ __u32 assigned_dev_id;
+ __u32 host_irq;
+ __u32 guest_irq;
+ __u32 flags;
+ union {
+ struct {
+ __u32 addr_lo;
+ __u32 addr_hi;
+ __u32 data;
+ } guest_msi;
+ __u32 reserved[12];
+ };
+};
+
+The following flags are defined:
+
+#define KVM_DEV_IRQ_HOST_INTX (1 << 0)
+#define KVM_DEV_IRQ_HOST_MSI (1 << 1)
+#define KVM_DEV_IRQ_HOST_MSIX (1 << 2)
+
+#define KVM_DEV_IRQ_GUEST_INTX (1 << 8)
+#define KVM_DEV_IRQ_GUEST_MSI (1 << 9)
+#define KVM_DEV_IRQ_GUEST_MSIX (1 << 10)
+
+It is not valid to specify multiple types per host or guest IRQ. However, the
+IRQ type of host and guest can differ or can even be null.
+
+4.50 KVM_DEASSIGN_DEV_IRQ
+
+Capability: KVM_CAP_ASSIGN_DEV_IRQ
+Architectures: x86 ia64
+Type: vm ioctl
+Parameters: struct kvm_assigned_irq (in)
+Returns: 0 on success, -1 on error
+
+Ends an IRQ assignment to a passed-through device.
+
+See KVM_ASSIGN_DEV_IRQ for the data structure. The target device is specified
+by assigned_dev_id, flags must correspond to the IRQ type specified on
+KVM_ASSIGN_DEV_IRQ. Partial deassignment of host or guest IRQ is allowed.
+
+4.51 KVM_SET_GSI_ROUTING
+
+Capability: KVM_CAP_IRQ_ROUTING
+Architectures: x86 ia64
+Type: vm ioctl
+Parameters: struct kvm_irq_routing (in)
+Returns: 0 on success, -1 on error
+
+Sets the GSI routing table entries, overwriting any previously set entries.
+
+struct kvm_irq_routing {
+ __u32 nr;
+ __u32 flags;
+ struct kvm_irq_routing_entry entries[0];
+};
+
+No flags are specified so far, the corresponding field must be set to zero.
+
+struct kvm_irq_routing_entry {
+ __u32 gsi;
+ __u32 type;
+ __u32 flags;
+ __u32 pad;
+ union {
+ struct kvm_irq_routing_irqchip irqchip;
+ struct kvm_irq_routing_msi msi;
+ __u32 pad[8];
+ } u;
+};
+
+/* gsi routing entry types */
+#define KVM_IRQ_ROUTING_IRQCHIP 1
+#define KVM_IRQ_ROUTING_MSI 2
+
+No flags are specified so far, the corresponding field must be set to zero.
+
+struct kvm_irq_routing_irqchip {
+ __u32 irqchip;
+ __u32 pin;
+};
+
+struct kvm_irq_routing_msi {
+ __u32 address_lo;
+ __u32 address_hi;
+ __u32 data;
+ __u32 pad;
+};
+
+4.52 KVM_ASSIGN_SET_MSIX_NR
+
+Capability: KVM_CAP_DEVICE_MSIX
+Architectures: x86 ia64
+Type: vm ioctl
+Parameters: struct kvm_assigned_msix_nr (in)
+Returns: 0 on success, -1 on error
+
+Set the number of MSI-X interrupts for an assigned device. This service can
+only be called once in the lifetime of an assigned device.
+
+struct kvm_assigned_msix_nr {
+ __u32 assigned_dev_id;
+ __u16 entry_nr;
+ __u16 padding;
+};
+
+#define KVM_MAX_MSIX_PER_DEV 256
+
+4.53 KVM_ASSIGN_SET_MSIX_ENTRY
+
+Capability: KVM_CAP_DEVICE_MSIX
+Architectures: x86 ia64
+Type: vm ioctl
+Parameters: struct kvm_assigned_msix_entry (in)
+Returns: 0 on success, -1 on error
+
+Specifies the routing of an MSI-X assigned device interrupt to a GSI. Setting
+the GSI vector to zero means disabling the interrupt.
+
+struct kvm_assigned_msix_entry {
+ __u32 assigned_dev_id;
+ __u32 gsi;
+ __u16 entry; /* The index of entry in the MSI-X table */
+ __u16 padding[3];
+};
+
5. The kvm_run structure
Application code obtains a pointer to the kvm_run structure by
diff --git a/Documentation/kvm/cpuid.txt b/Documentation/kvm/cpuid.txt
index 14a12ea..8820685 100644
--- a/Documentation/kvm/cpuid.txt
+++ b/Documentation/kvm/cpuid.txt
@@ -36,6 +36,9 @@
KVM_FEATURE_CLOCKSOURCE2 || 3 || kvmclock available at msrs
|| || 0x4b564d00 and 0x4b564d01
------------------------------------------------------------------------------
+KVM_FEATURE_ASYNC_PF || 4 || async pf can be enabled by
+ || || writing to msr 0x4b564d02
+------------------------------------------------------------------------------
KVM_FEATURE_CLOCKSOURCE_STABLE_BIT || 24 || host will warn if no guest-side
|| || per-cpu warps are expected in
|| || kvmclock.
diff --git a/Documentation/kvm/msr.txt b/Documentation/kvm/msr.txt
index 8ddcfe8..d079aed 100644
--- a/Documentation/kvm/msr.txt
+++ b/Documentation/kvm/msr.txt
@@ -3,7 +3,6 @@
=====================================================
KVM makes use of some custom MSRs to service some requests.
-At present, this facility is only used by kvmclock.
Custom MSRs have a range reserved for them, that goes from
0x4b564d00 to 0x4b564dff. There are MSRs outside this area,
@@ -151,3 +150,38 @@
return PRESENT;
} else
return NON_PRESENT;
+
+MSR_KVM_ASYNC_PF_EN: 0x4b564d02
+ data: Bits 63-6 hold 64-byte aligned physical address of a
+ 64 byte memory area which must be in guest RAM and must be
+ zeroed. Bits 5-2 are reserved and should be zero. Bit 0 is 1
+ when asynchronous page faults are enabled on the vcpu 0 when
+ disabled. Bit 2 is 1 if asynchronous page faults can be injected
+ when vcpu is in cpl == 0.
+
+ First 4 byte of 64 byte memory location will be written to by
+ the hypervisor at the time of asynchronous page fault (APF)
+ injection to indicate type of asynchronous page fault. Value
+ of 1 means that the page referred to by the page fault is not
+ present. Value 2 means that the page is now available. Disabling
+ interrupt inhibits APFs. Guest must not enable interrupt
+ before the reason is read, or it may be overwritten by another
+ APF. Since APF uses the same exception vector as regular page
+ fault guest must reset the reason to 0 before it does
+ something that can generate normal page fault. If during page
+ fault APF reason is 0 it means that this is regular page
+ fault.
+
+ During delivery of type 1 APF cr2 contains a token that will
+ be used to notify a guest when missing page becomes
+ available. When page becomes available type 2 APF is sent with
+ cr2 set to the token associated with the page. There is special
+ kind of token 0xffffffff which tells vcpu that it should wake
+ up all processes waiting for APFs and no individual type 2 APFs
+ will be sent.
+
+ If APF is disabled while there are outstanding APFs, they will
+ not be delivered.
+
+ Currently type 2 APF will be always delivered on the same vcpu as
+ type 1 was, but guest should not rely on that.
diff --git a/Documentation/lguest/lguest.txt b/Documentation/lguest/lguest.txt
index efb3a6a..6ccaf8e 100644
--- a/Documentation/lguest/lguest.txt
+++ b/Documentation/lguest/lguest.txt
@@ -111,8 +111,11 @@
Then use --tunnet=bridge:lg0 when launching the guest.
- See http://linux-net.osdl.org/index.php/Bridge for general information
- on how to get bridging working.
+ See:
+
+ http://www.linuxfoundation.org/collaborate/workgroups/networking/bridge
+
+ for general information on how to get bridging to work.
There is a helpful mailing list at http://ozlabs.org/mailman/listinfo/lguest
diff --git a/Documentation/networking/bridge.txt b/Documentation/networking/bridge.txt
index bec69a8..a7ba5e4 100644
--- a/Documentation/networking/bridge.txt
+++ b/Documentation/networking/bridge.txt
@@ -1,8 +1,8 @@
In order to use the Ethernet bridging functionality, you'll need the
userspace tools. These programs and documentation are available
-at http://www.linux-foundation.org/en/Net:Bridge. The download page is
+at http://www.linuxfoundation.org/en/Net:Bridge. The download page is
http://prdownloads.sourceforge.net/bridge.
If you still have questions, don't hesitate to post to the mailing list
-(more info http://lists.osdl.org/mailman/listinfo/bridge).
+(more info https://lists.linux-foundation.org/mailman/listinfo/bridge).
diff --git a/Documentation/networking/caif/spi_porting.txt b/Documentation/networking/caif/spi_porting.txt
index 61d7c92..0cb8cb9 100644
--- a/Documentation/networking/caif/spi_porting.txt
+++ b/Documentation/networking/caif/spi_porting.txt
@@ -32,7 +32,7 @@
This function is called by the CAIF SPI interface to give
you a chance to set up your hardware to be ready to receive
a stream of data from the master. The xfer structure contains
- both physical and logical adresses, as well as the total length
+ both physical and logical addresses, as well as the total length
of the transfer in both directions.The dev parameter can be used
to map to different CAIF SPI slave devices.
diff --git a/Documentation/networking/dccp.txt b/Documentation/networking/dccp.txt
index 811872b..d718bc2 100644
--- a/Documentation/networking/dccp.txt
+++ b/Documentation/networking/dccp.txt
@@ -38,11 +38,11 @@
specified in RFCs 4340...42.
The known bugs are at:
- http://linux-net.osdl.org/index.php/TODO#DCCP
+ http://www.linuxfoundation.org/collaborate/workgroups/networking/todo#DCCP
For more up-to-date versions of the DCCP implementation, please consider using
the experimental DCCP test tree; instructions for checking this out are on:
-http://linux-net.osdl.org/index.php/DCCP_Testing#Experimental_DCCP_source_tree
+http://www.linuxfoundation.org/collaborate/workgroups/networking/dccp_testing#Experimental_DCCP_source_tree
Socket options
diff --git a/Documentation/networking/generic_netlink.txt b/Documentation/networking/generic_netlink.txt
index d4f8b8b..3e07111 100644
--- a/Documentation/networking/generic_netlink.txt
+++ b/Documentation/networking/generic_netlink.txt
@@ -1,3 +1,3 @@
A wiki document on how to use Generic Netlink can be found here:
- * http://linux-net.osdl.org/index.php/Generic_Netlink_HOWTO
+ * http://www.linuxfoundation.org/collaborate/workgroups/networking/generic_netlink_howto
diff --git a/Documentation/nfc/nfc-pn544.txt b/Documentation/nfc/nfc-pn544.txt
new file mode 100644
index 0000000..2fcac9f5
--- /dev/null
+++ b/Documentation/nfc/nfc-pn544.txt
@@ -0,0 +1,114 @@
+Kernel driver for the NXP Semiconductors PN544 Near Field
+Communication chip
+
+Author: Jari Vanhala
+Contact: Matti Aaltonen (matti.j.aaltonen at nokia.com)
+
+General
+-------
+
+The PN544 is an integrated transmission module for contactless
+communication. The driver goes under drives/nfc/ and is compiled as a
+module named "pn544". It registers a misc device and creates a device
+file named "/dev/pn544".
+
+Host Interfaces: I2C, SPI and HSU, this driver supports currently only I2C.
+
+The Interface
+-------------
+
+The driver offers a sysfs interface for a hardware test and an IOCTL
+interface for selecting between two operating modes. There are read,
+write and poll functions for transferring messages. The two operating
+modes are the normal (HCI) mode and the firmware update mode.
+
+PN544 is controlled by sending messages from the userspace to the
+chip. The main function of the driver is just to pass those messages
+without caring about the message content.
+
+
+Protocols
+---------
+
+In the normal (HCI) mode and in the firmware update mode read and
+write functions behave a bit differently because the message formats
+or the protocols are different.
+
+In the normal (HCI) mode the protocol used is derived from the ETSI
+HCI specification. The firmware is updated using a specific protocol,
+which is different from HCI.
+
+HCI messages consist of an eight bit header and the message body. The
+header contains the message length. Maximum size for an HCI message is
+33. In HCI mode sent messages are tested for a correct
+checksum. Firmware update messages have the length in the second (MSB)
+and third (LSB) bytes of the message. The maximum FW message length is
+1024 bytes.
+
+For the ETSI HCI specification see
+http://www.etsi.org/WebSite/Technologies/ProtocolSpecification.aspx
+
+The Hardware Test
+-----------------
+
+The idea of the test is that it can performed by reading from the
+corresponding sysfs file. The test is implemented in the board file
+and it should test that PN544 can be put into the firmware update
+mode. If the test is not implemented the sysfs file does not get
+created.
+
+Example:
+> cat /sys/module/pn544/drivers/i2c\:pn544/3-002b/nfc_test
+1
+
+Normal Operation
+----------------
+
+PN544 is powered up when the device file is opened, otherwise it's
+turned off. Only one instance can use the device at a time.
+
+Userspace applications control PN544 with HCI messages. The hardware
+sends an interrupt when data is available for reading. Data is
+physically read when the read function is called by a userspace
+application. Poll() checks the read interrupt state. Configuration and
+self testing are also done from the userspace using read and write.
+
+Example platform data:
+
+static int rx71_pn544_nfc_request_resources(struct i2c_client *client)
+{
+ /* Get and setup the HW resources for the device */
+}
+
+static void rx71_pn544_nfc_free_resources(void)
+{
+ /* Release the HW resources */
+}
+
+static void rx71_pn544_nfc_enable(int fw)
+{
+ /* Turn the device on */
+}
+
+static int rx71_pn544_nfc_test(void)
+{
+ /*
+ * Put the device into the FW update mode
+ * and then back to the normal mode.
+ * Check the behavior and return one on success,
+ * zero on failure.
+ */
+}
+
+static void rx71_pn544_nfc_disable(void)
+{
+ /* turn the power off */
+}
+
+static struct pn544_nfc_platform_data rx71_nfc_data = {
+ .request_resources = rx71_pn544_nfc_request_resources,
+ .free_resources = rx71_pn544_nfc_free_resources,
+ .enable = rx71_pn544_nfc_enable,
+ .test = rx71_pn544_nfc_test,
+ .disable = rx71_pn544_nfc_disable,
+};
diff --git a/Documentation/powerpc/booting-without-of.txt b/Documentation/powerpc/booting-without-of.txt
index 3272ed59..7400d75 100644
--- a/Documentation/powerpc/booting-without-of.txt
+++ b/Documentation/powerpc/booting-without-of.txt
@@ -1098,7 +1098,7 @@
* an arbitrary array of bytes
*/
- childnode@addresss { /* define a child node named "childnode"
+ childnode@address { /* define a child node named "childnode"
* whose unit name is "childnode at
* address"
*/
diff --git a/Documentation/pps/pps.txt b/Documentation/pps/pps.txt
index 125f4ab..d35dcdd 100644
--- a/Documentation/pps/pps.txt
+++ b/Documentation/pps/pps.txt
@@ -170,3 +170,49 @@
Please, note that to compile userland programs you need the file timepps.h
(see Documentation/pps/).
+
+
+Generators
+----------
+
+Sometimes one needs to be able not only to catch PPS signals but to produce
+them also. For example, running a distributed simulation, which requires
+computers' clock to be synchronized very tightly. One way to do this is to
+invent some complicated hardware solutions but it may be neither necessary
+nor affordable. The cheap way is to load a PPS generator on one of the
+computers (master) and PPS clients on others (slaves), and use very simple
+cables to deliver signals using parallel ports, for example.
+
+Parallel port cable pinout:
+pin name master slave
+1 STROBE *------ *
+2 D0 * | *
+3 D1 * | *
+4 D2 * | *
+5 D3 * | *
+6 D4 * | *
+7 D5 * | *
+8 D6 * | *
+9 D7 * | *
+10 ACK * ------*
+11 BUSY * *
+12 PE * *
+13 SEL * *
+14 AUTOFD * *
+15 ERROR * *
+16 INIT * *
+17 SELIN * *
+18-25 GND *-----------*
+
+Please note that parallel port interrupt occurs only on high->low transition,
+so it is used for PPS assert edge. PPS clear edge can be determined only
+using polling in the interrupt handler which actually can be done way more
+precisely because interrupt handling delays can be quite big and random. So
+current parport PPS generator implementation (pps_gen_parport module) is
+geared towards using the clear edge for time synchronization.
+
+Clear edge polling is done with disabled interrupts so it's better to select
+delay between assert and clear edge as small as possible to reduce system
+latencies. But if it is too small slave won't be able to capture clear edge
+transition. The default of 30us should be good enough in most situations.
+The delay can be selected using 'delay' pps_gen_parport module parameter.
diff --git a/Documentation/scheduler/00-INDEX b/Documentation/scheduler/00-INDEX
index 3c00c9c..d2651c4 100644
--- a/Documentation/scheduler/00-INDEX
+++ b/Documentation/scheduler/00-INDEX
@@ -3,7 +3,7 @@
sched-arch.txt
- CPU Scheduler implementation hints for architecture specific code.
sched-design-CFS.txt
- - goals, design and implementation of the Complete Fair Scheduler.
+ - goals, design and implementation of the Completely Fair Scheduler.
sched-domains.txt
- information on scheduling domains.
sched-nice-design.txt
diff --git a/Documentation/scsi/ChangeLog.lpfc b/Documentation/scsi/ChangeLog.lpfc
index 337c924..5e83769 100644
--- a/Documentation/scsi/ChangeLog.lpfc
+++ b/Documentation/scsi/ChangeLog.lpfc
@@ -573,7 +573,7 @@
* Backround nodev_timeout processing to DPC This enables us to
unblock (stop dev_loss_tmo) when appopriate.
* Fix array discovery with multiple luns. The max_luns was 0 at
- the time the host structure was intialized. lpfc_cfg_params
+ the time the host structure was initialized. lpfc_cfg_params
then set the max_luns to the correct value afterwards.
* Remove unused define LPFC_MAX_LUN and set the default value of
lpfc_max_lun parameter to 512.
diff --git a/Documentation/serial/tty.txt b/Documentation/serial/tty.txt
index 7c90050..540db41 100644
--- a/Documentation/serial/tty.txt
+++ b/Documentation/serial/tty.txt
@@ -107,7 +107,7 @@
dcd_change() - Report to the tty line the current DCD pin status
changes and the relative timestamp. The timestamp
- can be NULL.
+ cannot be NULL.
Driver Access
diff --git a/Documentation/sysctl/00-INDEX b/Documentation/sysctl/00-INDEX
index 1286f45..8cf5d49 100644
--- a/Documentation/sysctl/00-INDEX
+++ b/Documentation/sysctl/00-INDEX
@@ -4,8 +4,6 @@
- general information about /proc/sys/ sysctl files.
abi.txt
- documentation for /proc/sys/abi/*.
-ctl_unnumbered.txt
- - explanation of why one should not add new binary sysctl numbers.
fs.txt
- documentation for /proc/sys/fs/*.
kernel.txt
diff --git a/Documentation/sysctl/kernel.txt b/Documentation/sysctl/kernel.txt
index 5740671..11d5ced 100644
--- a/Documentation/sysctl/kernel.txt
+++ b/Documentation/sysctl/kernel.txt
@@ -34,6 +34,7 @@
- hotplug
- java-appletviewer [ binfmt_java, obsolete ]
- java-interpreter [ binfmt_java, obsolete ]
+- kptr_restrict
- kstack_depth_to_print [ X86 only ]
- l2cr [ PPC only ]
- modprobe ==> Documentation/debugging-modules.txt
@@ -261,6 +262,19 @@
==============================================================
+kptr_restrict:
+
+This toggle indicates whether restrictions are placed on
+exposing kernel addresses via /proc and other interfaces. When
+kptr_restrict is set to (0), there are no restrictions. When
+kptr_restrict is set to (1), the default, kernel pointers
+printed using the %pK format specifier will be replaced with 0's
+unless the user has CAP_SYSLOG. When kptr_restrict is set to
+(2), kernel pointers printed using %pK will be replaced with 0's
+regardless of privileges.
+
+==============================================================
+
kstack_depth_to_print: (X86 only)
Controls the number of words to print when dumping the raw
diff --git a/Documentation/timers/timer_stats.txt b/Documentation/timers/timer_stats.txt
index 9bd00fc..8abd40b 100644
--- a/Documentation/timers/timer_stats.txt
+++ b/Documentation/timers/timer_stats.txt
@@ -19,7 +19,7 @@
- the pid of the task(process) which initialized the timer
- the name of the process which initialized the timer
-- the function where the timer was intialized
+- the function where the timer was initialized
- the callback function which is associated to the timer
- the number of events (callbacks)
diff --git a/Documentation/trace/events.txt b/Documentation/trace/events.txt
index 09bd8e9..b510564 100644
--- a/Documentation/trace/events.txt
+++ b/Documentation/trace/events.txt
@@ -125,7 +125,7 @@
For example, here's the information displayed for the 'sched_wakeup'
event:
-# cat /debug/tracing/events/sched/sched_wakeup/format
+# cat /sys/kernel/debug/tracing/events/sched/sched_wakeup/format
name: sched_wakeup
ID: 60
@@ -201,19 +201,19 @@
For example:
-# cd /debug/tracing/events/sched/sched_wakeup
+# cd /sys/kernel/debug/tracing/events/sched/sched_wakeup
# echo "common_preempt_count > 4" > filter
A slightly more involved example:
-# cd /debug/tracing/events/sched/sched_signal_send
+# cd /sys/kernel/debug/tracing/events/signal/signal_generate
# echo "((sig >= 10 && sig < 15) || sig == 17) && comm != bash" > filter
If there is an error in the expression, you'll get an 'Invalid
argument' error when setting it, and the erroneous string along with
an error message can be seen by looking at the filter e.g.:
-# cd /debug/tracing/events/sched/sched_signal_send
+# cd /sys/kernel/debug/tracing/events/signal/signal_generate
# echo "((sig >= 10 && sig < 15) || dsig == 17) && comm != bash" > filter
-bash: echo: write error: Invalid argument
# cat filter
diff --git a/Documentation/w1/slaves/00-INDEX b/Documentation/w1/slaves/00-INDEX
index f8101d6..75613c9 100644
--- a/Documentation/w1/slaves/00-INDEX
+++ b/Documentation/w1/slaves/00-INDEX
@@ -2,3 +2,5 @@
- This file
w1_therm
- The Maxim/Dallas Semiconductor ds18*20 temperature sensor.
+w1_ds2423
+ - The Maxim/Dallas Semiconductor ds2423 counter device.
diff --git a/Documentation/w1/slaves/w1_ds2423 b/Documentation/w1/slaves/w1_ds2423
new file mode 100644
index 0000000..90a65d2
--- /dev/null
+++ b/Documentation/w1/slaves/w1_ds2423
@@ -0,0 +1,47 @@
+Kernel driver w1_ds2423
+=======================
+
+Supported chips:
+ * Maxim DS2423 based counter devices.
+
+supported family codes:
+ W1_THERM_DS2423 0x1D
+
+Author: Mika Laitio <lamikr@pilppa.org>
+
+Description
+-----------
+
+Support is provided through the sysfs w1_slave file. Each opening and
+read sequence of w1_slave file initiates the read of counters and ram
+available in DS2423 pages 12 - 15.
+
+Result of each page is provided as an ASCII output where each counter
+value and associated ram buffer is outpputed to own line.
+
+Each lines will contain the values of 42 bytes read from the counter and
+memory page along the crc=YES or NO for indicating whether the read operation
+was successfull and CRC matched.
+If the operation was successfull, there is also in the end of each line
+a counter value expressed as an integer after c=
+
+Meaning of 42 bytes represented is following:
+ - 1 byte from ram page
+ - 4 bytes for the counter value
+ - 4 zero bytes
+ - 2 bytes for crc16 which was calculated from the data read since the previous crc bytes
+ - 31 remaining bytes from the ram page
+ - crc=YES/NO indicating whether read was ok and crc matched
+ - c=<int> current counter value
+
+example from the successfull read:
+00 02 00 00 00 00 00 00 00 6d 38 00 ff ff 00 00 fe ff 00 00 ff ff 00 00 ff ff 00 00 ff ff 00 00 ff ff 00 00 ff ff 00 00 ff ff crc=YES c=2
+00 02 00 00 00 00 00 00 00 e0 1f 00 ff ff 00 00 ff ff 00 00 ff ff 00 00 ff ff 00 00 ff ff 00 00 ff ff 00 00 ff ff 00 00 ff ff crc=YES c=2
+00 29 c6 5d 18 00 00 00 00 04 37 00 ff ff 00 00 ff ff 00 00 ff ff 00 00 ff ff 00 00 ff ff 00 00 ff ff 00 00 ff ff 00 00 ff ff crc=YES c=408798761
+00 05 00 00 00 00 00 00 00 8d 39 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff crc=YES c=5
+
+example from the read with crc errors:
+00 02 00 00 00 00 00 00 00 6d 38 00 ff ff 00 00 fe ff 00 00 ff ff 00 00 ff ff 00 00 ff ff 00 00 ff ff 00 00 ff ff 00 00 ff ff crc=YES c=2
+00 02 00 00 22 00 00 00 00 e0 1f 00 ff ff 00 00 ff ff 00 00 ff ff 00 00 ff ff 00 00 ff ff 00 00 ff ff 00 00 ff ff 00 00 ff ff crc=NO
+00 e1 61 5d 19 00 00 00 00 df 0b 00 ff ff 00 00 ff ff 00 00 ff ff 00 00 ff ff 00 00 ff ff 00 00 ff ff 00 00 ff ff 00 00 ff ff crc=NO
+00 05 00 00 20 00 00 00 00 8d 39 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff crc=NO
diff --git a/Documentation/x86/boot.txt b/Documentation/x86/boot.txt
index bdeb81c..9b7221a 100644
--- a/Documentation/x86/boot.txt
+++ b/Documentation/x86/boot.txt
@@ -622,9 +622,9 @@
The payload may be compressed. The format of both the compressed and
uncompressed data should be determined using the standard magic
numbers. The currently supported compression formats are gzip
- (magic numbers 1F 8B or 1F 9E), bzip2 (magic number 42 5A) and LZMA
- (magic number 5D 00). The uncompressed payload is currently always ELF
- (magic number 7F 45 4C 46).
+ (magic numbers 1F 8B or 1F 9E), bzip2 (magic number 42 5A), LZMA
+ (magic number 5D 00), and XZ (magic number FD 37). The uncompressed
+ payload is currently always ELF (magic number 7F 45 4C 46).
Field name: payload_length
Type: read
diff --git a/Documentation/xz.txt b/Documentation/xz.txt
new file mode 100644
index 0000000..2cf3e26
--- /dev/null
+++ b/Documentation/xz.txt
@@ -0,0 +1,121 @@
+
+XZ data compression in Linux
+============================
+
+Introduction
+
+ XZ is a general purpose data compression format with high compression
+ ratio and relatively fast decompression. The primary compression
+ algorithm (filter) is LZMA2. Additional filters can be used to improve
+ compression ratio even further. E.g. Branch/Call/Jump (BCJ) filters
+ improve compression ratio of executable data.
+
+ The XZ decompressor in Linux is called XZ Embedded. It supports
+ the LZMA2 filter and optionally also BCJ filters. CRC32 is supported
+ for integrity checking. The home page of XZ Embedded is at
+ <http://tukaani.org/xz/embedded.html>, where you can find the
+ latest version and also information about using the code outside
+ the Linux kernel.
+
+ For userspace, XZ Utils provide a zlib-like compression library
+ and a gzip-like command line tool. XZ Utils can be downloaded from
+ <http://tukaani.org/xz/>.
+
+XZ related components in the kernel
+
+ The xz_dec module provides XZ decompressor with single-call (buffer
+ to buffer) and multi-call (stateful) APIs. The usage of the xz_dec
+ module is documented in include/linux/xz.h.
+
+ The xz_dec_test module is for testing xz_dec. xz_dec_test is not
+ useful unless you are hacking the XZ decompressor. xz_dec_test
+ allocates a char device major dynamically to which one can write
+ .xz files from userspace. The decompressed output is thrown away.
+ Keep an eye on dmesg to see diagnostics printed by xz_dec_test.
+ See the xz_dec_test source code for the details.
+
+ For decompressing the kernel image, initramfs, and initrd, there
+ is a wrapper function in lib/decompress_unxz.c. Its API is the
+ same as in other decompress_*.c files, which is defined in
+ include/linux/decompress/generic.h.
+
+ scripts/xz_wrap.sh is a wrapper for the xz command line tool found
+ from XZ Utils. The wrapper sets compression options to values suitable
+ for compressing the kernel image.
+
+ For kernel makefiles, two commands are provided for use with
+ $(call if_needed). The kernel image should be compressed with
+ $(call if_needed,xzkern) which will use a BCJ filter and a big LZMA2
+ dictionary. It will also append a four-byte trailer containing the
+ uncompressed size of the file, which is needed by the boot code.
+ Other things should be compressed with $(call if_needed,xzmisc)
+ which will use no BCJ filter and 1 MiB LZMA2 dictionary.
+
+Notes on compression options
+
+ Since the XZ Embedded supports only streams with no integrity check or
+ CRC32, make sure that you don't use some other integrity check type
+ when encoding files that are supposed to be decoded by the kernel. With
+ liblzma, you need to use either LZMA_CHECK_NONE or LZMA_CHECK_CRC32
+ when encoding. With the xz command line tool, use --check=none or
+ --check=crc32.
+
+ Using CRC32 is strongly recommended unless there is some other layer
+ which will verify the integrity of the uncompressed data anyway.
+ Double checking the integrity would probably be waste of CPU cycles.
+ Note that the headers will always have a CRC32 which will be validated
+ by the decoder; you can only change the integrity check type (or
+ disable it) for the actual uncompressed data.
+
+ In userspace, LZMA2 is typically used with dictionary sizes of several
+ megabytes. The decoder needs to have the dictionary in RAM, thus big
+ dictionaries cannot be used for files that are intended to be decoded
+ by the kernel. 1 MiB is probably the maximum reasonable dictionary
+ size for in-kernel use (maybe more is OK for initramfs). The presets
+ in XZ Utils may not be optimal when creating files for the kernel,
+ so don't hesitate to use custom settings. Example:
+
+ xz --check=crc32 --lzma2=dict=512KiB inputfile
+
+ An exception to above dictionary size limitation is when the decoder
+ is used in single-call mode. Decompressing the kernel itself is an
+ example of this situation. In single-call mode, the memory usage
+ doesn't depend on the dictionary size, and it is perfectly fine to
+ use a big dictionary: for maximum compression, the dictionary should
+ be at least as big as the uncompressed data itself.
+
+Future plans
+
+ Creating a limited XZ encoder may be considered if people think it is
+ useful. LZMA2 is slower to compress than e.g. Deflate or LZO even at
+ the fastest settings, so it isn't clear if LZMA2 encoder is wanted
+ into the kernel.
+
+ Support for limited random-access reading is planned for the
+ decompression code. I don't know if it could have any use in the
+ kernel, but I know that it would be useful in some embedded projects
+ outside the Linux kernel.
+
+Conformance to the .xz file format specification
+
+ There are a couple of corner cases where things have been simplified
+ at expense of detecting errors as early as possible. These should not
+ matter in practice all, since they don't cause security issues. But
+ it is good to know this if testing the code e.g. with the test files
+ from XZ Utils.
+
+Reporting bugs
+
+ Before reporting a bug, please check that it's not fixed already
+ at upstream. See <http://tukaani.org/xz/embedded.html> to get the
+ latest code.
+
+ Report bugs to <lasse.collin@tukaani.org> or visit #tukaani on
+ Freenode and talk to Larhzu. I don't actively read LKML or other
+ kernel-related mailing lists, so if there's something I should know,
+ you should email to me personally or use IRC.
+
+ Don't bother Igor Pavlov with questions about the XZ implementation
+ in the kernel or about XZ Utils. While these two implementations
+ include essential code that is directly based on Igor Pavlov's code,
+ these implementations aren't maintained nor supported by him.
diff --git a/Documentation/zh_CN/HOWTO b/Documentation/zh_CN/HOWTO
index 6916077..faf976c 100644
--- a/Documentation/zh_CN/HOWTO
+++ b/Documentation/zh_CN/HOWTO
@@ -347,8 +347,8 @@
最新bug的通知,可以订阅bugme-new邮件列表(只有新的bug报告会被寄到这里)
或者订阅bugme-janitor邮件列表(所有bugzilla的变动都会被寄到这里)。
- http://lists.osdl.org/mailman/listinfo/bugme-new
- http://lists.osdl.org/mailman/listinfo/bugme-janitors
+ https://lists.linux-foundation.org/mailman/listinfo/bugme-new
+ https://lists.linux-foundation.org/mailman/listinfo/bugme-janitors
邮件列表
diff --git a/Documentation/zh_CN/SubmittingDrivers b/Documentation/zh_CN/SubmittingDrivers
index c27b0f6..5889f8d 100644
--- a/Documentation/zh_CN/SubmittingDrivers
+++ b/Documentation/zh_CN/SubmittingDrivers
@@ -61,7 +61,7 @@
Linux 2.6:
除了遵循和 2.4 版内核同样的规则外,你还需要在 linux-kernel 邮件
列表上跟踪最新的 API 变化。向 Linux 2.6 内核提交驱动的顶级联系人
- 是 Andrew Morton <akpm@osdl.org>。
+ 是 Andrew Morton <akpm@linux-foundation.org>。
决定设备驱动能否被接受的条件
----------------------------
diff --git a/MAINTAINERS b/MAINTAINERS
index 42f991e..78162c4 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -496,7 +496,6 @@
F: arch/x86/kernel/microcode_amd.c
AMS (Apple Motion Sensor) DRIVER
-M: Stelian Pop <stelian@popies.net>
M: Michael Hanselmann <linux-kernel@hansmi.ch>
S: Supported
F: drivers/macintosh/ams/
@@ -2008,7 +2007,7 @@
DCCP PROTOCOL
M: Arnaldo Carvalho de Melo <acme@ghostprotocols.net>
L: dccp@vger.kernel.org
-W: http://linux-net.osdl.org/index.php/DCCP
+W: http://www.linuxfoundation.org/collaborate/workgroups/networking/dccp
S: Maintained
F: include/linux/dccp.h
F: include/linux/tfrc.h
@@ -2430,7 +2429,7 @@
M: Stephen Hemminger <shemminger@linux-foundation.org>
L: bridge@lists.linux-foundation.org
L: netdev@vger.kernel.org
-W: http://www.linux-foundation.org/en/Net:Bridge
+W: http://www.linuxfoundation.org/en/Net:Bridge
S: Maintained
F: include/linux/netfilter_bridge/
F: net/bridge/
@@ -4093,9 +4092,8 @@
F: kernel/module.c
MOTION EYE VAIO PICTUREBOOK CAMERA DRIVER
-M: Stelian Pop <stelian@popies.net>
W: http://popies.net/meye/
-S: Maintained
+S: Orphan
F: Documentation/video4linux/meye.txt
F: drivers/media/video/meye.*
F: include/linux/meye.h
@@ -4383,11 +4381,11 @@
F: drivers/scsi/nsp32*
NTFS FILESYSTEM
-M: Anton Altaparmakov <aia21@cantab.net>
+M: Anton Altaparmakov <anton@tuxera.com>
L: linux-ntfs-dev@lists.sourceforge.net
-W: http://www.linux-ntfs.org/
+W: http://www.tuxera.com/
T: git git://git.kernel.org/pub/scm/linux/kernel/git/aia21/ntfs-2.6.git
-S: Maintained
+S: Supported
F: Documentation/filesystems/ntfs.txt
F: fs/ntfs/
@@ -4635,7 +4633,7 @@
M: Chris Wright <chrisw@sous-sol.org>
M: Alok Kataria <akataria@vmware.com>
M: Rusty Russell <rusty@rustcorp.com.au>
-L: virtualization@lists.osdl.org
+L: virtualization@lists.linux-foundation.org
S: Supported
F: Documentation/ia64/paravirt_ops.txt
F: arch/*/kernel/paravirt*
@@ -5133,11 +5131,6 @@
F: kernel/srcu*
X: kernel/rcutorture.c
-REAL TIME CLOCK DRIVER (LEGACY)
-M: Paul Gortmaker <p_gortmaker@yahoo.com>
-S: Maintained
-F: drivers/char/rtc.c
-
REAL TIME CLOCK (RTC) SUBSYSTEM
M: Alessandro Zummo <a.zummo@towertech.it>
L: rtc-linux@googlegroups.com
@@ -6537,7 +6530,7 @@
VIRTIO HOST (VHOST)
M: "Michael S. Tsirkin" <mst@redhat.com>
L: kvm@vger.kernel.org
-L: virtualization@lists.osdl.org
+L: virtualization@lists.linux-foundation.org
L: netdev@vger.kernel.org
S: Maintained
F: drivers/vhost/
@@ -6587,7 +6580,7 @@
VLYNQ BUS
M: Florian Fainelli <florian@openwrt.org>
-L: openwrt-devel@lists.openwrt.org
+L: openwrt-devel@lists.openwrt.org (subscribers-only)
S: Maintained
F: drivers/vlynq/vlynq.c
F: include/linux/vlynq.h
@@ -6807,7 +6800,7 @@
M: Jeremy Fitzhardinge <jeremy.fitzhardinge@citrix.com>
M: Konrad Rzeszutek Wilk <konrad.wilk@oracle.com>
L: xen-devel@lists.xensource.com (moderated for non-subscribers)
-L: virtualization@lists.osdl.org
+L: virtualization@lists.linux-foundation.org
S: Supported
F: arch/x86/xen/
F: drivers/*/xen-*front.c
diff --git a/arch/alpha/kernel/time.c b/arch/alpha/kernel/time.c
index 0f1d849..c1f3e7c 100644
--- a/arch/alpha/kernel/time.c
+++ b/arch/alpha/kernel/time.c
@@ -506,7 +506,7 @@
CMOS_WRITE(real_seconds,RTC_SECONDS);
CMOS_WRITE(real_minutes,RTC_MINUTES);
} else {
- printk(KERN_WARNING
+ printk_once(KERN_NOTICE
"set_rtc_mmss: can't update from %d to %d\n",
cmos_minutes, real_minutes);
retval = -1;
diff --git a/arch/arm/common/it8152.c b/arch/arm/common/it8152.c
index 42ff90b..665ebf7 100644
--- a/arch/arm/common/it8152.c
+++ b/arch/arm/common/it8152.c
@@ -236,7 +236,7 @@
/*
* The following functions are needed for DMA bouncing.
- * ITE8152 chip can addrees up to 64MByte, so all the devices
+ * ITE8152 chip can address up to 64MByte, so all the devices
* connected to ITE8152 (PCI and USB) should have limited DMA window
*/
diff --git a/arch/arm/common/vic.c b/arch/arm/common/vic.c
index ba65f6e..cb660bc 100644
--- a/arch/arm/common/vic.c
+++ b/arch/arm/common/vic.c
@@ -70,7 +70,7 @@
* vic_init2 - common initialisation code
* @base: Base of the VIC.
*
- * Common initialisation code for registeration
+ * Common initialisation code for registration
* and resume.
*/
static void vic_init2(void __iomem *base)
diff --git a/arch/arm/mach-at91/board-ecbat91.c b/arch/arm/mach-at91/board-ecbat91.c
index 7b58c94..de2fd04 100644
--- a/arch/arm/mach-at91/board-ecbat91.c
+++ b/arch/arm/mach-at91/board-ecbat91.c
@@ -128,17 +128,17 @@
.platform_data = &my_flash0_platform,
#endif
},
- { /* User accessable spi - cs1 (250KHz) */
+ { /* User accessible spi - cs1 (250KHz) */
.modalias = "spi-cs1",
.chip_select = 1,
.max_speed_hz = 250 * 1000,
},
- { /* User accessable spi - cs2 (1MHz) */
+ { /* User accessible spi - cs2 (1MHz) */
.modalias = "spi-cs2",
.chip_select = 2,
.max_speed_hz = 1 * 1000 * 1000,
},
- { /* User accessable spi - cs3 (10MHz) */
+ { /* User accessible spi - cs3 (10MHz) */
.modalias = "spi-cs3",
.chip_select = 3,
.max_speed_hz = 10 * 1000 * 1000,
diff --git a/arch/arm/mach-at91/pm.c b/arch/arm/mach-at91/pm.c
index dafbacc..ea53f4d 100644
--- a/arch/arm/mach-at91/pm.c
+++ b/arch/arm/mach-at91/pm.c
@@ -301,7 +301,7 @@
}
-static struct platform_suspend_ops at91_pm_ops ={
+static const struct platform_suspend_ops at91_pm_ops = {
.valid = at91_pm_valid_state,
.begin = at91_pm_begin,
.enter = at91_pm_enter,
diff --git a/arch/arm/mach-bcmring/csp/chipc/chipcHw.c b/arch/arm/mach-bcmring/csp/chipc/chipcHw.c
index b3a61d8..96273ff 100644
--- a/arch/arm/mach-bcmring/csp/chipc/chipcHw.c
+++ b/arch/arm/mach-bcmring/csp/chipc/chipcHw.c
@@ -757,7 +757,7 @@
t = t << 1;
}
- /* Intialize the result */
+ /* Initialize the result */
r = 0;
do {
diff --git a/arch/arm/mach-bcmring/csp/dmac/dmacHw.c b/arch/arm/mach-bcmring/csp/dmac/dmacHw.c
index 7b9bac2..6b9be2e 100644
--- a/arch/arm/mach-bcmring/csp/dmac/dmacHw.c
+++ b/arch/arm/mach-bcmring/csp/dmac/dmacHw.c
@@ -893,7 +893,7 @@
*/
/****************************************************************************/
uint32_t dmacHw_getDmaControllerAttribute(dmacHw_HANDLE_t handle, /* [ IN ] DMA Channel handle */
- dmacHw_CONTROLLER_ATTRIB_e attr /* [ IN ] DMA Controler attribute of type dmacHw_CONTROLLER_ATTRIB_e */
+ dmacHw_CONTROLLER_ATTRIB_e attr /* [ IN ] DMA Controller attribute of type dmacHw_CONTROLLER_ATTRIB_e */
) {
dmacHw_CBLK_t *pCblk = dmacHw_HANDLE_TO_CBLK(handle);
diff --git a/arch/arm/mach-bcmring/csp/dmac/dmacHw_extra.c b/arch/arm/mach-bcmring/csp/dmac/dmacHw_extra.c
index ff7b436..77f84b4 100644
--- a/arch/arm/mach-bcmring/csp/dmac/dmacHw_extra.c
+++ b/arch/arm/mach-bcmring/csp/dmac/dmacHw_extra.c
@@ -316,7 +316,7 @@
/**
* @brief Check if DMA channel is the flow controller
*
-* @return 1 : If DMA is a flow controler
+* @return 1 : If DMA is a flow controller
* 0 : Peripheral is the flow controller
*
* @note
diff --git a/arch/arm/mach-bcmring/csp/tmr/tmrHw.c b/arch/arm/mach-bcmring/csp/tmr/tmrHw.c
index 5c1c9a0..16225e4 100644
--- a/arch/arm/mach-bcmring/csp/tmr/tmrHw.c
+++ b/arch/arm/mach-bcmring/csp/tmr/tmrHw.c
@@ -558,7 +558,7 @@
t = t << 1;
}
- /* Intialize the result */
+ /* Initialize the result */
r = 0;
do {
diff --git a/arch/arm/mach-bcmring/dma.c b/arch/arm/mach-bcmring/dma.c
index 77eb35c..8d1baf3 100644
--- a/arch/arm/mach-bcmring/dma.c
+++ b/arch/arm/mach-bcmring/dma.c
@@ -671,7 +671,7 @@
/****************************************************************************/
/**
-* Intializes all of the data structures associated with the DMA.
+* Initializes all of the data structures associated with the DMA.
* @return
* >= 0 - Initialization was successfull.
*
diff --git a/arch/arm/mach-bcmring/include/csp/dmacHw.h b/arch/arm/mach-bcmring/include/csp/dmacHw.h
index 5d51013..6c8da2b 100644
--- a/arch/arm/mach-bcmring/include/csp/dmacHw.h
+++ b/arch/arm/mach-bcmring/include/csp/dmacHw.h
@@ -590,7 +590,7 @@
*/
/****************************************************************************/
uint32_t dmacHw_getDmaControllerAttribute(dmacHw_HANDLE_t handle, /* [ IN ] DMA Channel handle */
- dmacHw_CONTROLLER_ATTRIB_e attr /* [ IN ] DMA Controler attribute of type dmacHw_CONTROLLER_ATTRIB_e */
+ dmacHw_CONTROLLER_ATTRIB_e attr /* [ IN ] DMA Controller attribute of type dmacHw_CONTROLLER_ATTRIB_e */
);
#endif /* _DMACHW_H */
diff --git a/arch/arm/mach-bcmring/include/csp/tmrHw.h b/arch/arm/mach-bcmring/include/csp/tmrHw.h
index f1236d0..2cbb530 100644
--- a/arch/arm/mach-bcmring/include/csp/tmrHw.h
+++ b/arch/arm/mach-bcmring/include/csp/tmrHw.h
@@ -76,7 +76,7 @@
* certain time interval
*
* This function initializes a periodic timer to generate timer interrupt
-* after every time interval in milisecond
+* after every time interval in millisecond
*
* @return On success: Effective interval set in mili-second
* On failure: 0
@@ -93,7 +93,7 @@
* after certain time interval
*
* This function initializes a periodic timer to generate a single ticks after
-* certain time interval in milisecond
+* certain time interval in millisecond
*
* @return On success: Effective interval set in mili-second
* On failure: 0
diff --git a/arch/arm/mach-bcmring/include/mach/csp/dmacHw_priv.h b/arch/arm/mach-bcmring/include/mach/csp/dmacHw_priv.h
index cbf334d..d67e2f8 100644
--- a/arch/arm/mach-bcmring/include/mach/csp/dmacHw_priv.h
+++ b/arch/arm/mach-bcmring/include/mach/csp/dmacHw_priv.h
@@ -28,7 +28,7 @@
/* Data type for DMA Link List Item */
typedef struct {
- uint32_t sar; /* Source Adress Register.
+ uint32_t sar; /* Source Address Register.
Address must be aligned to CTLx.SRC_TR_WIDTH. */
uint32_t dar; /* Destination Address Register.
Address must be aligned to CTLx.DST_TR_WIDTH. */
diff --git a/arch/arm/mach-bcmring/include/mach/csp/dmacHw_reg.h b/arch/arm/mach-bcmring/include/mach/csp/dmacHw_reg.h
index 891cea8..f1ecf96 100644
--- a/arch/arm/mach-bcmring/include/mach/csp/dmacHw_reg.h
+++ b/arch/arm/mach-bcmring/include/mach/csp/dmacHw_reg.h
@@ -35,7 +35,7 @@
/* Data type representing DMA channel registers */
typedef struct {
- dmacHw_REG64_t ChannelSar; /* Source Adress Register. 64 bits (upper 32 bits are reserved)
+ dmacHw_REG64_t ChannelSar; /* Source Address Register. 64 bits (upper 32 bits are reserved)
Address must be aligned to CTLx.SRC_TR_WIDTH.
*/
dmacHw_REG64_t ChannelDar; /* Destination Address Register.64 bits (upper 32 bits are reserved)
diff --git a/arch/arm/mach-davinci/pm.c b/arch/arm/mach-davinci/pm.c
index fab953b..1bd73a04 100644
--- a/arch/arm/mach-davinci/pm.c
+++ b/arch/arm/mach-davinci/pm.c
@@ -110,7 +110,7 @@
return ret;
}
-static struct platform_suspend_ops davinci_pm_ops = {
+static const struct platform_suspend_ops davinci_pm_ops = {
.enter = davinci_pm_enter,
.valid = suspend_valid_only_mem,
};
diff --git a/arch/arm/mach-gemini/include/mach/hardware.h b/arch/arm/mach-gemini/include/mach/hardware.h
index 213a4fc..8c950e1 100644
--- a/arch/arm/mach-gemini/include/mach/hardware.h
+++ b/arch/arm/mach-gemini/include/mach/hardware.h
@@ -33,7 +33,7 @@
#define GEMINI_LPC_HOST_BASE 0x47000000
#define GEMINI_LPC_IO_BASE 0x47800000
#define GEMINI_INTERRUPT_BASE 0x48000000
-/* TODO: Different interrupt controlers when SMP
+/* TODO: Different interrupt controllers when SMP
* #define GEMINI_INTERRUPT0_BASE 0x48000000
* #define GEMINI_INTERRUPT1_BASE 0x49000000
*/
diff --git a/arch/arm/mach-imx/mach-pcm038.c b/arch/arm/mach-imx/mach-pcm038.c
index f667a26..5056148 100644
--- a/arch/arm/mach-imx/mach-pcm038.c
+++ b/arch/arm/mach-imx/mach-pcm038.c
@@ -254,10 +254,10 @@
static struct mc13783_regulator_init_data pcm038_regulators[] = {
{
- .id = MC13783_REGU_VCAM,
+ .id = MC13783_REG_VCAM,
.init_data = &cam_data,
}, {
- .id = MC13783_REGU_VMMC1,
+ .id = MC13783_REG_VMMC1,
.init_data = &sdhc1_data,
},
};
diff --git a/arch/arm/mach-imx/pm-imx27.c b/arch/arm/mach-imx/pm-imx27.c
index 6bf81ce..acf1769 100644
--- a/arch/arm/mach-imx/pm-imx27.c
+++ b/arch/arm/mach-imx/pm-imx27.c
@@ -32,7 +32,7 @@
return 0;
}
-static struct platform_suspend_ops mx27_suspend_ops = {
+static const struct platform_suspend_ops mx27_suspend_ops = {
.enter = mx27_suspend_enter,
.valid = suspend_valid_only_mem,
};
diff --git a/arch/arm/mach-lpc32xx/pm.c b/arch/arm/mach-lpc32xx/pm.c
index a6e2aed..e76d41b 100644
--- a/arch/arm/mach-lpc32xx/pm.c
+++ b/arch/arm/mach-lpc32xx/pm.c
@@ -123,7 +123,7 @@
return 0;
}
-static struct platform_suspend_ops lpc32xx_pm_ops = {
+static const struct platform_suspend_ops lpc32xx_pm_ops = {
.valid = suspend_valid_only_mem,
.enter = lpc32xx_pm_enter,
};
diff --git a/arch/arm/mach-msm/include/mach/entry-macro-qgic.S b/arch/arm/mach-msm/include/mach/entry-macro-qgic.S
index 4dc99aa..1246715 100644
--- a/arch/arm/mach-msm/include/mach/entry-macro-qgic.S
+++ b/arch/arm/mach-msm/include/mach/entry-macro-qgic.S
@@ -26,7 +26,7 @@
* The interrupt numbering scheme is defined in the
* interrupt controller spec. To wit:
*
- * Migrated the code from ARM MP port to be more consistant
+ * Migrated the code from ARM MP port to be more consistent
* with interrupt processing , the following still holds true
* however, all interrupts are treated the same regardless of
* if they are local IPI or PPI
diff --git a/arch/arm/mach-msm/io.c b/arch/arm/mach-msm/io.c
index 800f327..1260007 100644
--- a/arch/arm/mach-msm/io.c
+++ b/arch/arm/mach-msm/io.c
@@ -154,7 +154,7 @@
{
if (mtype == MT_DEVICE) {
/* The peripherals in the 88000000 - D0000000 range
- * are only accessable by type MT_DEVICE_NONSHARED.
+ * are only accessible by type MT_DEVICE_NONSHARED.
* Adjust mtype as necessary to make this "just work."
*/
if ((phys_addr >= 0x88000000) && (phys_addr < 0xD0000000))
diff --git a/arch/arm/mach-mx3/mach-mx31_3ds.c b/arch/arm/mach-mx3/mach-mx31_3ds.c
index 4e516b4..899a969 100644
--- a/arch/arm/mach-mx3/mach-mx31_3ds.c
+++ b/arch/arm/mach-mx3/mach-mx31_3ds.c
@@ -140,10 +140,10 @@
static struct mc13783_regulator_init_data mx31_3ds_regulators[] = {
{
- .id = MC13783_REGU_PWGT1SPI, /* Power Gate for ARM core. */
+ .id = MC13783_REG_PWGT1SPI, /* Power Gate for ARM core. */
.init_data = &pwgtx_init,
}, {
- .id = MC13783_REGU_PWGT2SPI, /* Power Gate for L2 Cache. */
+ .id = MC13783_REG_PWGT2SPI, /* Power Gate for L2 Cache. */
.init_data = &pwgtx_init,
}, {
diff --git a/arch/arm/mach-mx3/mach-mx31moboard.c b/arch/arm/mach-mx3/mach-mx31moboard.c
index 203d21a..1aa8d65 100644
--- a/arch/arm/mach-mx3/mach-mx31moboard.c
+++ b/arch/arm/mach-mx3/mach-mx31moboard.c
@@ -216,11 +216,11 @@
static struct mc13783_regulator_init_data moboard_regulators[] = {
{
- .id = MC13783_REGU_VMMC1,
+ .id = MC13783_REG_VMMC1,
.init_data = &sdhc_vreg_data,
},
{
- .id = MC13783_REGU_VCAM,
+ .id = MC13783_REG_VCAM,
.init_data = &cam_vreg_data,
},
};
diff --git a/arch/arm/mach-omap1/pm.c b/arch/arm/mach-omap1/pm.c
index 0cca23a..98ba978 100644
--- a/arch/arm/mach-omap1/pm.c
+++ b/arch/arm/mach-omap1/pm.c
@@ -647,7 +647,7 @@
-static struct platform_suspend_ops omap_pm_ops ={
+static const struct platform_suspend_ops omap_pm_ops = {
.prepare = omap_pm_prepare,
.enter = omap_pm_enter,
.finish = omap_pm_finish,
diff --git a/arch/arm/mach-omap2/cpuidle34xx.c b/arch/arm/mach-omap2/cpuidle34xx.c
index f3e043f..11b89e96 100644
--- a/arch/arm/mach-omap2/cpuidle34xx.c
+++ b/arch/arm/mach-omap2/cpuidle34xx.c
@@ -252,7 +252,7 @@
* FIXME: we currently manage device-specific idle states
* for PER and CORE in combination with CPU-specific
* idle states. This is wrong, and device-specific
- * idle managment needs to be separated out into
+ * idle management needs to be separated out into
* its own code.
*/
diff --git a/arch/arm/mach-omap2/pm24xx.c b/arch/arm/mach-omap2/pm24xx.c
index dac2d1d..9e5dc8e 100644
--- a/arch/arm/mach-omap2/pm24xx.c
+++ b/arch/arm/mach-omap2/pm24xx.c
@@ -350,7 +350,7 @@
enable_hlt();
}
-static struct platform_suspend_ops omap_pm_ops = {
+static const struct platform_suspend_ops omap_pm_ops = {
.begin = omap2_pm_begin,
.enter = omap2_pm_enter,
.end = omap2_pm_end,
diff --git a/arch/arm/mach-omap2/pm34xx.c b/arch/arm/mach-omap2/pm34xx.c
index 5b323f2..8cbbead 100644
--- a/arch/arm/mach-omap2/pm34xx.c
+++ b/arch/arm/mach-omap2/pm34xx.c
@@ -605,7 +605,7 @@
return;
}
-static struct platform_suspend_ops omap_pm_ops = {
+static const struct platform_suspend_ops omap_pm_ops = {
.begin = omap3_pm_begin,
.end = omap3_pm_end,
.enter = omap3_pm_enter,
diff --git a/arch/arm/mach-omap2/pm44xx.c b/arch/arm/mach-omap2/pm44xx.c
index e9f4862c..76cfff2 100644
--- a/arch/arm/mach-omap2/pm44xx.c
+++ b/arch/arm/mach-omap2/pm44xx.c
@@ -65,7 +65,7 @@
return;
}
-static struct platform_suspend_ops omap_pm_ops = {
+static const struct platform_suspend_ops omap_pm_ops = {
.begin = omap4_pm_begin,
.end = omap4_pm_end,
.enter = omap4_pm_enter,
diff --git a/arch/arm/mach-omap2/serial.c b/arch/arm/mach-omap2/serial.c
index c645788..302da74 100644
--- a/arch/arm/mach-omap2/serial.c
+++ b/arch/arm/mach-omap2/serial.c
@@ -852,7 +852,7 @@
}
/**
- * omap_serial_init() - intialize all supported serial ports
+ * omap_serial_init() - initialize all supported serial ports
*
* Initializes all available UARTs as serial ports. Platforms
* can call this function when they want to have default behaviour
diff --git a/arch/arm/mach-pnx4008/pm.c b/arch/arm/mach-pnx4008/pm.c
index ee3c29c..f3e60a0 100644
--- a/arch/arm/mach-pnx4008/pm.c
+++ b/arch/arm/mach-pnx4008/pm.c
@@ -119,7 +119,7 @@
(state == PM_SUSPEND_MEM);
}
-static struct platform_suspend_ops pnx4008_pm_ops = {
+static const struct platform_suspend_ops pnx4008_pm_ops = {
.enter = pnx4008_pm_enter,
.valid = pnx4008_pm_valid,
};
diff --git a/arch/arm/mach-pxa/mxm8x10.c b/arch/arm/mach-pxa/mxm8x10.c
index 462167a..cdf7f41 100644
--- a/arch/arm/mach-pxa/mxm8x10.c
+++ b/arch/arm/mach-pxa/mxm8x10.c
@@ -337,7 +337,7 @@
}
#endif
-/* USB Open Host Controler Interface */
+/* USB Open Host Controller Interface */
static struct pxaohci_platform_data mxm_8x10_ohci_platform_data = {
.port_mode = PMM_NPS_MODE,
.flags = ENABLE_PORT_ALL
diff --git a/arch/arm/mach-pxa/pm.c b/arch/arm/mach-pxa/pm.c
index 166c15f..978e1b2 100644
--- a/arch/arm/mach-pxa/pm.c
+++ b/arch/arm/mach-pxa/pm.c
@@ -96,7 +96,7 @@
pxa_cpu_pm_fns->finish();
}
-static struct platform_suspend_ops pxa_pm_ops = {
+static const struct platform_suspend_ops pxa_pm_ops = {
.valid = pxa_pm_valid,
.enter = pxa_pm_enter,
.prepare = pxa_pm_prepare,
diff --git a/arch/arm/mach-pxa/sharpsl_pm.c b/arch/arm/mach-pxa/sharpsl_pm.c
index e68d46d..785880f 100644
--- a/arch/arm/mach-pxa/sharpsl_pm.c
+++ b/arch/arm/mach-pxa/sharpsl_pm.c
@@ -869,7 +869,7 @@
}
#ifdef CONFIG_PM
-static struct platform_suspend_ops sharpsl_pm_ops = {
+static const struct platform_suspend_ops sharpsl_pm_ops = {
.prepare = pxa_pm_prepare,
.finish = pxa_pm_finish,
.enter = corgi_pxa_pm_enter,
diff --git a/arch/arm/mach-s3c64xx/dma.c b/arch/arm/mach-s3c64xx/dma.c
index e7d03ab..372ea68 100644
--- a/arch/arm/mach-s3c64xx/dma.c
+++ b/arch/arm/mach-s3c64xx/dma.c
@@ -740,7 +740,7 @@
/* Set all DMA configuration to be DMA, not SDMA */
writel(0xffffff, S3C_SYSREG(0x110));
- /* Register standard DMA controlers */
+ /* Register standard DMA controllers */
s3c64xx_dma_init1(0, DMACH_UART0, IRQ_DMA0, 0x75000000);
s3c64xx_dma_init1(8, DMACH_PCM1_TX, IRQ_DMA1, 0x75100000);
diff --git a/arch/arm/mach-sa1100/pm.c b/arch/arm/mach-sa1100/pm.c
index c83fdc8..ab9fc44 100644
--- a/arch/arm/mach-sa1100/pm.c
+++ b/arch/arm/mach-sa1100/pm.c
@@ -120,7 +120,7 @@
return virt_to_phys(sp);
}
-static struct platform_suspend_ops sa11x0_pm_ops = {
+static const struct platform_suspend_ops sa11x0_pm_ops = {
.enter = sa11x0_pm_enter,
.valid = suspend_valid_only_mem,
};
diff --git a/arch/arm/mach-spear3xx/spear300.c b/arch/arm/mach-spear3xx/spear300.c
index 3560f8c..5aa2d54 100644
--- a/arch/arm/mach-spear3xx/spear300.c
+++ b/arch/arm/mach-spear3xx/spear300.c
@@ -371,7 +371,7 @@
};
/* Add spear300 specific devices here */
-/* arm gpio1 device registeration */
+/* arm gpio1 device registration */
static struct pl061_platform_data gpio1_plat_data = {
.gpio_base = 8,
.irq_base = SPEAR_GPIO1_INT_BASE,
@@ -451,7 +451,7 @@
/* call spear3xx family common init function */
spear3xx_init();
- /* shared irq registeration */
+ /* shared irq registration */
shirq_ras1.regs.base =
ioremap(SPEAR300_TELECOM_BASE, SPEAR300_TELECOM_REG_SIZE);
if (shirq_ras1.regs.base) {
diff --git a/arch/arm/mach-spear3xx/spear310.c b/arch/arm/mach-spear3xx/spear310.c
index 96a1ab8..53b41b5 100644
--- a/arch/arm/mach-spear3xx/spear310.c
+++ b/arch/arm/mach-spear3xx/spear310.c
@@ -266,7 +266,7 @@
/* call spear3xx family common init function */
spear3xx_init();
- /* shared irq registeration */
+ /* shared irq registration */
base = ioremap(SPEAR310_SOC_CONFIG_BASE, SPEAR310_SOC_CONFIG_SIZE);
if (base) {
/* shirq 1 */
diff --git a/arch/arm/mach-spear3xx/spear320.c b/arch/arm/mach-spear3xx/spear320.c
index 6a121954..88b4652 100644
--- a/arch/arm/mach-spear3xx/spear320.c
+++ b/arch/arm/mach-spear3xx/spear320.c
@@ -519,7 +519,7 @@
/* call spear3xx family common init function */
spear3xx_init();
- /* shared irq registeration */
+ /* shared irq registration */
base = ioremap(SPEAR320_SOC_CONFIG_BASE, SPEAR320_SOC_CONFIG_SIZE);
if (base) {
/* shirq 1 */
diff --git a/arch/arm/mach-spear3xx/spear3xx.c b/arch/arm/mach-spear3xx/spear3xx.c
index e87313a..52f553c 100644
--- a/arch/arm/mach-spear3xx/spear3xx.c
+++ b/arch/arm/mach-spear3xx/spear3xx.c
@@ -22,7 +22,7 @@
#include <mach/spear.h>
/* Add spear3xx machines common devices here */
-/* gpio device registeration */
+/* gpio device registration */
static struct pl061_platform_data gpio_plat_data = {
.gpio_base = 0,
.irq_base = SPEAR_GPIO_INT_BASE,
@@ -41,7 +41,7 @@
.irq = {IRQ_BASIC_GPIO, NO_IRQ},
};
-/* uart device registeration */
+/* uart device registration */
struct amba_device uart_device = {
.dev = {
.init_name = "uart",
@@ -543,6 +543,6 @@
pmx_fail:
if (ret)
- printk(KERN_ERR "padmux: registeration failed. err no: %d\n",
+ printk(KERN_ERR "padmux: registration failed. err no: %d\n",
ret);
}
diff --git a/arch/arm/mach-spear6xx/spear6xx.c b/arch/arm/mach-spear6xx/spear6xx.c
index baf6bcc..f2fe14e 100644
--- a/arch/arm/mach-spear6xx/spear6xx.c
+++ b/arch/arm/mach-spear6xx/spear6xx.c
@@ -23,7 +23,7 @@
#include <mach/spear.h>
/* Add spear6xx machines common devices here */
-/* uart device registeration */
+/* uart device registration */
struct amba_device uart_device[] = {
{
.dev = {
@@ -50,7 +50,7 @@
}
};
-/* gpio device registeration */
+/* gpio device registration */
static struct pl061_platform_data gpio_plat_data[] = {
{
.gpio_base = 0,
diff --git a/arch/arm/mach-u300/Kconfig b/arch/arm/mach-u300/Kconfig
index 801b21e..32a7b0f 100644
--- a/arch/arm/mach-u300/Kconfig
+++ b/arch/arm/mach-u300/Kconfig
@@ -64,7 +64,7 @@
bool "Dual RAM"
help
Select this if you want support for Dual RAM phones.
- This is two RAM memorys on different EMIFs.
+ This is two RAM memories on different EMIFs.
endchoice
config U300_DEBUG
diff --git a/arch/arm/mach-u300/include/mach/coh901318.h b/arch/arm/mach-u300/include/mach/coh901318.h
index 193da2d..6193aaa 100644
--- a/arch/arm/mach-u300/include/mach/coh901318.h
+++ b/arch/arm/mach-u300/include/mach/coh901318.h
@@ -24,7 +24,7 @@
* @src_addr: transfer source address
* @dst_addr: transfer destination address
* @link_addr: physical address to next lli
- * @virt_link_addr: virtual addres of next lli (only used by pool_free)
+ * @virt_link_addr: virtual address of next lli (only used by pool_free)
* @phy_this: physical address of current lli (only used by pool_free)
*/
struct coh901318_lli {
@@ -90,7 +90,7 @@
* struct coh901318_platform - platform arch structure
* @chans_slave: specifying dma slave channels
* @chans_memcpy: specifying dma memcpy channels
- * @access_memory_state: requesting DMA memeory access (on / off)
+ * @access_memory_state: requesting DMA memory access (on / off)
* @chan_conf: dma channel configurations
* @max_channels: max number of dma chanenls
*/
diff --git a/arch/arm/mach-ux500/board-mop500-regulators.c b/arch/arm/mach-ux500/board-mop500-regulators.c
index 1187f1f..533967c 100644
--- a/arch/arm/mach-ux500/board-mop500-regulators.c
+++ b/arch/arm/mach-ux500/board-mop500-regulators.c
@@ -3,99 +3,94 @@
*
* License Terms: GNU General Public License v2
*
- * Author: Sundar Iyer <sundar.iyer@stericsson.com>
+ * Authors: Sundar Iyer <sundar.iyer@stericsson.com>
+ * Bengt Jonsson <bengt.g.jonsson@stericsson.com>
*
* MOP500 board specific initialization for regulators
*/
#include <linux/kernel.h>
#include <linux/regulator/machine.h>
+#include <linux/regulator/ab8500.h>
-/* supplies to the display/camera */
-static struct regulator_init_data ab8500_vaux1_regulator = {
- .constraints = {
- .name = "V-DISPLAY",
- .min_uV = 2500000,
- .max_uV = 2900000,
- .valid_ops_mask = REGULATOR_CHANGE_VOLTAGE|
- REGULATOR_CHANGE_STATUS,
+/* AB8500 regulators */
+struct regulator_init_data ab8500_regulators[AB8500_NUM_REGULATORS] = {
+ /* supplies to the display/camera */
+ [AB8500_LDO_AUX1] = {
+ .constraints = {
+ .name = "V-DISPLAY",
+ .min_uV = 2500000,
+ .max_uV = 2900000,
+ .valid_ops_mask = REGULATOR_CHANGE_VOLTAGE |
+ REGULATOR_CHANGE_STATUS,
+ },
+ },
+ /* supplies to the on-board eMMC */
+ [AB8500_LDO_AUX2] = {
+ .constraints = {
+ .name = "V-eMMC1",
+ .min_uV = 1100000,
+ .max_uV = 3300000,
+ .valid_ops_mask = REGULATOR_CHANGE_VOLTAGE |
+ REGULATOR_CHANGE_STATUS,
+ },
+ },
+ /* supply for VAUX3, supplies to SDcard slots */
+ [AB8500_LDO_AUX3] = {
+ .constraints = {
+ .name = "V-MMC-SD",
+ .min_uV = 1100000,
+ .max_uV = 3300000,
+ .valid_ops_mask = REGULATOR_CHANGE_VOLTAGE |
+ REGULATOR_CHANGE_STATUS,
+ },
+ },
+ /* supply for tvout, gpadc, TVOUT LDO */
+ [AB8500_LDO_TVOUT] = {
+ .constraints = {
+ .name = "V-TVOUT",
+ .valid_ops_mask = REGULATOR_CHANGE_STATUS,
+ },
+ },
+ /* supply for ab8500-vaudio, VAUDIO LDO */
+ [AB8500_LDO_AUDIO] = {
+ .constraints = {
+ .name = "V-AUD",
+ .valid_ops_mask = REGULATOR_CHANGE_STATUS,
+ },
+ },
+ /* supply for v-anamic1 VAMic1-LDO */
+ [AB8500_LDO_ANAMIC1] = {
+ .constraints = {
+ .name = "V-AMIC1",
+ .valid_ops_mask = REGULATOR_CHANGE_STATUS,
+ },
+ },
+ /* supply for v-amic2, VAMIC2 LDO, reuse constants for AMIC1 */
+ [AB8500_LDO_ANAMIC2] = {
+ .constraints = {
+ .name = "V-AMIC2",
+ .valid_ops_mask = REGULATOR_CHANGE_STATUS,
+ },
+ },
+ /* supply for v-dmic, VDMIC LDO */
+ [AB8500_LDO_DMIC] = {
+ .constraints = {
+ .name = "V-DMIC",
+ .valid_ops_mask = REGULATOR_CHANGE_STATUS,
+ },
+ },
+ /* supply for v-intcore12, VINTCORE12 LDO */
+ [AB8500_LDO_INTCORE] = {
+ .constraints = {
+ .name = "V-INTCORE",
+ .valid_ops_mask = REGULATOR_CHANGE_STATUS,
+ },
+ },
+ /* supply for U8500 CSI/DSI, VANA LDO */
+ [AB8500_LDO_ANA] = {
+ .constraints = {
+ .name = "V-CSI/DSI",
+ .valid_ops_mask = REGULATOR_CHANGE_STATUS,
+ },
},
};
-
-/* supplies to the on-board eMMC */
-static struct regulator_init_data ab8500_vaux2_regulator = {
- .constraints = {
- .name = "V-eMMC1",
- .min_uV = 1100000,
- .max_uV = 3300000,
- .valid_ops_mask = REGULATOR_CHANGE_VOLTAGE|
- REGULATOR_CHANGE_STATUS,
- },
-};
-
-/* supply for VAUX3, supplies to SDcard slots */
-static struct regulator_init_data ab8500_vaux3_regulator = {
- .constraints = {
- .name = "V-MMC-SD",
- .min_uV = 1100000,
- .max_uV = 3300000,
- .valid_ops_mask = REGULATOR_CHANGE_VOLTAGE|
- REGULATOR_CHANGE_STATUS,
- },
-};
-
-/* supply for tvout, gpadc, TVOUT LDO */
-static struct regulator_init_data ab8500_vtvout_init = {
- .constraints = {
- .name = "V-TVOUT",
- .valid_ops_mask = REGULATOR_CHANGE_STATUS,
- },
-};
-
-/* supply for ab8500-vaudio, VAUDIO LDO */
-static struct regulator_init_data ab8500_vaudio_init = {
- .constraints = {
- .name = "V-AUD",
- .valid_ops_mask = REGULATOR_CHANGE_STATUS,
- },
-};
-
-/* supply for v-anamic1 VAMic1-LDO */
-static struct regulator_init_data ab8500_vamic1_init = {
- .constraints = {
- .name = "V-AMIC1",
- .valid_ops_mask = REGULATOR_CHANGE_STATUS,
- },
-};
-
-/* supply for v-amic2, VAMIC2 LDO, reuse constants for AMIC1 */
-static struct regulator_init_data ab8500_vamic2_init = {
- .constraints = {
- .name = "V-AMIC2",
- .valid_ops_mask = REGULATOR_CHANGE_STATUS,
- },
-};
-
-/* supply for v-dmic, VDMIC LDO */
-static struct regulator_init_data ab8500_vdmic_init = {
- .constraints = {
- .name = "V-DMIC",
- .valid_ops_mask = REGULATOR_CHANGE_STATUS,
- },
-};
-
-/* supply for v-intcore12, VINTCORE12 LDO */
-static struct regulator_init_data ab8500_vintcore_init = {
- .constraints = {
- .name = "V-INTCORE",
- .valid_ops_mask = REGULATOR_CHANGE_STATUS,
- },
-};
-
-/* supply for U8500 CSI/DSI, VANA LDO */
-static struct regulator_init_data ab8500_vana_init = {
- .constraints = {
- .name = "V-CSI/DSI",
- .valid_ops_mask = REGULATOR_CHANGE_STATUS,
- },
-};
-
diff --git a/arch/arm/mach-ux500/board-mop500-regulators.h b/arch/arm/mach-ux500/board-mop500-regulators.h
new file mode 100644
index 0000000..2675fae
--- /dev/null
+++ b/arch/arm/mach-ux500/board-mop500-regulators.h
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2010
+ *
+ * License Terms: GNU General Public License v2
+ *
+ * Author: Bengt Jonsson <bengt.g.jonsson@stericsson.com> for ST-Ericsson
+ *
+ * MOP500 board specific initialization for regulators
+ */
+
+#ifndef __BOARD_MOP500_REGULATORS_H
+#define __BOARD_MOP500_REGULATORS_H
+
+#include <linux/regulator/machine.h>
+#include <linux/regulator/ab8500.h>
+
+extern struct regulator_init_data ab8500_regulators[AB8500_NUM_REGULATORS];
+
+#endif
diff --git a/arch/arm/mach-ux500/board-mop500.c b/arch/arm/mach-ux500/board-mop500.c
index a1c9ea1..a393f57 100644
--- a/arch/arm/mach-ux500/board-mop500.c
+++ b/arch/arm/mach-ux500/board-mop500.c
@@ -35,6 +35,7 @@
#include "devices-db8500.h"
#include "pins-db8500.h"
#include "board-mop500.h"
+#include "board-mop500-regulators.h"
static pin_cfg_t mop500_pins[] = {
/* SSP0 */
@@ -80,6 +81,8 @@
static struct ab8500_platform_data ab8500_platdata = {
.irq_base = MOP500_AB8500_IRQ_BASE,
+ .regulator = ab8500_regulators,
+ .num_regulator = ARRAY_SIZE(ab8500_regulators),
};
static struct resource ab8500_resources[] = {
diff --git a/arch/arm/mm/flush.c b/arch/arm/mm/flush.c
index c29f283..2b269c9 100644
--- a/arch/arm/mm/flush.c
+++ b/arch/arm/mm/flush.c
@@ -18,7 +18,6 @@
#include <asm/smp_plat.h>
#include <asm/system.h>
#include <asm/tlbflush.h>
-#include <asm/smp_plat.h>
#include "mm.h"
diff --git a/arch/arm/plat-mxc/include/mach/irqs.h b/arch/arm/plat-mxc/include/mach/irqs.h
index 58a49cc..ba65c92 100644
--- a/arch/arm/plat-mxc/include/mach/irqs.h
+++ b/arch/arm/plat-mxc/include/mach/irqs.h
@@ -70,7 +70,7 @@
/* all normal IRQs can be FIQs */
#define FIQ_START 0
-/* switch betwean IRQ and FIQ */
+/* switch between IRQ and FIQ */
extern int mxc_set_irq_fiq(unsigned int irq, unsigned int type);
#endif /* __ASM_ARCH_MXC_IRQS_H__ */
diff --git a/arch/arm/plat-omap/include/plat/omap_hwmod.h b/arch/arm/plat-omap/include/plat/omap_hwmod.h
index 6864a99..1eee85a 100644
--- a/arch/arm/plat-omap/include/plat/omap_hwmod.h
+++ b/arch/arm/plat-omap/include/plat/omap_hwmod.h
@@ -351,7 +351,7 @@
/**
* struct omap_hwmod_omap4_prcm - OMAP4-specific PRCM data
* @clkctrl_reg: PRCM address of the clock control register
- * @rstctrl_reg: adress of the XXX_RSTCTRL register located in the PRM
+ * @rstctrl_reg: address of the XXX_RSTCTRL register located in the PRM
* @submodule_wkdep_bit: bit shift of the WKDEP range
*/
struct omap_hwmod_omap4_prcm {
diff --git a/arch/arm/plat-samsung/pm.c b/arch/arm/plat-samsung/pm.c
index 27cfca5..5bf3f2f 100644
--- a/arch/arm/plat-samsung/pm.c
+++ b/arch/arm/plat-samsung/pm.c
@@ -355,7 +355,7 @@
s3c_pm_check_cleanup();
}
-static struct platform_suspend_ops s3c_pm_ops = {
+static const struct platform_suspend_ops s3c_pm_ops = {
.enter = s3c_pm_enter,
.prepare = s3c_pm_prepare,
.finish = s3c_pm_finish,
diff --git a/arch/avr32/mach-at32ap/pm.c b/arch/avr32/mach-at32ap/pm.c
index f021edf..32d680e 100644
--- a/arch/avr32/mach-at32ap/pm.c
+++ b/arch/avr32/mach-at32ap/pm.c
@@ -176,7 +176,7 @@
return 0;
}
-static struct platform_suspend_ops avr32_pm_ops = {
+static const struct platform_suspend_ops avr32_pm_ops = {
.valid = avr32_pm_valid_state,
.enter = avr32_pm_enter,
};
diff --git a/arch/blackfin/mach-bf537/include/mach/defBF534.h b/arch/blackfin/mach-bf537/include/mach/defBF534.h
index 32529a0..725bb35 100644
--- a/arch/blackfin/mach-bf537/include/mach/defBF534.h
+++ b/arch/blackfin/mach-bf537/include/mach/defBF534.h
@@ -1418,7 +1418,7 @@
#define SADD_LEN 0x0002 /* Slave Address Length */
#define STDVAL 0x0004 /* Slave Transmit Data Valid */
#define NAK 0x0008 /* NAK/ACK* Generated At Conclusion Of Transfer */
-#define GEN 0x0010 /* General Call Adrress Matching Enabled */
+#define GEN 0x0010 /* General Call Address Matching Enabled */
/* TWI_SLAVE_STAT Masks */
#define SDIR 0x0001 /* Slave Transfer Direction (Transmit/Receive*) */
diff --git a/arch/blackfin/mach-common/pm.c b/arch/blackfin/mach-common/pm.c
index 42fa87e..3c648a0 100644
--- a/arch/blackfin/mach-common/pm.c
+++ b/arch/blackfin/mach-common/pm.c
@@ -223,7 +223,7 @@
return 0;
}
-struct platform_suspend_ops bfin_pm_ops = {
+static const struct platform_suspend_ops bfin_pm_ops = {
.enter = bfin_pm_enter,
.valid = bfin_pm_valid,
};
diff --git a/arch/cris/arch-v32/lib/nand_init.S b/arch/cris/arch-v32/lib/nand_init.S
index e705f5c..d671fed 100644
--- a/arch/cris/arch-v32/lib/nand_init.S
+++ b/arch/cris/arch-v32/lib/nand_init.S
@@ -139,7 +139,7 @@
lsrq 8, $r4
move.b $r4, [$r1] ; Row address
lsrq 8, $r4
- move.b $r4, [$r1] ; Row adddress
+ move.b $r4, [$r1] ; Row address
moveq 20, $r4
2: bne 2b
subq 1, $r4
diff --git a/arch/cris/include/asm/etraxgpio.h b/arch/cris/include/asm/etraxgpio.h
index d474818..461c089 100644
--- a/arch/cris/include/asm/etraxgpio.h
+++ b/arch/cris/include/asm/etraxgpio.h
@@ -1,5 +1,5 @@
/*
- * The following devices are accessable using this driver using
+ * The following devices are accessible using this driver using
* GPIO_MAJOR (120) and a couple of minor numbers.
*
* For ETRAX 100LX (CONFIG_ETRAX_ARCH_V10):
diff --git a/arch/cris/kernel/time.c b/arch/cris/kernel/time.c
index b509643..4e73092 100644
--- a/arch/cris/kernel/time.c
+++ b/arch/cris/kernel/time.c
@@ -86,7 +86,7 @@
CMOS_WRITE(real_seconds,RTC_SECONDS);
CMOS_WRITE(real_minutes,RTC_MINUTES);
} else {
- printk(KERN_WARNING
+ printk_once(KERN_NOTICE
"set_rtc_mmss: can't update from %d to %d\n",
cmos_minutes, real_minutes);
retval = -1;
diff --git a/arch/h8300/Kconfig.debug b/arch/h8300/Kconfig.debug
index ee671c3..e8d1b23 100644
--- a/arch/h8300/Kconfig.debug
+++ b/arch/h8300/Kconfig.debug
@@ -48,7 +48,7 @@
builtin kernel commandline enabled.
config KERNEL_COMMAND
- string "Buildin commmand string"
+ string "Buildin command string"
depends on DEFAULT_CMDLINE
help
builtin kernel commandline strings.
diff --git a/arch/ia64/include/asm/kvm_host.h b/arch/ia64/include/asm/kvm_host.h
index 2f229e5..2689ee5 100644
--- a/arch/ia64/include/asm/kvm_host.h
+++ b/arch/ia64/include/asm/kvm_host.h
@@ -590,6 +590,10 @@
int kvm_pal_emul(struct kvm_vcpu *vcpu, struct kvm_run *kvm_run);
void kvm_sal_emul(struct kvm_vcpu *vcpu);
+#define __KVM_HAVE_ARCH_VM_ALLOC 1
+struct kvm *kvm_arch_alloc_vm(void);
+void kvm_arch_free_vm(struct kvm *kvm);
+
#endif /* __ASSEMBLY__*/
#endif
diff --git a/arch/ia64/kvm/kvm-ia64.c b/arch/ia64/kvm/kvm-ia64.c
index f56a631..70d224d 100644
--- a/arch/ia64/kvm/kvm-ia64.c
+++ b/arch/ia64/kvm/kvm-ia64.c
@@ -749,7 +749,7 @@
return r;
}
-static struct kvm *kvm_alloc_kvm(void)
+struct kvm *kvm_arch_alloc_vm(void)
{
struct kvm *kvm;
@@ -760,7 +760,7 @@
vm_base = __get_free_pages(GFP_KERNEL, get_order(KVM_VM_DATA_SIZE));
if (!vm_base)
- return ERR_PTR(-ENOMEM);
+ return NULL;
memset((void *)vm_base, 0, KVM_VM_DATA_SIZE);
kvm = (struct kvm *)(vm_base +
@@ -806,10 +806,12 @@
#define GUEST_PHYSICAL_RR4 0x2739
#define VMM_INIT_RR 0x1660
-static void kvm_init_vm(struct kvm *kvm)
+int kvm_arch_init_vm(struct kvm *kvm)
{
BUG_ON(!kvm);
+ kvm->arch.is_sn2 = ia64_platform_is("sn2");
+
kvm->arch.metaphysical_rr0 = GUEST_PHYSICAL_RR0;
kvm->arch.metaphysical_rr4 = GUEST_PHYSICAL_RR4;
kvm->arch.vmm_init_rr = VMM_INIT_RR;
@@ -823,21 +825,8 @@
/* Reserve bit 0 of irq_sources_bitmap for userspace irq source */
set_bit(KVM_USERSPACE_IRQ_SOURCE_ID, &kvm->arch.irq_sources_bitmap);
-}
-struct kvm *kvm_arch_create_vm(void)
-{
- struct kvm *kvm = kvm_alloc_kvm();
-
- if (IS_ERR(kvm))
- return ERR_PTR(-ENOMEM);
-
- kvm->arch.is_sn2 = ia64_platform_is("sn2");
-
- kvm_init_vm(kvm);
-
- return kvm;
-
+ return 0;
}
static int kvm_vm_ioctl_get_irqchip(struct kvm *kvm,
@@ -962,7 +951,9 @@
goto out;
r = kvm_setup_default_irq_routing(kvm);
if (r) {
+ mutex_lock(&kvm->slots_lock);
kvm_ioapic_destroy(kvm);
+ mutex_unlock(&kvm->slots_lock);
goto out;
}
break;
@@ -1357,7 +1348,7 @@
return -EINVAL;
}
-static void free_kvm(struct kvm *kvm)
+void kvm_arch_free_vm(struct kvm *kvm)
{
unsigned long vm_base = kvm->arch.vm_base;
@@ -1399,9 +1390,6 @@
#endif
kfree(kvm->arch.vioapic);
kvm_release_vm_pages(kvm);
- kvm_free_physmem(kvm);
- cleanup_srcu_struct(&kvm->srcu);
- free_kvm(kvm);
}
void kvm_arch_vcpu_put(struct kvm_vcpu *vcpu)
diff --git a/arch/ia64/kvm/mmio.c b/arch/ia64/kvm/mmio.c
index fb8f9f5..f1e17d3 100644
--- a/arch/ia64/kvm/mmio.c
+++ b/arch/ia64/kvm/mmio.c
@@ -130,7 +130,7 @@
local_irq_save(psr);
- /*Intercept the acces for PIB range*/
+ /*Intercept the access for PIB range*/
if (iot == GPFN_PIB) {
if (!dir)
lsapic_write(vcpu, src_pa, s, *dest);
diff --git a/arch/m68k/ifpsp060/src/fpsp.S b/arch/m68k/ifpsp060/src/fpsp.S
index 73613b5..26e85e2 100644
--- a/arch/m68k/ifpsp060/src/fpsp.S
+++ b/arch/m68k/ifpsp060/src/fpsp.S
@@ -3881,7 +3881,7 @@
# FP Unimplemented Instruction stack frame and jump to that entry
# point.
#
-# but, if the FPU is disabled, then we need to jump to the FPU diabled
+# but, if the FPU is disabled, then we need to jump to the FPU disabled
# entry point.
movc %pcr,%d0
btst &0x1,%d0
diff --git a/arch/m68k/include/asm/m548xgpt.h b/arch/m68k/include/asm/m548xgpt.h
index c8ef158..33b2eef 100644
--- a/arch/m68k/include/asm/m548xgpt.h
+++ b/arch/m68k/include/asm/m548xgpt.h
@@ -59,11 +59,13 @@
#define MCF_GPT_GMS_GPIO_INPUT (0x00000000)
#define MCF_GPT_GMS_GPIO_OUTLO (0x00000020)
#define MCF_GPT_GMS_GPIO_OUTHI (0x00000030)
+#define MCF_GPT_GMS_GPIO_MASK (0x00000030)
#define MCF_GPT_GMS_TMS_DISABLE (0x00000000)
#define MCF_GPT_GMS_TMS_INCAPT (0x00000001)
#define MCF_GPT_GMS_TMS_OUTCAPT (0x00000002)
#define MCF_GPT_GMS_TMS_PWM (0x00000003)
#define MCF_GPT_GMS_TMS_GPIO (0x00000004)
+#define MCF_GPT_GMS_TMS_MASK (0x00000007)
/* Bit definitions and macros for MCF_GPT_GCIR */
#define MCF_GPT_GCIR_CNT(x) (((x)&0x0000FFFF)<<0)
diff --git a/arch/m68k/mac/psc.c b/arch/m68k/mac/psc.c
index ba6ccab..a4c3eb6 100644
--- a/arch/m68k/mac/psc.c
+++ b/arch/m68k/mac/psc.c
@@ -88,7 +88,7 @@
/*
* The PSC is always at the same spot, but using psc
- * keeps things consisant with the psc_xxxx functions.
+ * keeps things consistent with the psc_xxxx functions.
*/
psc = (void *) PSC_BASE;
diff --git a/arch/mips/alchemy/common/power.c b/arch/mips/alchemy/common/power.c
index e5916a5..647e518 100644
--- a/arch/mips/alchemy/common/power.c
+++ b/arch/mips/alchemy/common/power.c
@@ -130,7 +130,7 @@
au_writel(sleep_usb[1], USBD_ENABLE);
au_sync();
#else
- /* enable accces to OTG memory */
+ /* enable access to OTG memory */
au_writel(au_readl(USB_MSR_BASE + 4) | (1 << 6), USB_MSR_BASE + 4);
au_sync();
diff --git a/arch/mips/alchemy/devboards/pm.c b/arch/mips/alchemy/devboards/pm.c
index 4bbd313..acaf91b 100644
--- a/arch/mips/alchemy/devboards/pm.c
+++ b/arch/mips/alchemy/devboards/pm.c
@@ -110,7 +110,7 @@
}
-static struct platform_suspend_ops db1x_pm_ops = {
+static const struct platform_suspend_ops db1x_pm_ops = {
.valid = suspend_valid_only_mem,
.begin = db1x_pm_begin,
.enter = db1x_pm_enter,
diff --git a/arch/mips/dec/time.c b/arch/mips/dec/time.c
index 02f505f..ea57f39 100644
--- a/arch/mips/dec/time.c
+++ b/arch/mips/dec/time.c
@@ -104,7 +104,7 @@
CMOS_WRITE(real_seconds, RTC_SECONDS);
CMOS_WRITE(real_minutes, RTC_MINUTES);
} else {
- printk(KERN_WARNING
+ printk_once(KERN_NOTICE
"set_rtc_mmss: can't update from %d to %d\n",
cmos_minutes, real_minutes);
retval = -1;
diff --git a/arch/mips/include/asm/mach-powertv/ioremap.h b/arch/mips/include/asm/mach-powertv/ioremap.h
index 076f2ee..c86ef09 100644
--- a/arch/mips/include/asm/mach-powertv/ioremap.h
+++ b/arch/mips/include/asm/mach-powertv/ioremap.h
@@ -88,7 +88,7 @@
}
/* These are not portable and should not be used in drivers. Drivers should
- * be using ioremap() and friends to map physical addreses to virtual
+ * be using ioremap() and friends to map physical addresses to virtual
* addresses and dma_map*() and friends to map virtual addresses into DMA
* addresses and back.
*/
diff --git a/arch/mips/include/asm/mc146818-time.h b/arch/mips/include/asm/mc146818-time.h
index 199b457..4a08dbe 100644
--- a/arch/mips/include/asm/mc146818-time.h
+++ b/arch/mips/include/asm/mc146818-time.h
@@ -66,7 +66,7 @@
CMOS_WRITE(real_seconds, RTC_SECONDS);
CMOS_WRITE(real_minutes, RTC_MINUTES);
} else {
- printk(KERN_WARNING
+ printk_once(KERN_NOTICE
"set_rtc_mmss: can't update from %d to %d\n",
cmos_minutes, real_minutes);
retval = -1;
diff --git a/arch/mips/jz4740/board-qi_lb60.c b/arch/mips/jz4740/board-qi_lb60.c
index 5c0a357..2c0e107 100644
--- a/arch/mips/jz4740/board-qi_lb60.c
+++ b/arch/mips/jz4740/board-qi_lb60.c
@@ -65,7 +65,7 @@
/* Early prototypes of the QI LB60 had only 1GB of NAND.
* In order to support these devices aswell the partition and ecc layout is
- * initalized depending on the NAND size */
+ * initialized depending on the NAND size */
static struct mtd_partition qi_lb60_partitions_1gb[] = {
{
.name = "NAND BOOT partition",
@@ -464,7 +464,7 @@
board_gpio_setup();
if (qi_lb60_init_platform_devices())
- panic("Failed to initalize platform devices\n");
+ panic("Failed to initialize platform devices\n");
return 0;
}
diff --git a/arch/mips/jz4740/gpio.c b/arch/mips/jz4740/gpio.c
index 38f60f3..88e6aed 100644
--- a/arch/mips/jz4740/gpio.c
+++ b/arch/mips/jz4740/gpio.c
@@ -546,7 +546,7 @@
for (i = 0; i < ARRAY_SIZE(jz4740_gpio_chips); ++i)
jz4740_gpio_chip_init(&jz4740_gpio_chips[i], i);
- printk(KERN_INFO "JZ4740 GPIO initalized\n");
+ printk(KERN_INFO "JZ4740 GPIO initialized\n");
return 0;
}
diff --git a/arch/mips/jz4740/pm.c b/arch/mips/jz4740/pm.c
index a999458..902d5b5 100644
--- a/arch/mips/jz4740/pm.c
+++ b/arch/mips/jz4740/pm.c
@@ -42,7 +42,7 @@
return 0;
}
-static struct platform_suspend_ops jz4740_pm_ops = {
+static const struct platform_suspend_ops jz4740_pm_ops = {
.valid = suspend_valid_only_mem,
.enter = jz4740_pm_enter,
};
diff --git a/arch/mips/loongson/common/pm.c b/arch/mips/loongson/common/pm.c
index 6c1fd90..f55e07a 100644
--- a/arch/mips/loongson/common/pm.c
+++ b/arch/mips/loongson/common/pm.c
@@ -147,7 +147,7 @@
}
}
-static struct platform_suspend_ops loongson_pm_ops = {
+static const struct platform_suspend_ops loongson_pm_ops = {
.valid = loongson_pm_valid_state,
.enter = loongson_pm_enter,
};
diff --git a/arch/mips/mti-malta/malta-memory.c b/arch/mips/mti-malta/malta-memory.c
index b27419c..a96d281 100644
--- a/arch/mips/mti-malta/malta-memory.c
+++ b/arch/mips/mti-malta/malta-memory.c
@@ -43,7 +43,7 @@
static char *mtypes[3] = {
"Dont use memory",
"YAMON PROM memory",
- "Free memmory",
+ "Free memory",
};
#endif
diff --git a/arch/mips/pci/pcie-octeon.c b/arch/mips/pci/pcie-octeon.c
index 385f035..0583c463 100644
--- a/arch/mips/pci/pcie-octeon.c
+++ b/arch/mips/pci/pcie-octeon.c
@@ -900,7 +900,7 @@
mem_access_subid.s.ror = 0;
/* Disable Relaxed Ordering for Writes. */
mem_access_subid.s.row = 0;
- /* PCIe Adddress Bits <63:34>. */
+ /* PCIe Address Bits <63:34>. */
mem_access_subid.s.ba = 0;
/*
diff --git a/arch/mips/powertv/memory.c b/arch/mips/powertv/memory.c
index 73880ad..fb3d296 100644
--- a/arch/mips/powertv/memory.c
+++ b/arch/mips/powertv/memory.c
@@ -57,7 +57,7 @@
unsigned long ptv_memsize;
/*
- * struct low_mem_reserved - Items in low memmory that are reserved
+ * struct low_mem_reserved - Items in low memory that are reserved
* @start: Physical address of item
* @size: Size, in bytes, of this item
* @is_aliased: True if this is RAM aliased from another location. If false,
diff --git a/arch/mips/txx9/generic/pci.c b/arch/mips/txx9/generic/pci.c
index 9a0be81..96e69a0 100644
--- a/arch/mips/txx9/generic/pci.c
+++ b/arch/mips/txx9/generic/pci.c
@@ -107,7 +107,7 @@
/*
* allocate pci_controller and resources.
- * mem_base, io_base: physical addresss. 0 for auto assignment.
+ * mem_base, io_base: physical address. 0 for auto assignment.
* mem_size and io_size means max size on auto assignment.
* pcic must be &txx9_primary_pcic or NULL.
*/
diff --git a/arch/mn10300/Kconfig b/arch/mn10300/Kconfig
index 41ba385..8ed41cf 100644
--- a/arch/mn10300/Kconfig
+++ b/arch/mn10300/Kconfig
@@ -203,6 +203,7 @@
config SMP
bool "Symmetric multi-processing support"
default y
+ select USE_GENERIC_SMP_HELPERS
depends on MN10300_PROC_MN2WS0038 || MN10300_PROC_MN2WS0050
---help---
This enables support for systems with more than one CPU. If you have
@@ -226,11 +227,6 @@
depends on SMP
default "2"
-config USE_GENERIC_SMP_HELPERS
- bool
- depends on SMP
- default y
-
source "kernel/Kconfig.preempt"
config MN10300_CURRENT_IN_E2
diff --git a/arch/mn10300/kernel/rtc.c b/arch/mn10300/kernel/rtc.c
index e9e20f9..48d7058 100644
--- a/arch/mn10300/kernel/rtc.c
+++ b/arch/mn10300/kernel/rtc.c
@@ -89,7 +89,7 @@
CMOS_WRITE(real_seconds, RTC_SECONDS);
CMOS_WRITE(real_minutes, RTC_MINUTES);
} else {
- printk(KERN_WARNING
+ printk_once(KERN_NOTICE
"set_rtc_mmss: can't update from %d to %d\n",
cmos_minutes, real_minutes);
retval = -1;
diff --git a/arch/powerpc/include/asm/8xx_immap.h b/arch/powerpc/include/asm/8xx_immap.h
index 4b0e152..6b6dc20 100644
--- a/arch/powerpc/include/asm/8xx_immap.h
+++ b/arch/powerpc/include/asm/8xx_immap.h
@@ -93,7 +93,7 @@
} memctl8xx_t;
/*-----------------------------------------------------------------------
- * BR - Memory Controler: Base Register 16-9
+ * BR - Memory Controller: Base Register 16-9
*/
#define BR_BA_MSK 0xffff8000 /* Base Address Mask */
#define BR_AT_MSK 0x00007000 /* Address Type Mask */
@@ -110,7 +110,7 @@
#define BR_V 0x00000001 /* Bank Valid */
/*-----------------------------------------------------------------------
- * OR - Memory Controler: Option Register 16-11
+ * OR - Memory Controller: Option Register 16-11
*/
#define OR_AM_MSK 0xffff8000 /* Address Mask Mask */
#define OR_ATM_MSK 0x00007000 /* Address Type Mask Mask */
diff --git a/arch/powerpc/kvm/book3s.c b/arch/powerpc/kvm/book3s.c
index e316847..badc983 100644
--- a/arch/powerpc/kvm/book3s.c
+++ b/arch/powerpc/kvm/book3s.c
@@ -1307,12 +1307,10 @@
int err = -ENOMEM;
unsigned long p;
- vcpu_book3s = vmalloc(sizeof(struct kvmppc_vcpu_book3s));
+ vcpu_book3s = vzalloc(sizeof(struct kvmppc_vcpu_book3s));
if (!vcpu_book3s)
goto out;
- memset(vcpu_book3s, 0, sizeof(struct kvmppc_vcpu_book3s));
-
vcpu_book3s->shadow_vcpu = (struct kvmppc_book3s_shadow_vcpu *)
kzalloc(sizeof(*vcpu_book3s->shadow_vcpu), GFP_KERNEL);
if (!vcpu_book3s->shadow_vcpu)
diff --git a/arch/powerpc/kvm/powerpc.c b/arch/powerpc/kvm/powerpc.c
index 38f756f..9975846 100644
--- a/arch/powerpc/kvm/powerpc.c
+++ b/arch/powerpc/kvm/powerpc.c
@@ -145,18 +145,12 @@
*(int *)rtn = kvmppc_core_check_processor_compat();
}
-struct kvm *kvm_arch_create_vm(void)
+int kvm_arch_init_vm(struct kvm *kvm)
{
- struct kvm *kvm;
-
- kvm = kzalloc(sizeof(struct kvm), GFP_KERNEL);
- if (!kvm)
- return ERR_PTR(-ENOMEM);
-
- return kvm;
+ return 0;
}
-static void kvmppc_free_vcpus(struct kvm *kvm)
+void kvm_arch_destroy_vm(struct kvm *kvm)
{
unsigned int i;
struct kvm_vcpu *vcpu;
@@ -176,14 +170,6 @@
{
}
-void kvm_arch_destroy_vm(struct kvm *kvm)
-{
- kvmppc_free_vcpus(kvm);
- kvm_free_physmem(kvm);
- cleanup_srcu_struct(&kvm->srcu);
- kfree(kvm);
-}
-
int kvm_dev_ioctl_check_extension(long ext)
{
int r;
diff --git a/arch/powerpc/oprofile/op_model_cell.c b/arch/powerpc/oprofile/op_model_cell.c
index 7fd90d0..c4d2b71 100644
--- a/arch/powerpc/oprofile/op_model_cell.c
+++ b/arch/powerpc/oprofile/op_model_cell.c
@@ -1469,7 +1469,7 @@
* The pm_interval register is setup to write the SPU PC value into the
* trace buffer at the maximum rate possible. The trace buffer is configured
* to store the PCs, wrapping when it is full. The performance counter is
- * intialized to the max hardware count minus the number of events, N, between
+ * initialized to the max hardware count minus the number of events, N, between
* samples. Once the N events have occured, a HW counter overflow occurs
* causing the generation of a HW counter interrupt which also stops the
* writing of the SPU PC values to the trace buffer. Hence the last PC
diff --git a/arch/powerpc/platforms/52xx/lite5200_pm.c b/arch/powerpc/platforms/52xx/lite5200_pm.c
index 80234e5..eda0fc2 100644
--- a/arch/powerpc/platforms/52xx/lite5200_pm.c
+++ b/arch/powerpc/platforms/52xx/lite5200_pm.c
@@ -232,7 +232,7 @@
lite5200_pm_target_state = PM_SUSPEND_ON;
}
-static struct platform_suspend_ops lite5200_pm_ops = {
+static const struct platform_suspend_ops lite5200_pm_ops = {
.valid = lite5200_pm_valid,
.begin = lite5200_pm_begin,
.prepare = lite5200_pm_prepare,
diff --git a/arch/powerpc/platforms/52xx/mpc52xx_pm.c b/arch/powerpc/platforms/52xx/mpc52xx_pm.c
index 568cef6..8310e8b 100644
--- a/arch/powerpc/platforms/52xx/mpc52xx_pm.c
+++ b/arch/powerpc/platforms/52xx/mpc52xx_pm.c
@@ -186,7 +186,7 @@
iounmap(mbar);
}
-static struct platform_suspend_ops mpc52xx_pm_ops = {
+static const struct platform_suspend_ops mpc52xx_pm_ops = {
.valid = mpc52xx_pm_valid,
.prepare = mpc52xx_pm_prepare,
.enter = mpc52xx_pm_enter,
diff --git a/arch/powerpc/platforms/83xx/suspend-asm.S b/arch/powerpc/platforms/83xx/suspend-asm.S
index 1930543..3d1ecd2 100644
--- a/arch/powerpc/platforms/83xx/suspend-asm.S
+++ b/arch/powerpc/platforms/83xx/suspend-asm.S
@@ -231,7 +231,7 @@
ori r4, r4, 0x002a
mtspr SPRN_DBAT0L, r4
lis r8, TMP_VIRT_IMMR@h
- ori r4, r8, 0x001e /* 1 MByte accessable from Kernel Space only */
+ ori r4, r8, 0x001e /* 1 MByte accessible from Kernel Space only */
mtspr SPRN_DBAT0U, r4
isync
@@ -241,7 +241,7 @@
ori r4, r4, 0x002a
mtspr SPRN_DBAT1L, r4
lis r9, (TMP_VIRT_IMMR + 0x01000000)@h
- ori r4, r9, 0x001e /* 1 MByte accessable from Kernel Space only */
+ ori r4, r9, 0x001e /* 1 MByte accessible from Kernel Space only */
mtspr SPRN_DBAT1U, r4
isync
@@ -253,7 +253,7 @@
li r4, 0x0002
mtspr SPRN_DBAT2L, r4
lis r4, KERNELBASE@h
- ori r4, r4, 0x001e /* 1 MByte accessable from Kernel Space only */
+ ori r4, r4, 0x001e /* 1 MByte accessible from Kernel Space only */
mtspr SPRN_DBAT2U, r4
isync
diff --git a/arch/powerpc/platforms/83xx/suspend.c b/arch/powerpc/platforms/83xx/suspend.c
index 75ae77f..fd4f2f2 100644
--- a/arch/powerpc/platforms/83xx/suspend.c
+++ b/arch/powerpc/platforms/83xx/suspend.c
@@ -311,7 +311,7 @@
return ret;
}
-static struct platform_suspend_ops mpc83xx_suspend_ops = {
+static const struct platform_suspend_ops mpc83xx_suspend_ops = {
.valid = mpc83xx_suspend_valid,
.begin = mpc83xx_suspend_begin,
.enter = mpc83xx_suspend_enter,
diff --git a/arch/powerpc/platforms/85xx/mpc85xx_mds.c b/arch/powerpc/platforms/85xx/mpc85xx_mds.c
index aa34cac..747d1ee 100644
--- a/arch/powerpc/platforms/85xx/mpc85xx_mds.c
+++ b/arch/powerpc/platforms/85xx/mpc85xx_mds.c
@@ -309,7 +309,7 @@
/* P1021 has pins muxed for QE and other functions. To
* enable QE UEC mode, we need to set bit QE0 for UCC1
* in Eth mode, QE0 and QE3 for UCC5 in Eth mode, QE9
- * and QE12 for QE MII management singals in PMUXCR
+ * and QE12 for QE MII management signals in PMUXCR
* register.
*/
setbits32(pmuxcr, MPC85xx_PMUXCR_QE0 |
diff --git a/arch/powerpc/platforms/ps3/device-init.c b/arch/powerpc/platforms/ps3/device-init.c
index b341018..6c4b583 100644
--- a/arch/powerpc/platforms/ps3/device-init.c
+++ b/arch/powerpc/platforms/ps3/device-init.c
@@ -566,10 +566,10 @@
case PS3_DEV_TYPE_STOR_DISK:
result = ps3_setup_storage_dev(repo, PS3_MATCH_ID_STOR_DISK);
- /* Some devices are not accessable from the Other OS lpar. */
+ /* Some devices are not accessible from the Other OS lpar. */
if (result == -ENODEV) {
result = 0;
- pr_debug("%s:%u: not accessable\n", __func__,
+ pr_debug("%s:%u: not accessible\n", __func__,
__LINE__);
}
diff --git a/arch/powerpc/platforms/ps3/interrupt.c b/arch/powerpc/platforms/ps3/interrupt.c
index 59d9712..92290ff 100644
--- a/arch/powerpc/platforms/ps3/interrupt.c
+++ b/arch/powerpc/platforms/ps3/interrupt.c
@@ -44,7 +44,7 @@
* @lock:
* @ipi_debug_brk_mask:
*
- * The HV mantains per SMT thread mappings of HV outlet to HV plug on
+ * The HV maintains per SMT thread mappings of HV outlet to HV plug on
* behalf of the guest. These mappings are implemented as 256 bit guest
* supplied bitmaps indexed by plug number. The addresses of the bitmaps
* are registered with the HV through lv1_configure_irq_state_bitmap().
diff --git a/arch/powerpc/platforms/pseries/hvCall_inst.c b/arch/powerpc/platforms/pseries/hvCall_inst.c
index e19ff02..f106662 100644
--- a/arch/powerpc/platforms/pseries/hvCall_inst.c
+++ b/arch/powerpc/platforms/pseries/hvCall_inst.c
@@ -55,7 +55,7 @@
static int hc_show(struct seq_file *m, void *p)
{
unsigned long h_num = (unsigned long)p;
- struct hcall_stats *hs = (struct hcall_stats *)m->private;
+ struct hcall_stats *hs = m->private;
if (hs[h_num].num_calls) {
if (cpu_has_feature(CPU_FTR_PURR))
diff --git a/arch/powerpc/platforms/pseries/suspend.c b/arch/powerpc/platforms/pseries/suspend.c
index ed72098..a8ca289 100644
--- a/arch/powerpc/platforms/pseries/suspend.c
+++ b/arch/powerpc/platforms/pseries/suspend.c
@@ -153,7 +153,7 @@
.name = "power",
};
-static struct platform_suspend_ops pseries_suspend_ops = {
+static const struct platform_suspend_ops pseries_suspend_ops = {
.valid = suspend_valid_only_mem,
.begin = pseries_suspend_begin,
.prepare_late = pseries_prepare_late,
diff --git a/arch/powerpc/sysdev/fsl_pmc.c b/arch/powerpc/sysdev/fsl_pmc.c
index 44de855..e9381bf 100644
--- a/arch/powerpc/sysdev/fsl_pmc.c
+++ b/arch/powerpc/sysdev/fsl_pmc.c
@@ -53,7 +53,7 @@
return 1;
}
-static struct platform_suspend_ops pmc_suspend_ops = {
+static const struct platform_suspend_ops pmc_suspend_ops = {
.valid = pmc_suspend_valid,
.enter = pmc_suspend_enter,
};
diff --git a/arch/powerpc/sysdev/fsl_rio.c b/arch/powerpc/sysdev/fsl_rio.c
index 9725369..9f99bef 100644
--- a/arch/powerpc/sysdev/fsl_rio.c
+++ b/arch/powerpc/sysdev/fsl_rio.c
@@ -973,7 +973,6 @@
if (dsr & DOORBELL_DSR_QFI) {
pr_info("RIO: doorbell queue full\n");
out_be32(&priv->msg_regs->dsr, DOORBELL_DSR_QFI);
- goto out;
}
/* XXX Need to check/dispatch until queue empty */
diff --git a/arch/s390/kvm/kvm-s390.c b/arch/s390/kvm/kvm-s390.c
index 985d825..bade533 100644
--- a/arch/s390/kvm/kvm-s390.c
+++ b/arch/s390/kvm/kvm-s390.c
@@ -164,24 +164,18 @@
return r;
}
-struct kvm *kvm_arch_create_vm(void)
+int kvm_arch_init_vm(struct kvm *kvm)
{
- struct kvm *kvm;
int rc;
char debug_name[16];
rc = s390_enable_sie();
if (rc)
- goto out_nokvm;
-
- rc = -ENOMEM;
- kvm = kzalloc(sizeof(struct kvm), GFP_KERNEL);
- if (!kvm)
- goto out_nokvm;
+ goto out_err;
kvm->arch.sca = (struct sca_block *) get_zeroed_page(GFP_KERNEL);
if (!kvm->arch.sca)
- goto out_nosca;
+ goto out_err;
sprintf(debug_name, "kvm-%u", current->pid);
@@ -195,13 +189,11 @@
debug_register_view(kvm->arch.dbf, &debug_sprintf_view);
VM_EVENT(kvm, 3, "%s", "vm created");
- return kvm;
+ return 0;
out_nodbf:
free_page((unsigned long)(kvm->arch.sca));
-out_nosca:
- kfree(kvm);
-out_nokvm:
- return ERR_PTR(rc);
+out_err:
+ return rc;
}
void kvm_arch_vcpu_destroy(struct kvm_vcpu *vcpu)
@@ -240,11 +232,8 @@
void kvm_arch_destroy_vm(struct kvm *kvm)
{
kvm_free_vcpus(kvm);
- kvm_free_physmem(kvm);
free_page((unsigned long)(kvm->arch.sca));
debug_unregister(kvm->arch.dbf);
- cleanup_srcu_struct(&kvm->srcu);
- kfree(kvm);
}
/* Section: vcpu related */
diff --git a/arch/sh/boards/mach-hp6xx/pm.c b/arch/sh/boards/mach-hp6xx/pm.c
index 4499a37..adc9b4b 100644
--- a/arch/sh/boards/mach-hp6xx/pm.c
+++ b/arch/sh/boards/mach-hp6xx/pm.c
@@ -143,7 +143,7 @@
return 0;
}
-static struct platform_suspend_ops hp6x0_pm_ops = {
+static const struct platform_suspend_ops hp6x0_pm_ops = {
.enter = hp6x0_pm_enter,
.valid = suspend_valid_only_mem,
};
diff --git a/arch/sh/boards/mach-sh03/rtc.c b/arch/sh/boards/mach-sh03/rtc.c
index 1b20099..f83ac79 100644
--- a/arch/sh/boards/mach-sh03/rtc.c
+++ b/arch/sh/boards/mach-sh03/rtc.c
@@ -108,7 +108,7 @@
__raw_writeb(real_minutes % 10, RTC_MIN1);
__raw_writeb(real_minutes / 10, RTC_MIN10);
} else {
- printk(KERN_WARNING
+ printk_once(KERN_NOTICE
"set_rtc_mmss: can't update from %d to %d\n",
cmos_minutes, real_minutes);
retval = -1;
diff --git a/arch/sh/kernel/cpu/shmobile/pm.c b/arch/sh/kernel/cpu/shmobile/pm.c
index e559687..a6f95ae 100644
--- a/arch/sh/kernel/cpu/shmobile/pm.c
+++ b/arch/sh/kernel/cpu/shmobile/pm.c
@@ -141,7 +141,7 @@
return 0;
}
-static struct platform_suspend_ops sh_pm_ops = {
+static const struct platform_suspend_ops sh_pm_ops = {
.enter = sh_pm_enter,
.valid = suspend_valid_only_mem,
};
diff --git a/arch/sh/mm/cache-sh5.c b/arch/sh/mm/cache-sh5.c
index eb4cc4e..d1bffbc 100644
--- a/arch/sh/mm/cache-sh5.c
+++ b/arch/sh/mm/cache-sh5.c
@@ -568,7 +568,7 @@
}
/*
- * Flush the range [start,end] of kernel virtual adddress space from
+ * Flush the range [start,end] of kernel virtual address space from
* the I-cache. The corresponding range must be purged from the
* D-cache also because the SH-5 doesn't have cache snooping between
* the caches. The addresses will be visible through the superpage
diff --git a/arch/sparc/kernel/traps_64.c b/arch/sparc/kernel/traps_64.c
index 42ad2ba..1e97709 100644
--- a/arch/sparc/kernel/traps_64.c
+++ b/arch/sparc/kernel/traps_64.c
@@ -622,7 +622,7 @@
static const char CHAFSR_IERR_msg[] =
"Internal processor error";
static const char CHAFSR_ISAP_msg[] =
- "System request parity error on incoming addresss";
+ "System request parity error on incoming address";
static const char CHAFSR_UCU_msg[] =
"Uncorrectable E-cache ECC error for ifetch/data";
static const char CHAFSR_UCC_msg[] =
diff --git a/arch/um/Kconfig.um b/arch/um/Kconfig.um
index 50d6aa2..f8d1d0d 100644
--- a/arch/um/Kconfig.um
+++ b/arch/um/Kconfig.um
@@ -131,7 +131,7 @@
config HIGHMEM
bool "Highmem support (EXPERIMENTAL)"
- depends on !64BIT && EXPERIMENTAL
+ depends on !64BIT && BROKEN
default n
help
This was used to allow UML to run with big amounts of memory.
diff --git a/arch/um/drivers/line.c b/arch/um/drivers/line.c
index 1664cce..050e4dd 100644
--- a/arch/um/drivers/line.c
+++ b/arch/um/drivers/line.c
@@ -821,12 +821,12 @@
static void unregister_winch(struct tty_struct *tty)
{
- struct list_head *ele;
+ struct list_head *ele, *next;
struct winch *winch;
spin_lock(&winch_handler_lock);
- list_for_each(ele, &winch_handlers) {
+ list_for_each_safe(ele, next, &winch_handlers) {
winch = list_entry(ele, struct winch, list);
if (winch->tty == tty) {
free_winch(winch, 1);
diff --git a/arch/um/drivers/mmapper_kern.c b/arch/um/drivers/mmapper_kern.c
index 8501e7d..7e0619c 100644
--- a/arch/um/drivers/mmapper_kern.c
+++ b/arch/um/drivers/mmapper_kern.c
@@ -37,13 +37,7 @@
if (*ppos > mmapper_size)
return -EINVAL;
- if (count > mmapper_size - *ppos)
- count = mmapper_size - *ppos;
-
- if (copy_from_user(&v_buf[*ppos], buf, count))
- return -EFAULT;
-
- return count;
+ return simple_write_to_buffer(v_buf, mmapper_size, ppos, buf, count);
}
static long mmapper_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
@@ -137,3 +131,4 @@
MODULE_AUTHOR("Greg Lonnon <glonnon@ridgerun.com>");
MODULE_DESCRIPTION("DSPLinux simulator mmapper driver");
+MODULE_LICENSE("GPL");
diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig
index b6fccb0..36ed2e2 100644
--- a/arch/x86/Kconfig
+++ b/arch/x86/Kconfig
@@ -51,6 +51,7 @@
select HAVE_KERNEL_GZIP
select HAVE_KERNEL_BZIP2
select HAVE_KERNEL_LZMA
+ select HAVE_KERNEL_XZ
select HAVE_KERNEL_LZO
select HAVE_HW_BREAKPOINT
select HAVE_MIXED_BREAKPOINTS_REGS
@@ -65,6 +66,7 @@
select HAVE_SPARSE_IRQ
select GENERIC_IRQ_PROBE
select GENERIC_PENDING_IRQ if SMP
+ select USE_GENERIC_SMP_HELPERS if SMP
config INSTRUCTION_DECODER
def_bool (KPROBES || PERF_EVENTS)
@@ -203,10 +205,6 @@
def_bool y
depends on EXPERIMENTAL && DMAR && ACPI
-config USE_GENERIC_SMP_HELPERS
- def_bool y
- depends on SMP
-
config X86_32_SMP
def_bool y
depends on X86_32 && SMP
@@ -2078,11 +2076,17 @@
bool "Support for OLPC's Open Firmware"
depends on !X86_64 && !X86_PAE
default n
+ select OF
help
This option adds support for the implementation of Open Firmware
that is used on the OLPC XO-1 Children's Machine.
If unsure, say N here.
+config OLPC_OPENFIRMWARE_DT
+ bool
+ default y if OLPC_OPENFIRMWARE && PROC_DEVICETREE
+ select OF_PROMTREE
+
endif # X86_32
config AMD_NB
diff --git a/arch/x86/boot/compressed/Makefile b/arch/x86/boot/compressed/Makefile
index 0c22955..09664ef 100644
--- a/arch/x86/boot/compressed/Makefile
+++ b/arch/x86/boot/compressed/Makefile
@@ -4,7 +4,7 @@
# create a compressed vmlinux image from the original vmlinux
#
-targets := vmlinux.lds vmlinux vmlinux.bin vmlinux.bin.gz vmlinux.bin.bz2 vmlinux.bin.lzma vmlinux.bin.lzo head_$(BITS).o misc.o string.o cmdline.o early_serial_console.o piggy.o
+targets := vmlinux.lds vmlinux vmlinux.bin vmlinux.bin.gz vmlinux.bin.bz2 vmlinux.bin.lzma vmlinux.bin.xz vmlinux.bin.lzo head_$(BITS).o misc.o string.o cmdline.o early_serial_console.o piggy.o
KBUILD_CFLAGS := -m$(BITS) -D__KERNEL__ $(LINUX_INCLUDE) -O2
KBUILD_CFLAGS += -fno-strict-aliasing -fPIC
@@ -49,12 +49,15 @@
$(call if_changed,bzip2)
$(obj)/vmlinux.bin.lzma: $(vmlinux.bin.all-y) FORCE
$(call if_changed,lzma)
+$(obj)/vmlinux.bin.xz: $(vmlinux.bin.all-y) FORCE
+ $(call if_changed,xzkern)
$(obj)/vmlinux.bin.lzo: $(vmlinux.bin.all-y) FORCE
$(call if_changed,lzo)
suffix-$(CONFIG_KERNEL_GZIP) := gz
suffix-$(CONFIG_KERNEL_BZIP2) := bz2
suffix-$(CONFIG_KERNEL_LZMA) := lzma
+suffix-$(CONFIG_KERNEL_XZ) := xz
suffix-$(CONFIG_KERNEL_LZO) := lzo
quiet_cmd_mkpiggy = MKPIGGY $@
diff --git a/arch/x86/boot/compressed/misc.c b/arch/x86/boot/compressed/misc.c
index 325c052..3a19d04 100644
--- a/arch/x86/boot/compressed/misc.c
+++ b/arch/x86/boot/compressed/misc.c
@@ -139,6 +139,10 @@
#include "../../../../lib/decompress_unlzma.c"
#endif
+#ifdef CONFIG_KERNEL_XZ
+#include "../../../../lib/decompress_unxz.c"
+#endif
+
#ifdef CONFIG_KERNEL_LZO
#include "../../../../lib/decompress_unlzo.c"
#endif
diff --git a/arch/x86/boot/compressed/mkpiggy.c b/arch/x86/boot/compressed/mkpiggy.c
index 5c22812..646aa78 100644
--- a/arch/x86/boot/compressed/mkpiggy.c
+++ b/arch/x86/boot/compressed/mkpiggy.c
@@ -74,7 +74,7 @@
offs = (olen > ilen) ? olen - ilen : 0;
offs += olen >> 12; /* Add 8 bytes for each 32K block */
- offs += 32*1024 + 18; /* Add 32K + 18 bytes slack */
+ offs += 64*1024 + 128; /* Add 64K + 128 bytes slack */
offs = (offs+4095) & ~4095; /* Round to a 4K boundary */
printf(".section \".rodata..compressed\",\"a\",@progbits\n");
diff --git a/arch/x86/crypto/aesni-intel_asm.S b/arch/x86/crypto/aesni-intel_asm.S
index ff16756..8fe2a49 100644
--- a/arch/x86/crypto/aesni-intel_asm.S
+++ b/arch/x86/crypto/aesni-intel_asm.S
@@ -9,6 +9,20 @@
* Vinodh Gopal <vinodh.gopal@intel.com>
* Kahraman Akdemir
*
+ * Added RFC4106 AES-GCM support for 128-bit keys under the AEAD
+ * interface for 64-bit kernels.
+ * Authors: Erdinc Ozturk (erdinc.ozturk@intel.com)
+ * Aidan O'Mahony (aidan.o.mahony@intel.com)
+ * Adrian Hoban <adrian.hoban@intel.com>
+ * James Guilford (james.guilford@intel.com)
+ * Gabriele Paoloni <gabriele.paoloni@intel.com>
+ * Tadeusz Struk (tadeusz.struk@intel.com)
+ * Wajdi Feghali (wajdi.k.feghali@intel.com)
+ * Copyright (c) 2010, Intel Corporation.
+ *
+ * Ported x86_64 version to x86:
+ * Author: Mathias Krause <minipli@googlemail.com>
+ *
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
@@ -18,8 +32,62 @@
#include <linux/linkage.h>
#include <asm/inst.h>
+#ifdef __x86_64__
+.data
+POLY: .octa 0xC2000000000000000000000000000001
+TWOONE: .octa 0x00000001000000000000000000000001
+
+# order of these constants should not change.
+# more specifically, ALL_F should follow SHIFT_MASK,
+# and ZERO should follow ALL_F
+
+SHUF_MASK: .octa 0x000102030405060708090A0B0C0D0E0F
+MASK1: .octa 0x0000000000000000ffffffffffffffff
+MASK2: .octa 0xffffffffffffffff0000000000000000
+SHIFT_MASK: .octa 0x0f0e0d0c0b0a09080706050403020100
+ALL_F: .octa 0xffffffffffffffffffffffffffffffff
+ZERO: .octa 0x00000000000000000000000000000000
+ONE: .octa 0x00000000000000000000000000000001
+F_MIN_MASK: .octa 0xf1f2f3f4f5f6f7f8f9fafbfcfdfeff0
+dec: .octa 0x1
+enc: .octa 0x2
+
+
.text
+
+#define STACK_OFFSET 8*3
+#define HashKey 16*0 // store HashKey <<1 mod poly here
+#define HashKey_2 16*1 // store HashKey^2 <<1 mod poly here
+#define HashKey_3 16*2 // store HashKey^3 <<1 mod poly here
+#define HashKey_4 16*3 // store HashKey^4 <<1 mod poly here
+#define HashKey_k 16*4 // store XOR of High 64 bits and Low 64
+ // bits of HashKey <<1 mod poly here
+ //(for Karatsuba purposes)
+#define HashKey_2_k 16*5 // store XOR of High 64 bits and Low 64
+ // bits of HashKey^2 <<1 mod poly here
+ // (for Karatsuba purposes)
+#define HashKey_3_k 16*6 // store XOR of High 64 bits and Low 64
+ // bits of HashKey^3 <<1 mod poly here
+ // (for Karatsuba purposes)
+#define HashKey_4_k 16*7 // store XOR of High 64 bits and Low 64
+ // bits of HashKey^4 <<1 mod poly here
+ // (for Karatsuba purposes)
+#define VARIABLE_OFFSET 16*8
+
+#define arg1 rdi
+#define arg2 rsi
+#define arg3 rdx
+#define arg4 rcx
+#define arg5 r8
+#define arg6 r9
+#define arg7 STACK_OFFSET+8(%r14)
+#define arg8 STACK_OFFSET+16(%r14)
+#define arg9 STACK_OFFSET+24(%r14)
+#define arg10 STACK_OFFSET+32(%r14)
+#endif
+
+
#define STATE1 %xmm0
#define STATE2 %xmm4
#define STATE3 %xmm5
@@ -32,12 +100,16 @@
#define IN IN1
#define KEY %xmm2
#define IV %xmm3
+
#define BSWAP_MASK %xmm10
#define CTR %xmm11
#define INC %xmm12
+#ifdef __x86_64__
+#define AREG %rax
#define KEYP %rdi
#define OUTP %rsi
+#define UKEYP OUTP
#define INP %rdx
#define LEN %rcx
#define IVP %r8
@@ -46,6 +118,1588 @@
#define TKEYP T1
#define T2 %r11
#define TCTR_LOW T2
+#else
+#define AREG %eax
+#define KEYP %edi
+#define OUTP AREG
+#define UKEYP OUTP
+#define INP %edx
+#define LEN %esi
+#define IVP %ebp
+#define KLEN %ebx
+#define T1 %ecx
+#define TKEYP T1
+#endif
+
+
+#ifdef __x86_64__
+/* GHASH_MUL MACRO to implement: Data*HashKey mod (128,127,126,121,0)
+*
+*
+* Input: A and B (128-bits each, bit-reflected)
+* Output: C = A*B*x mod poly, (i.e. >>1 )
+* To compute GH = GH*HashKey mod poly, give HK = HashKey<<1 mod poly as input
+* GH = GH * HK * x mod poly which is equivalent to GH*HashKey mod poly.
+*
+*/
+.macro GHASH_MUL GH HK TMP1 TMP2 TMP3 TMP4 TMP5
+ movdqa \GH, \TMP1
+ pshufd $78, \GH, \TMP2
+ pshufd $78, \HK, \TMP3
+ pxor \GH, \TMP2 # TMP2 = a1+a0
+ pxor \HK, \TMP3 # TMP3 = b1+b0
+ PCLMULQDQ 0x11, \HK, \TMP1 # TMP1 = a1*b1
+ PCLMULQDQ 0x00, \HK, \GH # GH = a0*b0
+ PCLMULQDQ 0x00, \TMP3, \TMP2 # TMP2 = (a0+a1)*(b1+b0)
+ pxor \GH, \TMP2
+ pxor \TMP1, \TMP2 # TMP2 = (a0*b0)+(a1*b0)
+ movdqa \TMP2, \TMP3
+ pslldq $8, \TMP3 # left shift TMP3 2 DWs
+ psrldq $8, \TMP2 # right shift TMP2 2 DWs
+ pxor \TMP3, \GH
+ pxor \TMP2, \TMP1 # TMP2:GH holds the result of GH*HK
+
+ # first phase of the reduction
+
+ movdqa \GH, \TMP2
+ movdqa \GH, \TMP3
+ movdqa \GH, \TMP4 # copy GH into TMP2,TMP3 and TMP4
+ # in in order to perform
+ # independent shifts
+ pslld $31, \TMP2 # packed right shift <<31
+ pslld $30, \TMP3 # packed right shift <<30
+ pslld $25, \TMP4 # packed right shift <<25
+ pxor \TMP3, \TMP2 # xor the shifted versions
+ pxor \TMP4, \TMP2
+ movdqa \TMP2, \TMP5
+ psrldq $4, \TMP5 # right shift TMP5 1 DW
+ pslldq $12, \TMP2 # left shift TMP2 3 DWs
+ pxor \TMP2, \GH
+
+ # second phase of the reduction
+
+ movdqa \GH,\TMP2 # copy GH into TMP2,TMP3 and TMP4
+ # in in order to perform
+ # independent shifts
+ movdqa \GH,\TMP3
+ movdqa \GH,\TMP4
+ psrld $1,\TMP2 # packed left shift >>1
+ psrld $2,\TMP3 # packed left shift >>2
+ psrld $7,\TMP4 # packed left shift >>7
+ pxor \TMP3,\TMP2 # xor the shifted versions
+ pxor \TMP4,\TMP2
+ pxor \TMP5, \TMP2
+ pxor \TMP2, \GH
+ pxor \TMP1, \GH # result is in TMP1
+.endm
+
+/*
+* if a = number of total plaintext bytes
+* b = floor(a/16)
+* num_initial_blocks = b mod 4
+* encrypt the initial num_initial_blocks blocks and apply ghash on
+* the ciphertext
+* %r10, %r11, %r12, %rax, %xmm5, %xmm6, %xmm7, %xmm8, %xmm9 registers
+* are clobbered
+* arg1, %arg2, %arg3, %r14 are used as a pointer only, not modified
+*/
+
+
+.macro INITIAL_BLOCKS_DEC num_initial_blocks TMP1 TMP2 TMP3 TMP4 TMP5 XMM0 XMM1 \
+XMM2 XMM3 XMM4 XMMDst TMP6 TMP7 i i_seq operation
+ mov arg7, %r10 # %r10 = AAD
+ mov arg8, %r12 # %r12 = aadLen
+ mov %r12, %r11
+ pxor %xmm\i, %xmm\i
+_get_AAD_loop\num_initial_blocks\operation:
+ movd (%r10), \TMP1
+ pslldq $12, \TMP1
+ psrldq $4, %xmm\i
+ pxor \TMP1, %xmm\i
+ add $4, %r10
+ sub $4, %r12
+ jne _get_AAD_loop\num_initial_blocks\operation
+ cmp $16, %r11
+ je _get_AAD_loop2_done\num_initial_blocks\operation
+ mov $16, %r12
+_get_AAD_loop2\num_initial_blocks\operation:
+ psrldq $4, %xmm\i
+ sub $4, %r12
+ cmp %r11, %r12
+ jne _get_AAD_loop2\num_initial_blocks\operation
+_get_AAD_loop2_done\num_initial_blocks\operation:
+ movdqa SHUF_MASK(%rip), %xmm14
+ PSHUFB_XMM %xmm14, %xmm\i # byte-reflect the AAD data
+
+ xor %r11, %r11 # initialise the data pointer offset as zero
+
+ # start AES for num_initial_blocks blocks
+
+ mov %arg5, %rax # %rax = *Y0
+ movdqu (%rax), \XMM0 # XMM0 = Y0
+ movdqa SHUF_MASK(%rip), %xmm14
+ PSHUFB_XMM %xmm14, \XMM0
+
+.if (\i == 5) || (\i == 6) || (\i == 7)
+.irpc index, \i_seq
+ paddd ONE(%rip), \XMM0 # INCR Y0
+ movdqa \XMM0, %xmm\index
+ movdqa SHUF_MASK(%rip), %xmm14
+ PSHUFB_XMM %xmm14, %xmm\index # perform a 16 byte swap
+
+.endr
+.irpc index, \i_seq
+ pxor 16*0(%arg1), %xmm\index
+.endr
+.irpc index, \i_seq
+ movaps 0x10(%rdi), \TMP1
+ AESENC \TMP1, %xmm\index # Round 1
+.endr
+.irpc index, \i_seq
+ movaps 0x20(%arg1), \TMP1
+ AESENC \TMP1, %xmm\index # Round 2
+.endr
+.irpc index, \i_seq
+ movaps 0x30(%arg1), \TMP1
+ AESENC \TMP1, %xmm\index # Round 2
+.endr
+.irpc index, \i_seq
+ movaps 0x40(%arg1), \TMP1
+ AESENC \TMP1, %xmm\index # Round 2
+.endr
+.irpc index, \i_seq
+ movaps 0x50(%arg1), \TMP1
+ AESENC \TMP1, %xmm\index # Round 2
+.endr
+.irpc index, \i_seq
+ movaps 0x60(%arg1), \TMP1
+ AESENC \TMP1, %xmm\index # Round 2
+.endr
+.irpc index, \i_seq
+ movaps 0x70(%arg1), \TMP1
+ AESENC \TMP1, %xmm\index # Round 2
+.endr
+.irpc index, \i_seq
+ movaps 0x80(%arg1), \TMP1
+ AESENC \TMP1, %xmm\index # Round 2
+.endr
+.irpc index, \i_seq
+ movaps 0x90(%arg1), \TMP1
+ AESENC \TMP1, %xmm\index # Round 2
+.endr
+.irpc index, \i_seq
+ movaps 0xa0(%arg1), \TMP1
+ AESENCLAST \TMP1, %xmm\index # Round 10
+.endr
+.irpc index, \i_seq
+ movdqu (%arg3 , %r11, 1), \TMP1
+ pxor \TMP1, %xmm\index
+ movdqu %xmm\index, (%arg2 , %r11, 1)
+ # write back plaintext/ciphertext for num_initial_blocks
+ add $16, %r11
+
+ movdqa \TMP1, %xmm\index
+ movdqa SHUF_MASK(%rip), %xmm14
+ PSHUFB_XMM %xmm14, %xmm\index
+
+ # prepare plaintext/ciphertext for GHASH computation
+.endr
+.endif
+ GHASH_MUL %xmm\i, \TMP3, \TMP1, \TMP2, \TMP4, \TMP5, \XMM1
+ # apply GHASH on num_initial_blocks blocks
+
+.if \i == 5
+ pxor %xmm5, %xmm6
+ GHASH_MUL %xmm6, \TMP3, \TMP1, \TMP2, \TMP4, \TMP5, \XMM1
+ pxor %xmm6, %xmm7
+ GHASH_MUL %xmm7, \TMP3, \TMP1, \TMP2, \TMP4, \TMP5, \XMM1
+ pxor %xmm7, %xmm8
+ GHASH_MUL %xmm8, \TMP3, \TMP1, \TMP2, \TMP4, \TMP5, \XMM1
+.elseif \i == 6
+ pxor %xmm6, %xmm7
+ GHASH_MUL %xmm7, \TMP3, \TMP1, \TMP2, \TMP4, \TMP5, \XMM1
+ pxor %xmm7, %xmm8
+ GHASH_MUL %xmm8, \TMP3, \TMP1, \TMP2, \TMP4, \TMP5, \XMM1
+.elseif \i == 7
+ pxor %xmm7, %xmm8
+ GHASH_MUL %xmm8, \TMP3, \TMP1, \TMP2, \TMP4, \TMP5, \XMM1
+.endif
+ cmp $64, %r13
+ jl _initial_blocks_done\num_initial_blocks\operation
+ # no need for precomputed values
+/*
+*
+* Precomputations for HashKey parallel with encryption of first 4 blocks.
+* Haskey_i_k holds XORed values of the low and high parts of the Haskey_i
+*/
+ paddd ONE(%rip), \XMM0 # INCR Y0
+ movdqa \XMM0, \XMM1
+ movdqa SHUF_MASK(%rip), %xmm14
+ PSHUFB_XMM %xmm14, \XMM1 # perform a 16 byte swap
+
+ paddd ONE(%rip), \XMM0 # INCR Y0
+ movdqa \XMM0, \XMM2
+ movdqa SHUF_MASK(%rip), %xmm14
+ PSHUFB_XMM %xmm14, \XMM2 # perform a 16 byte swap
+
+ paddd ONE(%rip), \XMM0 # INCR Y0
+ movdqa \XMM0, \XMM3
+ movdqa SHUF_MASK(%rip), %xmm14
+ PSHUFB_XMM %xmm14, \XMM3 # perform a 16 byte swap
+
+ paddd ONE(%rip), \XMM0 # INCR Y0
+ movdqa \XMM0, \XMM4
+ movdqa SHUF_MASK(%rip), %xmm14
+ PSHUFB_XMM %xmm14, \XMM4 # perform a 16 byte swap
+
+ pxor 16*0(%arg1), \XMM1
+ pxor 16*0(%arg1), \XMM2
+ pxor 16*0(%arg1), \XMM3
+ pxor 16*0(%arg1), \XMM4
+ movdqa \TMP3, \TMP5
+ pshufd $78, \TMP3, \TMP1
+ pxor \TMP3, \TMP1
+ movdqa \TMP1, HashKey_k(%rsp)
+ GHASH_MUL \TMP5, \TMP3, \TMP1, \TMP2, \TMP4, \TMP6, \TMP7
+# TMP5 = HashKey^2<<1 (mod poly)
+ movdqa \TMP5, HashKey_2(%rsp)
+# HashKey_2 = HashKey^2<<1 (mod poly)
+ pshufd $78, \TMP5, \TMP1
+ pxor \TMP5, \TMP1
+ movdqa \TMP1, HashKey_2_k(%rsp)
+.irpc index, 1234 # do 4 rounds
+ movaps 0x10*\index(%arg1), \TMP1
+ AESENC \TMP1, \XMM1
+ AESENC \TMP1, \XMM2
+ AESENC \TMP1, \XMM3
+ AESENC \TMP1, \XMM4
+.endr
+ GHASH_MUL \TMP5, \TMP3, \TMP1, \TMP2, \TMP4, \TMP6, \TMP7
+# TMP5 = HashKey^3<<1 (mod poly)
+ movdqa \TMP5, HashKey_3(%rsp)
+ pshufd $78, \TMP5, \TMP1
+ pxor \TMP5, \TMP1
+ movdqa \TMP1, HashKey_3_k(%rsp)
+.irpc index, 56789 # do next 5 rounds
+ movaps 0x10*\index(%arg1), \TMP1
+ AESENC \TMP1, \XMM1
+ AESENC \TMP1, \XMM2
+ AESENC \TMP1, \XMM3
+ AESENC \TMP1, \XMM4
+.endr
+ GHASH_MUL \TMP5, \TMP3, \TMP1, \TMP2, \TMP4, \TMP6, \TMP7
+# TMP5 = HashKey^3<<1 (mod poly)
+ movdqa \TMP5, HashKey_4(%rsp)
+ pshufd $78, \TMP5, \TMP1
+ pxor \TMP5, \TMP1
+ movdqa \TMP1, HashKey_4_k(%rsp)
+ movaps 0xa0(%arg1), \TMP2
+ AESENCLAST \TMP2, \XMM1
+ AESENCLAST \TMP2, \XMM2
+ AESENCLAST \TMP2, \XMM3
+ AESENCLAST \TMP2, \XMM4
+ movdqu 16*0(%arg3 , %r11 , 1), \TMP1
+ pxor \TMP1, \XMM1
+ movdqu \XMM1, 16*0(%arg2 , %r11 , 1)
+ movdqa \TMP1, \XMM1
+ movdqu 16*1(%arg3 , %r11 , 1), \TMP1
+ pxor \TMP1, \XMM2
+ movdqu \XMM2, 16*1(%arg2 , %r11 , 1)
+ movdqa \TMP1, \XMM2
+ movdqu 16*2(%arg3 , %r11 , 1), \TMP1
+ pxor \TMP1, \XMM3
+ movdqu \XMM3, 16*2(%arg2 , %r11 , 1)
+ movdqa \TMP1, \XMM3
+ movdqu 16*3(%arg3 , %r11 , 1), \TMP1
+ pxor \TMP1, \XMM4
+ movdqu \XMM4, 16*3(%arg2 , %r11 , 1)
+ movdqa \TMP1, \XMM4
+ add $64, %r11
+ movdqa SHUF_MASK(%rip), %xmm14
+ PSHUFB_XMM %xmm14, \XMM1 # perform a 16 byte swap
+ pxor \XMMDst, \XMM1
+# combine GHASHed value with the corresponding ciphertext
+ movdqa SHUF_MASK(%rip), %xmm14
+ PSHUFB_XMM %xmm14, \XMM2 # perform a 16 byte swap
+ movdqa SHUF_MASK(%rip), %xmm14
+ PSHUFB_XMM %xmm14, \XMM3 # perform a 16 byte swap
+ movdqa SHUF_MASK(%rip), %xmm14
+ PSHUFB_XMM %xmm14, \XMM4 # perform a 16 byte swap
+
+_initial_blocks_done\num_initial_blocks\operation:
+
+.endm
+
+
+/*
+* if a = number of total plaintext bytes
+* b = floor(a/16)
+* num_initial_blocks = b mod 4
+* encrypt the initial num_initial_blocks blocks and apply ghash on
+* the ciphertext
+* %r10, %r11, %r12, %rax, %xmm5, %xmm6, %xmm7, %xmm8, %xmm9 registers
+* are clobbered
+* arg1, %arg2, %arg3, %r14 are used as a pointer only, not modified
+*/
+
+
+.macro INITIAL_BLOCKS_ENC num_initial_blocks TMP1 TMP2 TMP3 TMP4 TMP5 XMM0 XMM1 \
+XMM2 XMM3 XMM4 XMMDst TMP6 TMP7 i i_seq operation
+ mov arg7, %r10 # %r10 = AAD
+ mov arg8, %r12 # %r12 = aadLen
+ mov %r12, %r11
+ pxor %xmm\i, %xmm\i
+_get_AAD_loop\num_initial_blocks\operation:
+ movd (%r10), \TMP1
+ pslldq $12, \TMP1
+ psrldq $4, %xmm\i
+ pxor \TMP1, %xmm\i
+ add $4, %r10
+ sub $4, %r12
+ jne _get_AAD_loop\num_initial_blocks\operation
+ cmp $16, %r11
+ je _get_AAD_loop2_done\num_initial_blocks\operation
+ mov $16, %r12
+_get_AAD_loop2\num_initial_blocks\operation:
+ psrldq $4, %xmm\i
+ sub $4, %r12
+ cmp %r11, %r12
+ jne _get_AAD_loop2\num_initial_blocks\operation
+_get_AAD_loop2_done\num_initial_blocks\operation:
+ movdqa SHUF_MASK(%rip), %xmm14
+ PSHUFB_XMM %xmm14, %xmm\i # byte-reflect the AAD data
+
+ xor %r11, %r11 # initialise the data pointer offset as zero
+
+ # start AES for num_initial_blocks blocks
+
+ mov %arg5, %rax # %rax = *Y0
+ movdqu (%rax), \XMM0 # XMM0 = Y0
+ movdqa SHUF_MASK(%rip), %xmm14
+ PSHUFB_XMM %xmm14, \XMM0
+
+.if (\i == 5) || (\i == 6) || (\i == 7)
+.irpc index, \i_seq
+ paddd ONE(%rip), \XMM0 # INCR Y0
+ movdqa \XMM0, %xmm\index
+ movdqa SHUF_MASK(%rip), %xmm14
+ PSHUFB_XMM %xmm14, %xmm\index # perform a 16 byte swap
+
+.endr
+.irpc index, \i_seq
+ pxor 16*0(%arg1), %xmm\index
+.endr
+.irpc index, \i_seq
+ movaps 0x10(%rdi), \TMP1
+ AESENC \TMP1, %xmm\index # Round 1
+.endr
+.irpc index, \i_seq
+ movaps 0x20(%arg1), \TMP1
+ AESENC \TMP1, %xmm\index # Round 2
+.endr
+.irpc index, \i_seq
+ movaps 0x30(%arg1), \TMP1
+ AESENC \TMP1, %xmm\index # Round 2
+.endr
+.irpc index, \i_seq
+ movaps 0x40(%arg1), \TMP1
+ AESENC \TMP1, %xmm\index # Round 2
+.endr
+.irpc index, \i_seq
+ movaps 0x50(%arg1), \TMP1
+ AESENC \TMP1, %xmm\index # Round 2
+.endr
+.irpc index, \i_seq
+ movaps 0x60(%arg1), \TMP1
+ AESENC \TMP1, %xmm\index # Round 2
+.endr
+.irpc index, \i_seq
+ movaps 0x70(%arg1), \TMP1
+ AESENC \TMP1, %xmm\index # Round 2
+.endr
+.irpc index, \i_seq
+ movaps 0x80(%arg1), \TMP1
+ AESENC \TMP1, %xmm\index # Round 2
+.endr
+.irpc index, \i_seq
+ movaps 0x90(%arg1), \TMP1
+ AESENC \TMP1, %xmm\index # Round 2
+.endr
+.irpc index, \i_seq
+ movaps 0xa0(%arg1), \TMP1
+ AESENCLAST \TMP1, %xmm\index # Round 10
+.endr
+.irpc index, \i_seq
+ movdqu (%arg3 , %r11, 1), \TMP1
+ pxor \TMP1, %xmm\index
+ movdqu %xmm\index, (%arg2 , %r11, 1)
+ # write back plaintext/ciphertext for num_initial_blocks
+ add $16, %r11
+
+ movdqa SHUF_MASK(%rip), %xmm14
+ PSHUFB_XMM %xmm14, %xmm\index
+
+ # prepare plaintext/ciphertext for GHASH computation
+.endr
+.endif
+ GHASH_MUL %xmm\i, \TMP3, \TMP1, \TMP2, \TMP4, \TMP5, \XMM1
+ # apply GHASH on num_initial_blocks blocks
+
+.if \i == 5
+ pxor %xmm5, %xmm6
+ GHASH_MUL %xmm6, \TMP3, \TMP1, \TMP2, \TMP4, \TMP5, \XMM1
+ pxor %xmm6, %xmm7
+ GHASH_MUL %xmm7, \TMP3, \TMP1, \TMP2, \TMP4, \TMP5, \XMM1
+ pxor %xmm7, %xmm8
+ GHASH_MUL %xmm8, \TMP3, \TMP1, \TMP2, \TMP4, \TMP5, \XMM1
+.elseif \i == 6
+ pxor %xmm6, %xmm7
+ GHASH_MUL %xmm7, \TMP3, \TMP1, \TMP2, \TMP4, \TMP5, \XMM1
+ pxor %xmm7, %xmm8
+ GHASH_MUL %xmm8, \TMP3, \TMP1, \TMP2, \TMP4, \TMP5, \XMM1
+.elseif \i == 7
+ pxor %xmm7, %xmm8
+ GHASH_MUL %xmm8, \TMP3, \TMP1, \TMP2, \TMP4, \TMP5, \XMM1
+.endif
+ cmp $64, %r13
+ jl _initial_blocks_done\num_initial_blocks\operation
+ # no need for precomputed values
+/*
+*
+* Precomputations for HashKey parallel with encryption of first 4 blocks.
+* Haskey_i_k holds XORed values of the low and high parts of the Haskey_i
+*/
+ paddd ONE(%rip), \XMM0 # INCR Y0
+ movdqa \XMM0, \XMM1
+ movdqa SHUF_MASK(%rip), %xmm14
+ PSHUFB_XMM %xmm14, \XMM1 # perform a 16 byte swap
+
+ paddd ONE(%rip), \XMM0 # INCR Y0
+ movdqa \XMM0, \XMM2
+ movdqa SHUF_MASK(%rip), %xmm14
+ PSHUFB_XMM %xmm14, \XMM2 # perform a 16 byte swap
+
+ paddd ONE(%rip), \XMM0 # INCR Y0
+ movdqa \XMM0, \XMM3
+ movdqa SHUF_MASK(%rip), %xmm14
+ PSHUFB_XMM %xmm14, \XMM3 # perform a 16 byte swap
+
+ paddd ONE(%rip), \XMM0 # INCR Y0
+ movdqa \XMM0, \XMM4
+ movdqa SHUF_MASK(%rip), %xmm14
+ PSHUFB_XMM %xmm14, \XMM4 # perform a 16 byte swap
+
+ pxor 16*0(%arg1), \XMM1
+ pxor 16*0(%arg1), \XMM2
+ pxor 16*0(%arg1), \XMM3
+ pxor 16*0(%arg1), \XMM4
+ movdqa \TMP3, \TMP5
+ pshufd $78, \TMP3, \TMP1
+ pxor \TMP3, \TMP1
+ movdqa \TMP1, HashKey_k(%rsp)
+ GHASH_MUL \TMP5, \TMP3, \TMP1, \TMP2, \TMP4, \TMP6, \TMP7
+# TMP5 = HashKey^2<<1 (mod poly)
+ movdqa \TMP5, HashKey_2(%rsp)
+# HashKey_2 = HashKey^2<<1 (mod poly)
+ pshufd $78, \TMP5, \TMP1
+ pxor \TMP5, \TMP1
+ movdqa \TMP1, HashKey_2_k(%rsp)
+.irpc index, 1234 # do 4 rounds
+ movaps 0x10*\index(%arg1), \TMP1
+ AESENC \TMP1, \XMM1
+ AESENC \TMP1, \XMM2
+ AESENC \TMP1, \XMM3
+ AESENC \TMP1, \XMM4
+.endr
+ GHASH_MUL \TMP5, \TMP3, \TMP1, \TMP2, \TMP4, \TMP6, \TMP7
+# TMP5 = HashKey^3<<1 (mod poly)
+ movdqa \TMP5, HashKey_3(%rsp)
+ pshufd $78, \TMP5, \TMP1
+ pxor \TMP5, \TMP1
+ movdqa \TMP1, HashKey_3_k(%rsp)
+.irpc index, 56789 # do next 5 rounds
+ movaps 0x10*\index(%arg1), \TMP1
+ AESENC \TMP1, \XMM1
+ AESENC \TMP1, \XMM2
+ AESENC \TMP1, \XMM3
+ AESENC \TMP1, \XMM4
+.endr
+ GHASH_MUL \TMP5, \TMP3, \TMP1, \TMP2, \TMP4, \TMP6, \TMP7
+# TMP5 = HashKey^3<<1 (mod poly)
+ movdqa \TMP5, HashKey_4(%rsp)
+ pshufd $78, \TMP5, \TMP1
+ pxor \TMP5, \TMP1
+ movdqa \TMP1, HashKey_4_k(%rsp)
+ movaps 0xa0(%arg1), \TMP2
+ AESENCLAST \TMP2, \XMM1
+ AESENCLAST \TMP2, \XMM2
+ AESENCLAST \TMP2, \XMM3
+ AESENCLAST \TMP2, \XMM4
+ movdqu 16*0(%arg3 , %r11 , 1), \TMP1
+ pxor \TMP1, \XMM1
+ movdqu 16*1(%arg3 , %r11 , 1), \TMP1
+ pxor \TMP1, \XMM2
+ movdqu 16*2(%arg3 , %r11 , 1), \TMP1
+ pxor \TMP1, \XMM3
+ movdqu 16*3(%arg3 , %r11 , 1), \TMP1
+ pxor \TMP1, \XMM4
+ movdqu \XMM1, 16*0(%arg2 , %r11 , 1)
+ movdqu \XMM2, 16*1(%arg2 , %r11 , 1)
+ movdqu \XMM3, 16*2(%arg2 , %r11 , 1)
+ movdqu \XMM4, 16*3(%arg2 , %r11 , 1)
+
+ add $64, %r11
+ movdqa SHUF_MASK(%rip), %xmm14
+ PSHUFB_XMM %xmm14, \XMM1 # perform a 16 byte swap
+ pxor \XMMDst, \XMM1
+# combine GHASHed value with the corresponding ciphertext
+ movdqa SHUF_MASK(%rip), %xmm14
+ PSHUFB_XMM %xmm14, \XMM2 # perform a 16 byte swap
+ movdqa SHUF_MASK(%rip), %xmm14
+ PSHUFB_XMM %xmm14, \XMM3 # perform a 16 byte swap
+ movdqa SHUF_MASK(%rip), %xmm14
+ PSHUFB_XMM %xmm14, \XMM4 # perform a 16 byte swap
+
+_initial_blocks_done\num_initial_blocks\operation:
+
+.endm
+
+/*
+* encrypt 4 blocks at a time
+* ghash the 4 previously encrypted ciphertext blocks
+* arg1, %arg2, %arg3 are used as pointers only, not modified
+* %r11 is the data offset value
+*/
+.macro GHASH_4_ENCRYPT_4_PARALLEL_ENC TMP1 TMP2 TMP3 TMP4 TMP5 \
+TMP6 XMM0 XMM1 XMM2 XMM3 XMM4 XMM5 XMM6 XMM7 XMM8 operation
+
+ movdqa \XMM1, \XMM5
+ movdqa \XMM2, \XMM6
+ movdqa \XMM3, \XMM7
+ movdqa \XMM4, \XMM8
+
+ movdqa SHUF_MASK(%rip), %xmm15
+ # multiply TMP5 * HashKey using karatsuba
+
+ movdqa \XMM5, \TMP4
+ pshufd $78, \XMM5, \TMP6
+ pxor \XMM5, \TMP6
+ paddd ONE(%rip), \XMM0 # INCR CNT
+ movdqa HashKey_4(%rsp), \TMP5
+ PCLMULQDQ 0x11, \TMP5, \TMP4 # TMP4 = a1*b1
+ movdqa \XMM0, \XMM1
+ paddd ONE(%rip), \XMM0 # INCR CNT
+ movdqa \XMM0, \XMM2
+ paddd ONE(%rip), \XMM0 # INCR CNT
+ movdqa \XMM0, \XMM3
+ paddd ONE(%rip), \XMM0 # INCR CNT
+ movdqa \XMM0, \XMM4
+ PSHUFB_XMM %xmm15, \XMM1 # perform a 16 byte swap
+ PCLMULQDQ 0x00, \TMP5, \XMM5 # XMM5 = a0*b0
+ PSHUFB_XMM %xmm15, \XMM2 # perform a 16 byte swap
+ PSHUFB_XMM %xmm15, \XMM3 # perform a 16 byte swap
+ PSHUFB_XMM %xmm15, \XMM4 # perform a 16 byte swap
+
+ pxor (%arg1), \XMM1
+ pxor (%arg1), \XMM2
+ pxor (%arg1), \XMM3
+ pxor (%arg1), \XMM4
+ movdqa HashKey_4_k(%rsp), \TMP5
+ PCLMULQDQ 0x00, \TMP5, \TMP6 # TMP6 = (a1+a0)*(b1+b0)
+ movaps 0x10(%arg1), \TMP1
+ AESENC \TMP1, \XMM1 # Round 1
+ AESENC \TMP1, \XMM2
+ AESENC \TMP1, \XMM3
+ AESENC \TMP1, \XMM4
+ movaps 0x20(%arg1), \TMP1
+ AESENC \TMP1, \XMM1 # Round 2
+ AESENC \TMP1, \XMM2
+ AESENC \TMP1, \XMM3
+ AESENC \TMP1, \XMM4
+ movdqa \XMM6, \TMP1
+ pshufd $78, \XMM6, \TMP2
+ pxor \XMM6, \TMP2
+ movdqa HashKey_3(%rsp), \TMP5
+ PCLMULQDQ 0x11, \TMP5, \TMP1 # TMP1 = a1 * b1
+ movaps 0x30(%arg1), \TMP3
+ AESENC \TMP3, \XMM1 # Round 3
+ AESENC \TMP3, \XMM2
+ AESENC \TMP3, \XMM3
+ AESENC \TMP3, \XMM4
+ PCLMULQDQ 0x00, \TMP5, \XMM6 # XMM6 = a0*b0
+ movaps 0x40(%arg1), \TMP3
+ AESENC \TMP3, \XMM1 # Round 4
+ AESENC \TMP3, \XMM2
+ AESENC \TMP3, \XMM3
+ AESENC \TMP3, \XMM4
+ movdqa HashKey_3_k(%rsp), \TMP5
+ PCLMULQDQ 0x00, \TMP5, \TMP2 # TMP2 = (a1+a0)*(b1+b0)
+ movaps 0x50(%arg1), \TMP3
+ AESENC \TMP3, \XMM1 # Round 5
+ AESENC \TMP3, \XMM2
+ AESENC \TMP3, \XMM3
+ AESENC \TMP3, \XMM4
+ pxor \TMP1, \TMP4
+# accumulate the results in TMP4:XMM5, TMP6 holds the middle part
+ pxor \XMM6, \XMM5
+ pxor \TMP2, \TMP6
+ movdqa \XMM7, \TMP1
+ pshufd $78, \XMM7, \TMP2
+ pxor \XMM7, \TMP2
+ movdqa HashKey_2(%rsp ), \TMP5
+
+ # Multiply TMP5 * HashKey using karatsuba
+
+ PCLMULQDQ 0x11, \TMP5, \TMP1 # TMP1 = a1*b1
+ movaps 0x60(%arg1), \TMP3
+ AESENC \TMP3, \XMM1 # Round 6
+ AESENC \TMP3, \XMM2
+ AESENC \TMP3, \XMM3
+ AESENC \TMP3, \XMM4
+ PCLMULQDQ 0x00, \TMP5, \XMM7 # XMM7 = a0*b0
+ movaps 0x70(%arg1), \TMP3
+ AESENC \TMP3, \XMM1 # Round 7
+ AESENC \TMP3, \XMM2
+ AESENC \TMP3, \XMM3
+ AESENC \TMP3, \XMM4
+ movdqa HashKey_2_k(%rsp), \TMP5
+ PCLMULQDQ 0x00, \TMP5, \TMP2 # TMP2 = (a1+a0)*(b1+b0)
+ movaps 0x80(%arg1), \TMP3
+ AESENC \TMP3, \XMM1 # Round 8
+ AESENC \TMP3, \XMM2
+ AESENC \TMP3, \XMM3
+ AESENC \TMP3, \XMM4
+ pxor \TMP1, \TMP4
+# accumulate the results in TMP4:XMM5, TMP6 holds the middle part
+ pxor \XMM7, \XMM5
+ pxor \TMP2, \TMP6
+
+ # Multiply XMM8 * HashKey
+ # XMM8 and TMP5 hold the values for the two operands
+
+ movdqa \XMM8, \TMP1
+ pshufd $78, \XMM8, \TMP2
+ pxor \XMM8, \TMP2
+ movdqa HashKey(%rsp), \TMP5
+ PCLMULQDQ 0x11, \TMP5, \TMP1 # TMP1 = a1*b1
+ movaps 0x90(%arg1), \TMP3
+ AESENC \TMP3, \XMM1 # Round 9
+ AESENC \TMP3, \XMM2
+ AESENC \TMP3, \XMM3
+ AESENC \TMP3, \XMM4
+ PCLMULQDQ 0x00, \TMP5, \XMM8 # XMM8 = a0*b0
+ movaps 0xa0(%arg1), \TMP3
+ AESENCLAST \TMP3, \XMM1 # Round 10
+ AESENCLAST \TMP3, \XMM2
+ AESENCLAST \TMP3, \XMM3
+ AESENCLAST \TMP3, \XMM4
+ movdqa HashKey_k(%rsp), \TMP5
+ PCLMULQDQ 0x00, \TMP5, \TMP2 # TMP2 = (a1+a0)*(b1+b0)
+ movdqu (%arg3,%r11,1), \TMP3
+ pxor \TMP3, \XMM1 # Ciphertext/Plaintext XOR EK
+ movdqu 16(%arg3,%r11,1), \TMP3
+ pxor \TMP3, \XMM2 # Ciphertext/Plaintext XOR EK
+ movdqu 32(%arg3,%r11,1), \TMP3
+ pxor \TMP3, \XMM3 # Ciphertext/Plaintext XOR EK
+ movdqu 48(%arg3,%r11,1), \TMP3
+ pxor \TMP3, \XMM4 # Ciphertext/Plaintext XOR EK
+ movdqu \XMM1, (%arg2,%r11,1) # Write to the ciphertext buffer
+ movdqu \XMM2, 16(%arg2,%r11,1) # Write to the ciphertext buffer
+ movdqu \XMM3, 32(%arg2,%r11,1) # Write to the ciphertext buffer
+ movdqu \XMM4, 48(%arg2,%r11,1) # Write to the ciphertext buffer
+ PSHUFB_XMM %xmm15, \XMM1 # perform a 16 byte swap
+ PSHUFB_XMM %xmm15, \XMM2 # perform a 16 byte swap
+ PSHUFB_XMM %xmm15, \XMM3 # perform a 16 byte swap
+ PSHUFB_XMM %xmm15, \XMM4 # perform a 16 byte swap
+
+ pxor \TMP4, \TMP1
+ pxor \XMM8, \XMM5
+ pxor \TMP6, \TMP2
+ pxor \TMP1, \TMP2
+ pxor \XMM5, \TMP2
+ movdqa \TMP2, \TMP3
+ pslldq $8, \TMP3 # left shift TMP3 2 DWs
+ psrldq $8, \TMP2 # right shift TMP2 2 DWs
+ pxor \TMP3, \XMM5
+ pxor \TMP2, \TMP1 # accumulate the results in TMP1:XMM5
+
+ # first phase of reduction
+
+ movdqa \XMM5, \TMP2
+ movdqa \XMM5, \TMP3
+ movdqa \XMM5, \TMP4
+# move XMM5 into TMP2, TMP3, TMP4 in order to perform shifts independently
+ pslld $31, \TMP2 # packed right shift << 31
+ pslld $30, \TMP3 # packed right shift << 30
+ pslld $25, \TMP4 # packed right shift << 25
+ pxor \TMP3, \TMP2 # xor the shifted versions
+ pxor \TMP4, \TMP2
+ movdqa \TMP2, \TMP5
+ psrldq $4, \TMP5 # right shift T5 1 DW
+ pslldq $12, \TMP2 # left shift T2 3 DWs
+ pxor \TMP2, \XMM5
+
+ # second phase of reduction
+
+ movdqa \XMM5,\TMP2 # make 3 copies of XMM5 into TMP2, TMP3, TMP4
+ movdqa \XMM5,\TMP3
+ movdqa \XMM5,\TMP4
+ psrld $1, \TMP2 # packed left shift >>1
+ psrld $2, \TMP3 # packed left shift >>2
+ psrld $7, \TMP4 # packed left shift >>7
+ pxor \TMP3,\TMP2 # xor the shifted versions
+ pxor \TMP4,\TMP2
+ pxor \TMP5, \TMP2
+ pxor \TMP2, \XMM5
+ pxor \TMP1, \XMM5 # result is in TMP1
+
+ pxor \XMM5, \XMM1
+.endm
+
+/*
+* decrypt 4 blocks at a time
+* ghash the 4 previously decrypted ciphertext blocks
+* arg1, %arg2, %arg3 are used as pointers only, not modified
+* %r11 is the data offset value
+*/
+.macro GHASH_4_ENCRYPT_4_PARALLEL_DEC TMP1 TMP2 TMP3 TMP4 TMP5 \
+TMP6 XMM0 XMM1 XMM2 XMM3 XMM4 XMM5 XMM6 XMM7 XMM8 operation
+
+ movdqa \XMM1, \XMM5
+ movdqa \XMM2, \XMM6
+ movdqa \XMM3, \XMM7
+ movdqa \XMM4, \XMM8
+
+ movdqa SHUF_MASK(%rip), %xmm15
+ # multiply TMP5 * HashKey using karatsuba
+
+ movdqa \XMM5, \TMP4
+ pshufd $78, \XMM5, \TMP6
+ pxor \XMM5, \TMP6
+ paddd ONE(%rip), \XMM0 # INCR CNT
+ movdqa HashKey_4(%rsp), \TMP5
+ PCLMULQDQ 0x11, \TMP5, \TMP4 # TMP4 = a1*b1
+ movdqa \XMM0, \XMM1
+ paddd ONE(%rip), \XMM0 # INCR CNT
+ movdqa \XMM0, \XMM2
+ paddd ONE(%rip), \XMM0 # INCR CNT
+ movdqa \XMM0, \XMM3
+ paddd ONE(%rip), \XMM0 # INCR CNT
+ movdqa \XMM0, \XMM4
+ PSHUFB_XMM %xmm15, \XMM1 # perform a 16 byte swap
+ PCLMULQDQ 0x00, \TMP5, \XMM5 # XMM5 = a0*b0
+ PSHUFB_XMM %xmm15, \XMM2 # perform a 16 byte swap
+ PSHUFB_XMM %xmm15, \XMM3 # perform a 16 byte swap
+ PSHUFB_XMM %xmm15, \XMM4 # perform a 16 byte swap
+
+ pxor (%arg1), \XMM1
+ pxor (%arg1), \XMM2
+ pxor (%arg1), \XMM3
+ pxor (%arg1), \XMM4
+ movdqa HashKey_4_k(%rsp), \TMP5
+ PCLMULQDQ 0x00, \TMP5, \TMP6 # TMP6 = (a1+a0)*(b1+b0)
+ movaps 0x10(%arg1), \TMP1
+ AESENC \TMP1, \XMM1 # Round 1
+ AESENC \TMP1, \XMM2
+ AESENC \TMP1, \XMM3
+ AESENC \TMP1, \XMM4
+ movaps 0x20(%arg1), \TMP1
+ AESENC \TMP1, \XMM1 # Round 2
+ AESENC \TMP1, \XMM2
+ AESENC \TMP1, \XMM3
+ AESENC \TMP1, \XMM4
+ movdqa \XMM6, \TMP1
+ pshufd $78, \XMM6, \TMP2
+ pxor \XMM6, \TMP2
+ movdqa HashKey_3(%rsp), \TMP5
+ PCLMULQDQ 0x11, \TMP5, \TMP1 # TMP1 = a1 * b1
+ movaps 0x30(%arg1), \TMP3
+ AESENC \TMP3, \XMM1 # Round 3
+ AESENC \TMP3, \XMM2
+ AESENC \TMP3, \XMM3
+ AESENC \TMP3, \XMM4
+ PCLMULQDQ 0x00, \TMP5, \XMM6 # XMM6 = a0*b0
+ movaps 0x40(%arg1), \TMP3
+ AESENC \TMP3, \XMM1 # Round 4
+ AESENC \TMP3, \XMM2
+ AESENC \TMP3, \XMM3
+ AESENC \TMP3, \XMM4
+ movdqa HashKey_3_k(%rsp), \TMP5
+ PCLMULQDQ 0x00, \TMP5, \TMP2 # TMP2 = (a1+a0)*(b1+b0)
+ movaps 0x50(%arg1), \TMP3
+ AESENC \TMP3, \XMM1 # Round 5
+ AESENC \TMP3, \XMM2
+ AESENC \TMP3, \XMM3
+ AESENC \TMP3, \XMM4
+ pxor \TMP1, \TMP4
+# accumulate the results in TMP4:XMM5, TMP6 holds the middle part
+ pxor \XMM6, \XMM5
+ pxor \TMP2, \TMP6
+ movdqa \XMM7, \TMP1
+ pshufd $78, \XMM7, \TMP2
+ pxor \XMM7, \TMP2
+ movdqa HashKey_2(%rsp ), \TMP5
+
+ # Multiply TMP5 * HashKey using karatsuba
+
+ PCLMULQDQ 0x11, \TMP5, \TMP1 # TMP1 = a1*b1
+ movaps 0x60(%arg1), \TMP3
+ AESENC \TMP3, \XMM1 # Round 6
+ AESENC \TMP3, \XMM2
+ AESENC \TMP3, \XMM3
+ AESENC \TMP3, \XMM4
+ PCLMULQDQ 0x00, \TMP5, \XMM7 # XMM7 = a0*b0
+ movaps 0x70(%arg1), \TMP3
+ AESENC \TMP3, \XMM1 # Round 7
+ AESENC \TMP3, \XMM2
+ AESENC \TMP3, \XMM3
+ AESENC \TMP3, \XMM4
+ movdqa HashKey_2_k(%rsp), \TMP5
+ PCLMULQDQ 0x00, \TMP5, \TMP2 # TMP2 = (a1+a0)*(b1+b0)
+ movaps 0x80(%arg1), \TMP3
+ AESENC \TMP3, \XMM1 # Round 8
+ AESENC \TMP3, \XMM2
+ AESENC \TMP3, \XMM3
+ AESENC \TMP3, \XMM4
+ pxor \TMP1, \TMP4
+# accumulate the results in TMP4:XMM5, TMP6 holds the middle part
+ pxor \XMM7, \XMM5
+ pxor \TMP2, \TMP6
+
+ # Multiply XMM8 * HashKey
+ # XMM8 and TMP5 hold the values for the two operands
+
+ movdqa \XMM8, \TMP1
+ pshufd $78, \XMM8, \TMP2
+ pxor \XMM8, \TMP2
+ movdqa HashKey(%rsp), \TMP5
+ PCLMULQDQ 0x11, \TMP5, \TMP1 # TMP1 = a1*b1
+ movaps 0x90(%arg1), \TMP3
+ AESENC \TMP3, \XMM1 # Round 9
+ AESENC \TMP3, \XMM2
+ AESENC \TMP3, \XMM3
+ AESENC \TMP3, \XMM4
+ PCLMULQDQ 0x00, \TMP5, \XMM8 # XMM8 = a0*b0
+ movaps 0xa0(%arg1), \TMP3
+ AESENCLAST \TMP3, \XMM1 # Round 10
+ AESENCLAST \TMP3, \XMM2
+ AESENCLAST \TMP3, \XMM3
+ AESENCLAST \TMP3, \XMM4
+ movdqa HashKey_k(%rsp), \TMP5
+ PCLMULQDQ 0x00, \TMP5, \TMP2 # TMP2 = (a1+a0)*(b1+b0)
+ movdqu (%arg3,%r11,1), \TMP3
+ pxor \TMP3, \XMM1 # Ciphertext/Plaintext XOR EK
+ movdqu \XMM1, (%arg2,%r11,1) # Write to plaintext buffer
+ movdqa \TMP3, \XMM1
+ movdqu 16(%arg3,%r11,1), \TMP3
+ pxor \TMP3, \XMM2 # Ciphertext/Plaintext XOR EK
+ movdqu \XMM2, 16(%arg2,%r11,1) # Write to plaintext buffer
+ movdqa \TMP3, \XMM2
+ movdqu 32(%arg3,%r11,1), \TMP3
+ pxor \TMP3, \XMM3 # Ciphertext/Plaintext XOR EK
+ movdqu \XMM3, 32(%arg2,%r11,1) # Write to plaintext buffer
+ movdqa \TMP3, \XMM3
+ movdqu 48(%arg3,%r11,1), \TMP3
+ pxor \TMP3, \XMM4 # Ciphertext/Plaintext XOR EK
+ movdqu \XMM4, 48(%arg2,%r11,1) # Write to plaintext buffer
+ movdqa \TMP3, \XMM4
+ PSHUFB_XMM %xmm15, \XMM1 # perform a 16 byte swap
+ PSHUFB_XMM %xmm15, \XMM2 # perform a 16 byte swap
+ PSHUFB_XMM %xmm15, \XMM3 # perform a 16 byte swap
+ PSHUFB_XMM %xmm15, \XMM4 # perform a 16 byte swap
+
+ pxor \TMP4, \TMP1
+ pxor \XMM8, \XMM5
+ pxor \TMP6, \TMP2
+ pxor \TMP1, \TMP2
+ pxor \XMM5, \TMP2
+ movdqa \TMP2, \TMP3
+ pslldq $8, \TMP3 # left shift TMP3 2 DWs
+ psrldq $8, \TMP2 # right shift TMP2 2 DWs
+ pxor \TMP3, \XMM5
+ pxor \TMP2, \TMP1 # accumulate the results in TMP1:XMM5
+
+ # first phase of reduction
+
+ movdqa \XMM5, \TMP2
+ movdqa \XMM5, \TMP3
+ movdqa \XMM5, \TMP4
+# move XMM5 into TMP2, TMP3, TMP4 in order to perform shifts independently
+ pslld $31, \TMP2 # packed right shift << 31
+ pslld $30, \TMP3 # packed right shift << 30
+ pslld $25, \TMP4 # packed right shift << 25
+ pxor \TMP3, \TMP2 # xor the shifted versions
+ pxor \TMP4, \TMP2
+ movdqa \TMP2, \TMP5
+ psrldq $4, \TMP5 # right shift T5 1 DW
+ pslldq $12, \TMP2 # left shift T2 3 DWs
+ pxor \TMP2, \XMM5
+
+ # second phase of reduction
+
+ movdqa \XMM5,\TMP2 # make 3 copies of XMM5 into TMP2, TMP3, TMP4
+ movdqa \XMM5,\TMP3
+ movdqa \XMM5,\TMP4
+ psrld $1, \TMP2 # packed left shift >>1
+ psrld $2, \TMP3 # packed left shift >>2
+ psrld $7, \TMP4 # packed left shift >>7
+ pxor \TMP3,\TMP2 # xor the shifted versions
+ pxor \TMP4,\TMP2
+ pxor \TMP5, \TMP2
+ pxor \TMP2, \XMM5
+ pxor \TMP1, \XMM5 # result is in TMP1
+
+ pxor \XMM5, \XMM1
+.endm
+
+/* GHASH the last 4 ciphertext blocks. */
+.macro GHASH_LAST_4 TMP1 TMP2 TMP3 TMP4 TMP5 TMP6 \
+TMP7 XMM1 XMM2 XMM3 XMM4 XMMDst
+
+ # Multiply TMP6 * HashKey (using Karatsuba)
+
+ movdqa \XMM1, \TMP6
+ pshufd $78, \XMM1, \TMP2
+ pxor \XMM1, \TMP2
+ movdqa HashKey_4(%rsp), \TMP5
+ PCLMULQDQ 0x11, \TMP5, \TMP6 # TMP6 = a1*b1
+ PCLMULQDQ 0x00, \TMP5, \XMM1 # XMM1 = a0*b0
+ movdqa HashKey_4_k(%rsp), \TMP4
+ PCLMULQDQ 0x00, \TMP4, \TMP2 # TMP2 = (a1+a0)*(b1+b0)
+ movdqa \XMM1, \XMMDst
+ movdqa \TMP2, \XMM1 # result in TMP6, XMMDst, XMM1
+
+ # Multiply TMP1 * HashKey (using Karatsuba)
+
+ movdqa \XMM2, \TMP1
+ pshufd $78, \XMM2, \TMP2
+ pxor \XMM2, \TMP2
+ movdqa HashKey_3(%rsp), \TMP5
+ PCLMULQDQ 0x11, \TMP5, \TMP1 # TMP1 = a1*b1
+ PCLMULQDQ 0x00, \TMP5, \XMM2 # XMM2 = a0*b0
+ movdqa HashKey_3_k(%rsp), \TMP4
+ PCLMULQDQ 0x00, \TMP4, \TMP2 # TMP2 = (a1+a0)*(b1+b0)
+ pxor \TMP1, \TMP6
+ pxor \XMM2, \XMMDst
+ pxor \TMP2, \XMM1
+# results accumulated in TMP6, XMMDst, XMM1
+
+ # Multiply TMP1 * HashKey (using Karatsuba)
+
+ movdqa \XMM3, \TMP1
+ pshufd $78, \XMM3, \TMP2
+ pxor \XMM3, \TMP2
+ movdqa HashKey_2(%rsp), \TMP5
+ PCLMULQDQ 0x11, \TMP5, \TMP1 # TMP1 = a1*b1
+ PCLMULQDQ 0x00, \TMP5, \XMM3 # XMM3 = a0*b0
+ movdqa HashKey_2_k(%rsp), \TMP4
+ PCLMULQDQ 0x00, \TMP4, \TMP2 # TMP2 = (a1+a0)*(b1+b0)
+ pxor \TMP1, \TMP6
+ pxor \XMM3, \XMMDst
+ pxor \TMP2, \XMM1 # results accumulated in TMP6, XMMDst, XMM1
+
+ # Multiply TMP1 * HashKey (using Karatsuba)
+ movdqa \XMM4, \TMP1
+ pshufd $78, \XMM4, \TMP2
+ pxor \XMM4, \TMP2
+ movdqa HashKey(%rsp), \TMP5
+ PCLMULQDQ 0x11, \TMP5, \TMP1 # TMP1 = a1*b1
+ PCLMULQDQ 0x00, \TMP5, \XMM4 # XMM4 = a0*b0
+ movdqa HashKey_k(%rsp), \TMP4
+ PCLMULQDQ 0x00, \TMP4, \TMP2 # TMP2 = (a1+a0)*(b1+b0)
+ pxor \TMP1, \TMP6
+ pxor \XMM4, \XMMDst
+ pxor \XMM1, \TMP2
+ pxor \TMP6, \TMP2
+ pxor \XMMDst, \TMP2
+ # middle section of the temp results combined as in karatsuba algorithm
+ movdqa \TMP2, \TMP4
+ pslldq $8, \TMP4 # left shift TMP4 2 DWs
+ psrldq $8, \TMP2 # right shift TMP2 2 DWs
+ pxor \TMP4, \XMMDst
+ pxor \TMP2, \TMP6
+# TMP6:XMMDst holds the result of the accumulated carry-less multiplications
+ # first phase of the reduction
+ movdqa \XMMDst, \TMP2
+ movdqa \XMMDst, \TMP3
+ movdqa \XMMDst, \TMP4
+# move XMMDst into TMP2, TMP3, TMP4 in order to perform 3 shifts independently
+ pslld $31, \TMP2 # packed right shifting << 31
+ pslld $30, \TMP3 # packed right shifting << 30
+ pslld $25, \TMP4 # packed right shifting << 25
+ pxor \TMP3, \TMP2 # xor the shifted versions
+ pxor \TMP4, \TMP2
+ movdqa \TMP2, \TMP7
+ psrldq $4, \TMP7 # right shift TMP7 1 DW
+ pslldq $12, \TMP2 # left shift TMP2 3 DWs
+ pxor \TMP2, \XMMDst
+
+ # second phase of the reduction
+ movdqa \XMMDst, \TMP2
+ # make 3 copies of XMMDst for doing 3 shift operations
+ movdqa \XMMDst, \TMP3
+ movdqa \XMMDst, \TMP4
+ psrld $1, \TMP2 # packed left shift >> 1
+ psrld $2, \TMP3 # packed left shift >> 2
+ psrld $7, \TMP4 # packed left shift >> 7
+ pxor \TMP3, \TMP2 # xor the shifted versions
+ pxor \TMP4, \TMP2
+ pxor \TMP7, \TMP2
+ pxor \TMP2, \XMMDst
+ pxor \TMP6, \XMMDst # reduced result is in XMMDst
+.endm
+
+/* Encryption of a single block done*/
+.macro ENCRYPT_SINGLE_BLOCK XMM0 TMP1
+
+ pxor (%arg1), \XMM0
+ movaps 16(%arg1), \TMP1
+ AESENC \TMP1, \XMM0
+ movaps 32(%arg1), \TMP1
+ AESENC \TMP1, \XMM0
+ movaps 48(%arg1), \TMP1
+ AESENC \TMP1, \XMM0
+ movaps 64(%arg1), \TMP1
+ AESENC \TMP1, \XMM0
+ movaps 80(%arg1), \TMP1
+ AESENC \TMP1, \XMM0
+ movaps 96(%arg1), \TMP1
+ AESENC \TMP1, \XMM0
+ movaps 112(%arg1), \TMP1
+ AESENC \TMP1, \XMM0
+ movaps 128(%arg1), \TMP1
+ AESENC \TMP1, \XMM0
+ movaps 144(%arg1), \TMP1
+ AESENC \TMP1, \XMM0
+ movaps 160(%arg1), \TMP1
+ AESENCLAST \TMP1, \XMM0
+.endm
+
+
+/*****************************************************************************
+* void aesni_gcm_dec(void *aes_ctx, // AES Key schedule. Starts on a 16 byte boundary.
+* u8 *out, // Plaintext output. Encrypt in-place is allowed.
+* const u8 *in, // Ciphertext input
+* u64 plaintext_len, // Length of data in bytes for decryption.
+* u8 *iv, // Pre-counter block j0: 4 byte salt (from Security Association)
+* // concatenated with 8 byte Initialisation Vector (from IPSec ESP Payload)
+* // concatenated with 0x00000001. 16-byte aligned pointer.
+* u8 *hash_subkey, // H, the Hash sub key input. Data starts on a 16-byte boundary.
+* const u8 *aad, // Additional Authentication Data (AAD)
+* u64 aad_len, // Length of AAD in bytes. With RFC4106 this is going to be 8 or 12 bytes
+* u8 *auth_tag, // Authenticated Tag output. The driver will compare this to the
+* // given authentication tag and only return the plaintext if they match.
+* u64 auth_tag_len); // Authenticated Tag Length in bytes. Valid values are 16
+* // (most likely), 12 or 8.
+*
+* Assumptions:
+*
+* keys:
+* keys are pre-expanded and aligned to 16 bytes. we are using the first
+* set of 11 keys in the data structure void *aes_ctx
+*
+* iv:
+* 0 1 2 3
+* 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+* | Salt (From the SA) |
+* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+* | Initialization Vector |
+* | (This is the sequence number from IPSec header) |
+* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+* | 0x1 |
+* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+*
+*
+*
+* AAD:
+* AAD padded to 128 bits with 0
+* for example, assume AAD is a u32 vector
+*
+* if AAD is 8 bytes:
+* AAD[3] = {A0, A1};
+* padded AAD in xmm register = {A1 A0 0 0}
+*
+* 0 1 2 3
+* 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+* | SPI (A1) |
+* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+* | 32-bit Sequence Number (A0) |
+* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+* | 0x0 |
+* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+*
+* AAD Format with 32-bit Sequence Number
+*
+* if AAD is 12 bytes:
+* AAD[3] = {A0, A1, A2};
+* padded AAD in xmm register = {A2 A1 A0 0}
+*
+* 0 1 2 3
+* 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+* 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+* | SPI (A2) |
+* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+* | 64-bit Extended Sequence Number {A1,A0} |
+* | |
+* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+* | 0x0 |
+* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+*
+* AAD Format with 64-bit Extended Sequence Number
+*
+* aadLen:
+* from the definition of the spec, aadLen can only be 8 or 12 bytes.
+* The code supports 16 too but for other sizes, the code will fail.
+*
+* TLen:
+* from the definition of the spec, TLen can only be 8, 12 or 16 bytes.
+* For other sizes, the code will fail.
+*
+* poly = x^128 + x^127 + x^126 + x^121 + 1
+*
+*****************************************************************************/
+
+ENTRY(aesni_gcm_dec)
+ push %r12
+ push %r13
+ push %r14
+ mov %rsp, %r14
+/*
+* states of %xmm registers %xmm6:%xmm15 not saved
+* all %xmm registers are clobbered
+*/
+ sub $VARIABLE_OFFSET, %rsp
+ and $~63, %rsp # align rsp to 64 bytes
+ mov %arg6, %r12
+ movdqu (%r12), %xmm13 # %xmm13 = HashKey
+ movdqa SHUF_MASK(%rip), %xmm2
+ PSHUFB_XMM %xmm2, %xmm13
+
+
+# Precompute HashKey<<1 (mod poly) from the hash key (required for GHASH)
+
+ movdqa %xmm13, %xmm2
+ psllq $1, %xmm13
+ psrlq $63, %xmm2
+ movdqa %xmm2, %xmm1
+ pslldq $8, %xmm2
+ psrldq $8, %xmm1
+ por %xmm2, %xmm13
+
+ # Reduction
+
+ pshufd $0x24, %xmm1, %xmm2
+ pcmpeqd TWOONE(%rip), %xmm2
+ pand POLY(%rip), %xmm2
+ pxor %xmm2, %xmm13 # %xmm13 holds the HashKey<<1 (mod poly)
+
+
+ # Decrypt first few blocks
+
+ movdqa %xmm13, HashKey(%rsp) # store HashKey<<1 (mod poly)
+ mov %arg4, %r13 # save the number of bytes of plaintext/ciphertext
+ and $-16, %r13 # %r13 = %r13 - (%r13 mod 16)
+ mov %r13, %r12
+ and $(3<<4), %r12
+ jz _initial_num_blocks_is_0_decrypt
+ cmp $(2<<4), %r12
+ jb _initial_num_blocks_is_1_decrypt
+ je _initial_num_blocks_is_2_decrypt
+_initial_num_blocks_is_3_decrypt:
+ INITIAL_BLOCKS_DEC 3, %xmm9, %xmm10, %xmm13, %xmm11, %xmm12, %xmm0, \
+%xmm1, %xmm2, %xmm3, %xmm4, %xmm8, %xmm5, %xmm6, 5, 678, dec
+ sub $48, %r13
+ jmp _initial_blocks_decrypted
+_initial_num_blocks_is_2_decrypt:
+ INITIAL_BLOCKS_DEC 2, %xmm9, %xmm10, %xmm13, %xmm11, %xmm12, %xmm0, \
+%xmm1, %xmm2, %xmm3, %xmm4, %xmm8, %xmm5, %xmm6, 6, 78, dec
+ sub $32, %r13
+ jmp _initial_blocks_decrypted
+_initial_num_blocks_is_1_decrypt:
+ INITIAL_BLOCKS_DEC 1, %xmm9, %xmm10, %xmm13, %xmm11, %xmm12, %xmm0, \
+%xmm1, %xmm2, %xmm3, %xmm4, %xmm8, %xmm5, %xmm6, 7, 8, dec
+ sub $16, %r13
+ jmp _initial_blocks_decrypted
+_initial_num_blocks_is_0_decrypt:
+ INITIAL_BLOCKS_DEC 0, %xmm9, %xmm10, %xmm13, %xmm11, %xmm12, %xmm0, \
+%xmm1, %xmm2, %xmm3, %xmm4, %xmm8, %xmm5, %xmm6, 8, 0, dec
+_initial_blocks_decrypted:
+ cmp $0, %r13
+ je _zero_cipher_left_decrypt
+ sub $64, %r13
+ je _four_cipher_left_decrypt
+_decrypt_by_4:
+ GHASH_4_ENCRYPT_4_PARALLEL_DEC %xmm9, %xmm10, %xmm11, %xmm12, %xmm13, \
+%xmm14, %xmm0, %xmm1, %xmm2, %xmm3, %xmm4, %xmm5, %xmm6, %xmm7, %xmm8, dec
+ add $64, %r11
+ sub $64, %r13
+ jne _decrypt_by_4
+_four_cipher_left_decrypt:
+ GHASH_LAST_4 %xmm9, %xmm10, %xmm11, %xmm12, %xmm13, %xmm14, \
+%xmm15, %xmm1, %xmm2, %xmm3, %xmm4, %xmm8
+_zero_cipher_left_decrypt:
+ mov %arg4, %r13
+ and $15, %r13 # %r13 = arg4 (mod 16)
+ je _multiple_of_16_bytes_decrypt
+
+ # Handle the last <16 byte block seperately
+
+ paddd ONE(%rip), %xmm0 # increment CNT to get Yn
+ movdqa SHUF_MASK(%rip), %xmm10
+ PSHUFB_XMM %xmm10, %xmm0
+
+ ENCRYPT_SINGLE_BLOCK %xmm0, %xmm1 # E(K, Yn)
+ sub $16, %r11
+ add %r13, %r11
+ movdqu (%arg3,%r11,1), %xmm1 # recieve the last <16 byte block
+ lea SHIFT_MASK+16(%rip), %r12
+ sub %r13, %r12
+# adjust the shuffle mask pointer to be able to shift 16-%r13 bytes
+# (%r13 is the number of bytes in plaintext mod 16)
+ movdqu (%r12), %xmm2 # get the appropriate shuffle mask
+ PSHUFB_XMM %xmm2, %xmm1 # right shift 16-%r13 butes
+
+ movdqa %xmm1, %xmm2
+ pxor %xmm1, %xmm0 # Ciphertext XOR E(K, Yn)
+ movdqu ALL_F-SHIFT_MASK(%r12), %xmm1
+ # get the appropriate mask to mask out top 16-%r13 bytes of %xmm0
+ pand %xmm1, %xmm0 # mask out top 16-%r13 bytes of %xmm0
+ pand %xmm1, %xmm2
+ movdqa SHUF_MASK(%rip), %xmm10
+ PSHUFB_XMM %xmm10 ,%xmm2
+
+ pxor %xmm2, %xmm8
+ GHASH_MUL %xmm8, %xmm13, %xmm9, %xmm10, %xmm11, %xmm5, %xmm6
+ # GHASH computation for the last <16 byte block
+ sub %r13, %r11
+ add $16, %r11
+
+ # output %r13 bytes
+ MOVQ_R64_XMM %xmm0, %rax
+ cmp $8, %r13
+ jle _less_than_8_bytes_left_decrypt
+ mov %rax, (%arg2 , %r11, 1)
+ add $8, %r11
+ psrldq $8, %xmm0
+ MOVQ_R64_XMM %xmm0, %rax
+ sub $8, %r13
+_less_than_8_bytes_left_decrypt:
+ mov %al, (%arg2, %r11, 1)
+ add $1, %r11
+ shr $8, %rax
+ sub $1, %r13
+ jne _less_than_8_bytes_left_decrypt
+_multiple_of_16_bytes_decrypt:
+ mov arg8, %r12 # %r13 = aadLen (number of bytes)
+ shl $3, %r12 # convert into number of bits
+ movd %r12d, %xmm15 # len(A) in %xmm15
+ shl $3, %arg4 # len(C) in bits (*128)
+ MOVQ_R64_XMM %arg4, %xmm1
+ pslldq $8, %xmm15 # %xmm15 = len(A)||0x0000000000000000
+ pxor %xmm1, %xmm15 # %xmm15 = len(A)||len(C)
+ pxor %xmm15, %xmm8
+ GHASH_MUL %xmm8, %xmm13, %xmm9, %xmm10, %xmm11, %xmm5, %xmm6
+ # final GHASH computation
+ movdqa SHUF_MASK(%rip), %xmm10
+ PSHUFB_XMM %xmm10, %xmm8
+
+ mov %arg5, %rax # %rax = *Y0
+ movdqu (%rax), %xmm0 # %xmm0 = Y0
+ ENCRYPT_SINGLE_BLOCK %xmm0, %xmm1 # E(K, Y0)
+ pxor %xmm8, %xmm0
+_return_T_decrypt:
+ mov arg9, %r10 # %r10 = authTag
+ mov arg10, %r11 # %r11 = auth_tag_len
+ cmp $16, %r11
+ je _T_16_decrypt
+ cmp $12, %r11
+ je _T_12_decrypt
+_T_8_decrypt:
+ MOVQ_R64_XMM %xmm0, %rax
+ mov %rax, (%r10)
+ jmp _return_T_done_decrypt
+_T_12_decrypt:
+ MOVQ_R64_XMM %xmm0, %rax
+ mov %rax, (%r10)
+ psrldq $8, %xmm0
+ movd %xmm0, %eax
+ mov %eax, 8(%r10)
+ jmp _return_T_done_decrypt
+_T_16_decrypt:
+ movdqu %xmm0, (%r10)
+_return_T_done_decrypt:
+ mov %r14, %rsp
+ pop %r14
+ pop %r13
+ pop %r12
+ ret
+
+
+/*****************************************************************************
+* void aesni_gcm_enc(void *aes_ctx, // AES Key schedule. Starts on a 16 byte boundary.
+* u8 *out, // Ciphertext output. Encrypt in-place is allowed.
+* const u8 *in, // Plaintext input
+* u64 plaintext_len, // Length of data in bytes for encryption.
+* u8 *iv, // Pre-counter block j0: 4 byte salt (from Security Association)
+* // concatenated with 8 byte Initialisation Vector (from IPSec ESP Payload)
+* // concatenated with 0x00000001. 16-byte aligned pointer.
+* u8 *hash_subkey, // H, the Hash sub key input. Data starts on a 16-byte boundary.
+* const u8 *aad, // Additional Authentication Data (AAD)
+* u64 aad_len, // Length of AAD in bytes. With RFC4106 this is going to be 8 or 12 bytes
+* u8 *auth_tag, // Authenticated Tag output.
+* u64 auth_tag_len); // Authenticated Tag Length in bytes. Valid values are 16 (most likely),
+* // 12 or 8.
+*
+* Assumptions:
+*
+* keys:
+* keys are pre-expanded and aligned to 16 bytes. we are using the
+* first set of 11 keys in the data structure void *aes_ctx
+*
+*
+* iv:
+* 0 1 2 3
+* 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+* | Salt (From the SA) |
+* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+* | Initialization Vector |
+* | (This is the sequence number from IPSec header) |
+* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+* | 0x1 |
+* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+*
+*
+*
+* AAD:
+* AAD padded to 128 bits with 0
+* for example, assume AAD is a u32 vector
+*
+* if AAD is 8 bytes:
+* AAD[3] = {A0, A1};
+* padded AAD in xmm register = {A1 A0 0 0}
+*
+* 0 1 2 3
+* 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+* | SPI (A1) |
+* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+* | 32-bit Sequence Number (A0) |
+* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+* | 0x0 |
+* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+*
+* AAD Format with 32-bit Sequence Number
+*
+* if AAD is 12 bytes:
+* AAD[3] = {A0, A1, A2};
+* padded AAD in xmm register = {A2 A1 A0 0}
+*
+* 0 1 2 3
+* 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+* | SPI (A2) |
+* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+* | 64-bit Extended Sequence Number {A1,A0} |
+* | |
+* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+* | 0x0 |
+* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+*
+* AAD Format with 64-bit Extended Sequence Number
+*
+* aadLen:
+* from the definition of the spec, aadLen can only be 8 or 12 bytes.
+* The code supports 16 too but for other sizes, the code will fail.
+*
+* TLen:
+* from the definition of the spec, TLen can only be 8, 12 or 16 bytes.
+* For other sizes, the code will fail.
+*
+* poly = x^128 + x^127 + x^126 + x^121 + 1
+***************************************************************************/
+ENTRY(aesni_gcm_enc)
+ push %r12
+ push %r13
+ push %r14
+ mov %rsp, %r14
+#
+# states of %xmm registers %xmm6:%xmm15 not saved
+# all %xmm registers are clobbered
+#
+ sub $VARIABLE_OFFSET, %rsp
+ and $~63, %rsp
+ mov %arg6, %r12
+ movdqu (%r12), %xmm13
+ movdqa SHUF_MASK(%rip), %xmm2
+ PSHUFB_XMM %xmm2, %xmm13
+
+
+# precompute HashKey<<1 mod poly from the HashKey (required for GHASH)
+
+ movdqa %xmm13, %xmm2
+ psllq $1, %xmm13
+ psrlq $63, %xmm2
+ movdqa %xmm2, %xmm1
+ pslldq $8, %xmm2
+ psrldq $8, %xmm1
+ por %xmm2, %xmm13
+
+ # reduce HashKey<<1
+
+ pshufd $0x24, %xmm1, %xmm2
+ pcmpeqd TWOONE(%rip), %xmm2
+ pand POLY(%rip), %xmm2
+ pxor %xmm2, %xmm13
+ movdqa %xmm13, HashKey(%rsp)
+ mov %arg4, %r13 # %xmm13 holds HashKey<<1 (mod poly)
+ and $-16, %r13
+ mov %r13, %r12
+
+ # Encrypt first few blocks
+
+ and $(3<<4), %r12
+ jz _initial_num_blocks_is_0_encrypt
+ cmp $(2<<4), %r12
+ jb _initial_num_blocks_is_1_encrypt
+ je _initial_num_blocks_is_2_encrypt
+_initial_num_blocks_is_3_encrypt:
+ INITIAL_BLOCKS_ENC 3, %xmm9, %xmm10, %xmm13, %xmm11, %xmm12, %xmm0, \
+%xmm1, %xmm2, %xmm3, %xmm4, %xmm8, %xmm5, %xmm6, 5, 678, enc
+ sub $48, %r13
+ jmp _initial_blocks_encrypted
+_initial_num_blocks_is_2_encrypt:
+ INITIAL_BLOCKS_ENC 2, %xmm9, %xmm10, %xmm13, %xmm11, %xmm12, %xmm0, \
+%xmm1, %xmm2, %xmm3, %xmm4, %xmm8, %xmm5, %xmm6, 6, 78, enc
+ sub $32, %r13
+ jmp _initial_blocks_encrypted
+_initial_num_blocks_is_1_encrypt:
+ INITIAL_BLOCKS_ENC 1, %xmm9, %xmm10, %xmm13, %xmm11, %xmm12, %xmm0, \
+%xmm1, %xmm2, %xmm3, %xmm4, %xmm8, %xmm5, %xmm6, 7, 8, enc
+ sub $16, %r13
+ jmp _initial_blocks_encrypted
+_initial_num_blocks_is_0_encrypt:
+ INITIAL_BLOCKS_ENC 0, %xmm9, %xmm10, %xmm13, %xmm11, %xmm12, %xmm0, \
+%xmm1, %xmm2, %xmm3, %xmm4, %xmm8, %xmm5, %xmm6, 8, 0, enc
+_initial_blocks_encrypted:
+
+ # Main loop - Encrypt remaining blocks
+
+ cmp $0, %r13
+ je _zero_cipher_left_encrypt
+ sub $64, %r13
+ je _four_cipher_left_encrypt
+_encrypt_by_4_encrypt:
+ GHASH_4_ENCRYPT_4_PARALLEL_ENC %xmm9, %xmm10, %xmm11, %xmm12, %xmm13, \
+%xmm14, %xmm0, %xmm1, %xmm2, %xmm3, %xmm4, %xmm5, %xmm6, %xmm7, %xmm8, enc
+ add $64, %r11
+ sub $64, %r13
+ jne _encrypt_by_4_encrypt
+_four_cipher_left_encrypt:
+ GHASH_LAST_4 %xmm9, %xmm10, %xmm11, %xmm12, %xmm13, %xmm14, \
+%xmm15, %xmm1, %xmm2, %xmm3, %xmm4, %xmm8
+_zero_cipher_left_encrypt:
+ mov %arg4, %r13
+ and $15, %r13 # %r13 = arg4 (mod 16)
+ je _multiple_of_16_bytes_encrypt
+
+ # Handle the last <16 Byte block seperately
+ paddd ONE(%rip), %xmm0 # INCR CNT to get Yn
+ movdqa SHUF_MASK(%rip), %xmm10
+ PSHUFB_XMM %xmm10, %xmm0
+
+ ENCRYPT_SINGLE_BLOCK %xmm0, %xmm1 # Encrypt(K, Yn)
+ sub $16, %r11
+ add %r13, %r11
+ movdqu (%arg3,%r11,1), %xmm1 # receive the last <16 byte blocks
+ lea SHIFT_MASK+16(%rip), %r12
+ sub %r13, %r12
+ # adjust the shuffle mask pointer to be able to shift 16-r13 bytes
+ # (%r13 is the number of bytes in plaintext mod 16)
+ movdqu (%r12), %xmm2 # get the appropriate shuffle mask
+ PSHUFB_XMM %xmm2, %xmm1 # shift right 16-r13 byte
+ pxor %xmm1, %xmm0 # Plaintext XOR Encrypt(K, Yn)
+ movdqu ALL_F-SHIFT_MASK(%r12), %xmm1
+ # get the appropriate mask to mask out top 16-r13 bytes of xmm0
+ pand %xmm1, %xmm0 # mask out top 16-r13 bytes of xmm0
+ movdqa SHUF_MASK(%rip), %xmm10
+ PSHUFB_XMM %xmm10,%xmm0
+
+ pxor %xmm0, %xmm8
+ GHASH_MUL %xmm8, %xmm13, %xmm9, %xmm10, %xmm11, %xmm5, %xmm6
+ # GHASH computation for the last <16 byte block
+ sub %r13, %r11
+ add $16, %r11
+ PSHUFB_XMM %xmm10, %xmm1
+
+ # shuffle xmm0 back to output as ciphertext
+
+ # Output %r13 bytes
+ MOVQ_R64_XMM %xmm0, %rax
+ cmp $8, %r13
+ jle _less_than_8_bytes_left_encrypt
+ mov %rax, (%arg2 , %r11, 1)
+ add $8, %r11
+ psrldq $8, %xmm0
+ MOVQ_R64_XMM %xmm0, %rax
+ sub $8, %r13
+_less_than_8_bytes_left_encrypt:
+ mov %al, (%arg2, %r11, 1)
+ add $1, %r11
+ shr $8, %rax
+ sub $1, %r13
+ jne _less_than_8_bytes_left_encrypt
+_multiple_of_16_bytes_encrypt:
+ mov arg8, %r12 # %r12 = addLen (number of bytes)
+ shl $3, %r12
+ movd %r12d, %xmm15 # len(A) in %xmm15
+ shl $3, %arg4 # len(C) in bits (*128)
+ MOVQ_R64_XMM %arg4, %xmm1
+ pslldq $8, %xmm15 # %xmm15 = len(A)||0x0000000000000000
+ pxor %xmm1, %xmm15 # %xmm15 = len(A)||len(C)
+ pxor %xmm15, %xmm8
+ GHASH_MUL %xmm8, %xmm13, %xmm9, %xmm10, %xmm11, %xmm5, %xmm6
+ # final GHASH computation
+ movdqa SHUF_MASK(%rip), %xmm10
+ PSHUFB_XMM %xmm10, %xmm8 # perform a 16 byte swap
+
+ mov %arg5, %rax # %rax = *Y0
+ movdqu (%rax), %xmm0 # %xmm0 = Y0
+ ENCRYPT_SINGLE_BLOCK %xmm0, %xmm15 # Encrypt(K, Y0)
+ pxor %xmm8, %xmm0
+_return_T_encrypt:
+ mov arg9, %r10 # %r10 = authTag
+ mov arg10, %r11 # %r11 = auth_tag_len
+ cmp $16, %r11
+ je _T_16_encrypt
+ cmp $12, %r11
+ je _T_12_encrypt
+_T_8_encrypt:
+ MOVQ_R64_XMM %xmm0, %rax
+ mov %rax, (%r10)
+ jmp _return_T_done_encrypt
+_T_12_encrypt:
+ MOVQ_R64_XMM %xmm0, %rax
+ mov %rax, (%r10)
+ psrldq $8, %xmm0
+ movd %xmm0, %eax
+ mov %eax, 8(%r10)
+ jmp _return_T_done_encrypt
+_T_16_encrypt:
+ movdqu %xmm0, (%r10)
+_return_T_done_encrypt:
+ mov %r14, %rsp
+ pop %r14
+ pop %r13
+ pop %r12
+ ret
+
+#endif
+
_key_expansion_128:
_key_expansion_256a:
@@ -55,10 +1709,11 @@
shufps $0b10001100, %xmm0, %xmm4
pxor %xmm4, %xmm0
pxor %xmm1, %xmm0
- movaps %xmm0, (%rcx)
- add $0x10, %rcx
+ movaps %xmm0, (TKEYP)
+ add $0x10, TKEYP
ret
+.align 4
_key_expansion_192a:
pshufd $0b01010101, %xmm1, %xmm1
shufps $0b00010000, %xmm0, %xmm4
@@ -76,12 +1731,13 @@
movaps %xmm0, %xmm1
shufps $0b01000100, %xmm0, %xmm6
- movaps %xmm6, (%rcx)
+ movaps %xmm6, (TKEYP)
shufps $0b01001110, %xmm2, %xmm1
- movaps %xmm1, 16(%rcx)
- add $0x20, %rcx
+ movaps %xmm1, 0x10(TKEYP)
+ add $0x20, TKEYP
ret
+.align 4
_key_expansion_192b:
pshufd $0b01010101, %xmm1, %xmm1
shufps $0b00010000, %xmm0, %xmm4
@@ -96,10 +1752,11 @@
pxor %xmm3, %xmm2
pxor %xmm5, %xmm2
- movaps %xmm0, (%rcx)
- add $0x10, %rcx
+ movaps %xmm0, (TKEYP)
+ add $0x10, TKEYP
ret
+.align 4
_key_expansion_256b:
pshufd $0b10101010, %xmm1, %xmm1
shufps $0b00010000, %xmm2, %xmm4
@@ -107,8 +1764,8 @@
shufps $0b10001100, %xmm2, %xmm4
pxor %xmm4, %xmm2
pxor %xmm1, %xmm2
- movaps %xmm2, (%rcx)
- add $0x10, %rcx
+ movaps %xmm2, (TKEYP)
+ add $0x10, TKEYP
ret
/*
@@ -116,17 +1773,23 @@
* unsigned int key_len)
*/
ENTRY(aesni_set_key)
- movups (%rsi), %xmm0 # user key (first 16 bytes)
- movaps %xmm0, (%rdi)
- lea 0x10(%rdi), %rcx # key addr
- movl %edx, 480(%rdi)
+#ifndef __x86_64__
+ pushl KEYP
+ movl 8(%esp), KEYP # ctx
+ movl 12(%esp), UKEYP # in_key
+ movl 16(%esp), %edx # key_len
+#endif
+ movups (UKEYP), %xmm0 # user key (first 16 bytes)
+ movaps %xmm0, (KEYP)
+ lea 0x10(KEYP), TKEYP # key addr
+ movl %edx, 480(KEYP)
pxor %xmm4, %xmm4 # xmm4 is assumed 0 in _key_expansion_x
cmp $24, %dl
jb .Lenc_key128
je .Lenc_key192
- movups 0x10(%rsi), %xmm2 # other user key
- movaps %xmm2, (%rcx)
- add $0x10, %rcx
+ movups 0x10(UKEYP), %xmm2 # other user key
+ movaps %xmm2, (TKEYP)
+ add $0x10, TKEYP
AESKEYGENASSIST 0x1 %xmm2 %xmm1 # round 1
call _key_expansion_256a
AESKEYGENASSIST 0x1 %xmm0 %xmm1
@@ -155,7 +1818,7 @@
call _key_expansion_256a
jmp .Ldec_key
.Lenc_key192:
- movq 0x10(%rsi), %xmm2 # other user key
+ movq 0x10(UKEYP), %xmm2 # other user key
AESKEYGENASSIST 0x1 %xmm2 %xmm1 # round 1
call _key_expansion_192a
AESKEYGENASSIST 0x2 %xmm2 %xmm1 # round 2
@@ -195,33 +1858,47 @@
AESKEYGENASSIST 0x36 %xmm0 %xmm1 # round 10
call _key_expansion_128
.Ldec_key:
- sub $0x10, %rcx
- movaps (%rdi), %xmm0
- movaps (%rcx), %xmm1
- movaps %xmm0, 240(%rcx)
- movaps %xmm1, 240(%rdi)
- add $0x10, %rdi
- lea 240-16(%rcx), %rsi
+ sub $0x10, TKEYP
+ movaps (KEYP), %xmm0
+ movaps (TKEYP), %xmm1
+ movaps %xmm0, 240(TKEYP)
+ movaps %xmm1, 240(KEYP)
+ add $0x10, KEYP
+ lea 240-16(TKEYP), UKEYP
.align 4
.Ldec_key_loop:
- movaps (%rdi), %xmm0
+ movaps (KEYP), %xmm0
AESIMC %xmm0 %xmm1
- movaps %xmm1, (%rsi)
- add $0x10, %rdi
- sub $0x10, %rsi
- cmp %rcx, %rdi
+ movaps %xmm1, (UKEYP)
+ add $0x10, KEYP
+ sub $0x10, UKEYP
+ cmp TKEYP, KEYP
jb .Ldec_key_loop
- xor %rax, %rax
+ xor AREG, AREG
+#ifndef __x86_64__
+ popl KEYP
+#endif
ret
/*
* void aesni_enc(struct crypto_aes_ctx *ctx, u8 *dst, const u8 *src)
*/
ENTRY(aesni_enc)
+#ifndef __x86_64__
+ pushl KEYP
+ pushl KLEN
+ movl 12(%esp), KEYP
+ movl 16(%esp), OUTP
+ movl 20(%esp), INP
+#endif
movl 480(KEYP), KLEN # key length
movups (INP), STATE # input
call _aesni_enc1
movups STATE, (OUTP) # output
+#ifndef __x86_64__
+ popl KLEN
+ popl KEYP
+#endif
ret
/*
@@ -236,6 +1913,7 @@
* KEY
* TKEYP (T1)
*/
+.align 4
_aesni_enc1:
movaps (KEYP), KEY # key
mov KEYP, TKEYP
@@ -298,6 +1976,7 @@
* KEY
* TKEYP (T1)
*/
+.align 4
_aesni_enc4:
movaps (KEYP), KEY # key
mov KEYP, TKEYP
@@ -391,11 +2070,22 @@
* void aesni_dec (struct crypto_aes_ctx *ctx, u8 *dst, const u8 *src)
*/
ENTRY(aesni_dec)
+#ifndef __x86_64__
+ pushl KEYP
+ pushl KLEN
+ movl 12(%esp), KEYP
+ movl 16(%esp), OUTP
+ movl 20(%esp), INP
+#endif
mov 480(KEYP), KLEN # key length
add $240, KEYP
movups (INP), STATE # input
call _aesni_dec1
movups STATE, (OUTP) #output
+#ifndef __x86_64__
+ popl KLEN
+ popl KEYP
+#endif
ret
/*
@@ -410,6 +2100,7 @@
* KEY
* TKEYP (T1)
*/
+.align 4
_aesni_dec1:
movaps (KEYP), KEY # key
mov KEYP, TKEYP
@@ -472,6 +2163,7 @@
* KEY
* TKEYP (T1)
*/
+.align 4
_aesni_dec4:
movaps (KEYP), KEY # key
mov KEYP, TKEYP
@@ -566,6 +2258,15 @@
* size_t len)
*/
ENTRY(aesni_ecb_enc)
+#ifndef __x86_64__
+ pushl LEN
+ pushl KEYP
+ pushl KLEN
+ movl 16(%esp), KEYP
+ movl 20(%esp), OUTP
+ movl 24(%esp), INP
+ movl 28(%esp), LEN
+#endif
test LEN, LEN # check length
jz .Lecb_enc_ret
mov 480(KEYP), KLEN
@@ -602,6 +2303,11 @@
cmp $16, LEN
jge .Lecb_enc_loop1
.Lecb_enc_ret:
+#ifndef __x86_64__
+ popl KLEN
+ popl KEYP
+ popl LEN
+#endif
ret
/*
@@ -609,6 +2315,15 @@
* size_t len);
*/
ENTRY(aesni_ecb_dec)
+#ifndef __x86_64__
+ pushl LEN
+ pushl KEYP
+ pushl KLEN
+ movl 16(%esp), KEYP
+ movl 20(%esp), OUTP
+ movl 24(%esp), INP
+ movl 28(%esp), LEN
+#endif
test LEN, LEN
jz .Lecb_dec_ret
mov 480(KEYP), KLEN
@@ -646,6 +2361,11 @@
cmp $16, LEN
jge .Lecb_dec_loop1
.Lecb_dec_ret:
+#ifndef __x86_64__
+ popl KLEN
+ popl KEYP
+ popl LEN
+#endif
ret
/*
@@ -653,6 +2373,17 @@
* size_t len, u8 *iv)
*/
ENTRY(aesni_cbc_enc)
+#ifndef __x86_64__
+ pushl IVP
+ pushl LEN
+ pushl KEYP
+ pushl KLEN
+ movl 20(%esp), KEYP
+ movl 24(%esp), OUTP
+ movl 28(%esp), INP
+ movl 32(%esp), LEN
+ movl 36(%esp), IVP
+#endif
cmp $16, LEN
jb .Lcbc_enc_ret
mov 480(KEYP), KLEN
@@ -670,6 +2401,12 @@
jge .Lcbc_enc_loop
movups STATE, (IVP)
.Lcbc_enc_ret:
+#ifndef __x86_64__
+ popl KLEN
+ popl KEYP
+ popl LEN
+ popl IVP
+#endif
ret
/*
@@ -677,6 +2414,17 @@
* size_t len, u8 *iv)
*/
ENTRY(aesni_cbc_dec)
+#ifndef __x86_64__
+ pushl IVP
+ pushl LEN
+ pushl KEYP
+ pushl KLEN
+ movl 20(%esp), KEYP
+ movl 24(%esp), OUTP
+ movl 28(%esp), INP
+ movl 32(%esp), LEN
+ movl 36(%esp), IVP
+#endif
cmp $16, LEN
jb .Lcbc_dec_just_ret
mov 480(KEYP), KLEN
@@ -690,16 +2438,30 @@
movaps IN1, STATE1
movups 0x10(INP), IN2
movaps IN2, STATE2
+#ifdef __x86_64__
movups 0x20(INP), IN3
movaps IN3, STATE3
movups 0x30(INP), IN4
movaps IN4, STATE4
+#else
+ movups 0x20(INP), IN1
+ movaps IN1, STATE3
+ movups 0x30(INP), IN2
+ movaps IN2, STATE4
+#endif
call _aesni_dec4
pxor IV, STATE1
+#ifdef __x86_64__
pxor IN1, STATE2
pxor IN2, STATE3
pxor IN3, STATE4
movaps IN4, IV
+#else
+ pxor (INP), STATE2
+ pxor 0x10(INP), STATE3
+ pxor IN1, STATE4
+ movaps IN2, IV
+#endif
movups STATE1, (OUTP)
movups STATE2, 0x10(OUTP)
movups STATE3, 0x20(OUTP)
@@ -727,8 +2489,15 @@
.Lcbc_dec_ret:
movups IV, (IVP)
.Lcbc_dec_just_ret:
+#ifndef __x86_64__
+ popl KLEN
+ popl KEYP
+ popl LEN
+ popl IVP
+#endif
ret
+#ifdef __x86_64__
.align 16
.Lbswap_mask:
.byte 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0
@@ -744,6 +2513,7 @@
* INC: == 1, in little endian
* BSWAP_MASK == endian swapping mask
*/
+.align 4
_aesni_inc_init:
movaps .Lbswap_mask, BSWAP_MASK
movaps IV, CTR
@@ -768,6 +2538,7 @@
* CTR: == output IV, in little endian
* TCTR_LOW: == lower qword of CTR
*/
+.align 4
_aesni_inc:
paddq INC, CTR
add $1, TCTR_LOW
@@ -839,3 +2610,4 @@
movups IV, (IVP)
.Lctr_enc_just_ret:
ret
+#endif
diff --git a/arch/x86/crypto/aesni-intel_glue.c b/arch/x86/crypto/aesni-intel_glue.c
index 2cb3dcc..e1e60c7 100644
--- a/arch/x86/crypto/aesni-intel_glue.c
+++ b/arch/x86/crypto/aesni-intel_glue.c
@@ -5,6 +5,14 @@
* Copyright (C) 2008, Intel Corp.
* Author: Huang Ying <ying.huang@intel.com>
*
+ * Added RFC4106 AES-GCM support for 128-bit keys under the AEAD
+ * interface for 64-bit kernels.
+ * Authors: Adrian Hoban <adrian.hoban@intel.com>
+ * Gabriele Paoloni <gabriele.paoloni@intel.com>
+ * Tadeusz Struk (tadeusz.struk@intel.com)
+ * Aidan O'Mahony (aidan.o.mahony@intel.com)
+ * Copyright (c) 2010, Intel Corporation.
+ *
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
@@ -21,6 +29,10 @@
#include <crypto/ctr.h>
#include <asm/i387.h>
#include <asm/aes.h>
+#include <crypto/scatterwalk.h>
+#include <crypto/internal/aead.h>
+#include <linux/workqueue.h>
+#include <linux/spinlock.h>
#if defined(CONFIG_CRYPTO_CTR) || defined(CONFIG_CRYPTO_CTR_MODULE)
#define HAS_CTR
@@ -42,8 +54,31 @@
struct cryptd_ablkcipher *cryptd_tfm;
};
-#define AESNI_ALIGN 16
+/* This data is stored at the end of the crypto_tfm struct.
+ * It's a type of per "session" data storage location.
+ * This needs to be 16 byte aligned.
+ */
+struct aesni_rfc4106_gcm_ctx {
+ u8 hash_subkey[16];
+ struct crypto_aes_ctx aes_key_expanded;
+ u8 nonce[4];
+ struct cryptd_aead *cryptd_tfm;
+};
+
+struct aesni_gcm_set_hash_subkey_result {
+ int err;
+ struct completion completion;
+};
+
+struct aesni_hash_subkey_req_data {
+ u8 iv[16];
+ struct aesni_gcm_set_hash_subkey_result result;
+ struct scatterlist sg;
+};
+
+#define AESNI_ALIGN (16)
#define AES_BLOCK_MASK (~(AES_BLOCK_SIZE-1))
+#define RFC4106_HASH_SUBKEY_SIZE 16
asmlinkage int aesni_set_key(struct crypto_aes_ctx *ctx, const u8 *in_key,
unsigned int key_len);
@@ -59,9 +94,62 @@
const u8 *in, unsigned int len, u8 *iv);
asmlinkage void aesni_cbc_dec(struct crypto_aes_ctx *ctx, u8 *out,
const u8 *in, unsigned int len, u8 *iv);
+#ifdef CONFIG_X86_64
asmlinkage void aesni_ctr_enc(struct crypto_aes_ctx *ctx, u8 *out,
const u8 *in, unsigned int len, u8 *iv);
+/* asmlinkage void aesni_gcm_enc()
+ * void *ctx, AES Key schedule. Starts on a 16 byte boundary.
+ * u8 *out, Ciphertext output. Encrypt in-place is allowed.
+ * const u8 *in, Plaintext input
+ * unsigned long plaintext_len, Length of data in bytes for encryption.
+ * u8 *iv, Pre-counter block j0: 4 byte salt (from Security Association)
+ * concatenated with 8 byte Initialisation Vector (from IPSec ESP
+ * Payload) concatenated with 0x00000001. 16-byte aligned pointer.
+ * u8 *hash_subkey, the Hash sub key input. Data starts on a 16-byte boundary.
+ * const u8 *aad, Additional Authentication Data (AAD)
+ * unsigned long aad_len, Length of AAD in bytes. With RFC4106 this
+ * is going to be 8 or 12 bytes
+ * u8 *auth_tag, Authenticated Tag output.
+ * unsigned long auth_tag_len), Authenticated Tag Length in bytes.
+ * Valid values are 16 (most likely), 12 or 8.
+ */
+asmlinkage void aesni_gcm_enc(void *ctx, u8 *out,
+ const u8 *in, unsigned long plaintext_len, u8 *iv,
+ u8 *hash_subkey, const u8 *aad, unsigned long aad_len,
+ u8 *auth_tag, unsigned long auth_tag_len);
+
+/* asmlinkage void aesni_gcm_dec()
+ * void *ctx, AES Key schedule. Starts on a 16 byte boundary.
+ * u8 *out, Plaintext output. Decrypt in-place is allowed.
+ * const u8 *in, Ciphertext input
+ * unsigned long ciphertext_len, Length of data in bytes for decryption.
+ * u8 *iv, Pre-counter block j0: 4 byte salt (from Security Association)
+ * concatenated with 8 byte Initialisation Vector (from IPSec ESP
+ * Payload) concatenated with 0x00000001. 16-byte aligned pointer.
+ * u8 *hash_subkey, the Hash sub key input. Data starts on a 16-byte boundary.
+ * const u8 *aad, Additional Authentication Data (AAD)
+ * unsigned long aad_len, Length of AAD in bytes. With RFC4106 this is going
+ * to be 8 or 12 bytes
+ * u8 *auth_tag, Authenticated Tag output.
+ * unsigned long auth_tag_len) Authenticated Tag Length in bytes.
+ * Valid values are 16 (most likely), 12 or 8.
+ */
+asmlinkage void aesni_gcm_dec(void *ctx, u8 *out,
+ const u8 *in, unsigned long ciphertext_len, u8 *iv,
+ u8 *hash_subkey, const u8 *aad, unsigned long aad_len,
+ u8 *auth_tag, unsigned long auth_tag_len);
+
+static inline struct
+aesni_rfc4106_gcm_ctx *aesni_rfc4106_gcm_ctx_get(struct crypto_aead *tfm)
+{
+ return
+ (struct aesni_rfc4106_gcm_ctx *)
+ PTR_ALIGN((u8 *)
+ crypto_tfm_ctx(crypto_aead_tfm(tfm)), AESNI_ALIGN);
+}
+#endif
+
static inline struct crypto_aes_ctx *aes_ctx(void *raw_ctx)
{
unsigned long addr = (unsigned long)raw_ctx;
@@ -324,6 +412,7 @@
},
};
+#ifdef CONFIG_X86_64
static void ctr_crypt_final(struct crypto_aes_ctx *ctx,
struct blkcipher_walk *walk)
{
@@ -389,6 +478,7 @@
},
},
};
+#endif
static int ablk_set_key(struct crypto_ablkcipher *tfm, const u8 *key,
unsigned int key_len)
@@ -536,6 +626,7 @@
},
};
+#ifdef CONFIG_X86_64
static int ablk_ctr_init(struct crypto_tfm *tfm)
{
struct cryptd_ablkcipher *cryptd_tfm;
@@ -612,6 +703,7 @@
},
};
#endif
+#endif
#ifdef HAS_LRW
static int ablk_lrw_init(struct crypto_tfm *tfm)
@@ -730,6 +822,424 @@
};
#endif
+#ifdef CONFIG_X86_64
+static int rfc4106_init(struct crypto_tfm *tfm)
+{
+ struct cryptd_aead *cryptd_tfm;
+ struct aesni_rfc4106_gcm_ctx *ctx = (struct aesni_rfc4106_gcm_ctx *)
+ PTR_ALIGN((u8 *)crypto_tfm_ctx(tfm), AESNI_ALIGN);
+ cryptd_tfm = cryptd_alloc_aead("__driver-gcm-aes-aesni", 0, 0);
+ if (IS_ERR(cryptd_tfm))
+ return PTR_ERR(cryptd_tfm);
+ ctx->cryptd_tfm = cryptd_tfm;
+ tfm->crt_aead.reqsize = sizeof(struct aead_request)
+ + crypto_aead_reqsize(&cryptd_tfm->base);
+ return 0;
+}
+
+static void rfc4106_exit(struct crypto_tfm *tfm)
+{
+ struct aesni_rfc4106_gcm_ctx *ctx =
+ (struct aesni_rfc4106_gcm_ctx *)
+ PTR_ALIGN((u8 *)crypto_tfm_ctx(tfm), AESNI_ALIGN);
+ if (!IS_ERR(ctx->cryptd_tfm))
+ cryptd_free_aead(ctx->cryptd_tfm);
+ return;
+}
+
+static void
+rfc4106_set_hash_subkey_done(struct crypto_async_request *req, int err)
+{
+ struct aesni_gcm_set_hash_subkey_result *result = req->data;
+
+ if (err == -EINPROGRESS)
+ return;
+ result->err = err;
+ complete(&result->completion);
+}
+
+static int
+rfc4106_set_hash_subkey(u8 *hash_subkey, const u8 *key, unsigned int key_len)
+{
+ struct crypto_ablkcipher *ctr_tfm;
+ struct ablkcipher_request *req;
+ int ret = -EINVAL;
+ struct aesni_hash_subkey_req_data *req_data;
+
+ ctr_tfm = crypto_alloc_ablkcipher("ctr(aes)", 0, 0);
+ if (IS_ERR(ctr_tfm))
+ return PTR_ERR(ctr_tfm);
+
+ crypto_ablkcipher_clear_flags(ctr_tfm, ~0);
+
+ ret = crypto_ablkcipher_setkey(ctr_tfm, key, key_len);
+ if (ret) {
+ crypto_free_ablkcipher(ctr_tfm);
+ return ret;
+ }
+
+ req = ablkcipher_request_alloc(ctr_tfm, GFP_KERNEL);
+ if (!req) {
+ crypto_free_ablkcipher(ctr_tfm);
+ return -EINVAL;
+ }
+
+ req_data = kmalloc(sizeof(*req_data), GFP_KERNEL);
+ if (!req_data) {
+ crypto_free_ablkcipher(ctr_tfm);
+ return -ENOMEM;
+ }
+ memset(req_data->iv, 0, sizeof(req_data->iv));
+
+ /* Clear the data in the hash sub key container to zero.*/
+ /* We want to cipher all zeros to create the hash sub key. */
+ memset(hash_subkey, 0, RFC4106_HASH_SUBKEY_SIZE);
+
+ init_completion(&req_data->result.completion);
+ sg_init_one(&req_data->sg, hash_subkey, RFC4106_HASH_SUBKEY_SIZE);
+ ablkcipher_request_set_tfm(req, ctr_tfm);
+ ablkcipher_request_set_callback(req, CRYPTO_TFM_REQ_MAY_SLEEP |
+ CRYPTO_TFM_REQ_MAY_BACKLOG,
+ rfc4106_set_hash_subkey_done,
+ &req_data->result);
+
+ ablkcipher_request_set_crypt(req, &req_data->sg,
+ &req_data->sg, RFC4106_HASH_SUBKEY_SIZE, req_data->iv);
+
+ ret = crypto_ablkcipher_encrypt(req);
+ if (ret == -EINPROGRESS || ret == -EBUSY) {
+ ret = wait_for_completion_interruptible
+ (&req_data->result.completion);
+ if (!ret)
+ ret = req_data->result.err;
+ }
+ ablkcipher_request_free(req);
+ kfree(req_data);
+ crypto_free_ablkcipher(ctr_tfm);
+ return ret;
+}
+
+static int rfc4106_set_key(struct crypto_aead *parent, const u8 *key,
+ unsigned int key_len)
+{
+ int ret = 0;
+ struct crypto_tfm *tfm = crypto_aead_tfm(parent);
+ struct aesni_rfc4106_gcm_ctx *ctx = aesni_rfc4106_gcm_ctx_get(parent);
+ u8 *new_key_mem = NULL;
+
+ if (key_len < 4) {
+ crypto_tfm_set_flags(tfm, CRYPTO_TFM_RES_BAD_KEY_LEN);
+ return -EINVAL;
+ }
+ /*Account for 4 byte nonce at the end.*/
+ key_len -= 4;
+ if (key_len != AES_KEYSIZE_128) {
+ crypto_tfm_set_flags(tfm, CRYPTO_TFM_RES_BAD_KEY_LEN);
+ return -EINVAL;
+ }
+
+ memcpy(ctx->nonce, key + key_len, sizeof(ctx->nonce));
+ /*This must be on a 16 byte boundary!*/
+ if ((unsigned long)(&(ctx->aes_key_expanded.key_enc[0])) % AESNI_ALIGN)
+ return -EINVAL;
+
+ if ((unsigned long)key % AESNI_ALIGN) {
+ /*key is not aligned: use an auxuliar aligned pointer*/
+ new_key_mem = kmalloc(key_len+AESNI_ALIGN, GFP_KERNEL);
+ if (!new_key_mem)
+ return -ENOMEM;
+
+ new_key_mem = PTR_ALIGN(new_key_mem, AESNI_ALIGN);
+ memcpy(new_key_mem, key, key_len);
+ key = new_key_mem;
+ }
+
+ if (!irq_fpu_usable())
+ ret = crypto_aes_expand_key(&(ctx->aes_key_expanded),
+ key, key_len);
+ else {
+ kernel_fpu_begin();
+ ret = aesni_set_key(&(ctx->aes_key_expanded), key, key_len);
+ kernel_fpu_end();
+ }
+ /*This must be on a 16 byte boundary!*/
+ if ((unsigned long)(&(ctx->hash_subkey[0])) % AESNI_ALIGN) {
+ ret = -EINVAL;
+ goto exit;
+ }
+ ret = rfc4106_set_hash_subkey(ctx->hash_subkey, key, key_len);
+exit:
+ kfree(new_key_mem);
+ return ret;
+}
+
+/* This is the Integrity Check Value (aka the authentication tag length and can
+ * be 8, 12 or 16 bytes long. */
+static int rfc4106_set_authsize(struct crypto_aead *parent,
+ unsigned int authsize)
+{
+ struct aesni_rfc4106_gcm_ctx *ctx = aesni_rfc4106_gcm_ctx_get(parent);
+ struct crypto_aead *cryptd_child = cryptd_aead_child(ctx->cryptd_tfm);
+
+ switch (authsize) {
+ case 8:
+ case 12:
+ case 16:
+ break;
+ default:
+ return -EINVAL;
+ }
+ crypto_aead_crt(parent)->authsize = authsize;
+ crypto_aead_crt(cryptd_child)->authsize = authsize;
+ return 0;
+}
+
+static int rfc4106_encrypt(struct aead_request *req)
+{
+ int ret;
+ struct crypto_aead *tfm = crypto_aead_reqtfm(req);
+ struct aesni_rfc4106_gcm_ctx *ctx = aesni_rfc4106_gcm_ctx_get(tfm);
+ struct crypto_aead *cryptd_child = cryptd_aead_child(ctx->cryptd_tfm);
+
+ if (!irq_fpu_usable()) {
+ struct aead_request *cryptd_req =
+ (struct aead_request *) aead_request_ctx(req);
+ memcpy(cryptd_req, req, sizeof(*req));
+ aead_request_set_tfm(cryptd_req, &ctx->cryptd_tfm->base);
+ return crypto_aead_encrypt(cryptd_req);
+ } else {
+ kernel_fpu_begin();
+ ret = cryptd_child->base.crt_aead.encrypt(req);
+ kernel_fpu_end();
+ return ret;
+ }
+}
+
+static int rfc4106_decrypt(struct aead_request *req)
+{
+ int ret;
+ struct crypto_aead *tfm = crypto_aead_reqtfm(req);
+ struct aesni_rfc4106_gcm_ctx *ctx = aesni_rfc4106_gcm_ctx_get(tfm);
+ struct crypto_aead *cryptd_child = cryptd_aead_child(ctx->cryptd_tfm);
+
+ if (!irq_fpu_usable()) {
+ struct aead_request *cryptd_req =
+ (struct aead_request *) aead_request_ctx(req);
+ memcpy(cryptd_req, req, sizeof(*req));
+ aead_request_set_tfm(cryptd_req, &ctx->cryptd_tfm->base);
+ return crypto_aead_decrypt(cryptd_req);
+ } else {
+ kernel_fpu_begin();
+ ret = cryptd_child->base.crt_aead.decrypt(req);
+ kernel_fpu_end();
+ return ret;
+ }
+}
+
+static struct crypto_alg rfc4106_alg = {
+ .cra_name = "rfc4106(gcm(aes))",
+ .cra_driver_name = "rfc4106-gcm-aesni",
+ .cra_priority = 400,
+ .cra_flags = CRYPTO_ALG_TYPE_AEAD | CRYPTO_ALG_ASYNC,
+ .cra_blocksize = 1,
+ .cra_ctxsize = sizeof(struct aesni_rfc4106_gcm_ctx) + AESNI_ALIGN,
+ .cra_alignmask = 0,
+ .cra_type = &crypto_nivaead_type,
+ .cra_module = THIS_MODULE,
+ .cra_list = LIST_HEAD_INIT(rfc4106_alg.cra_list),
+ .cra_init = rfc4106_init,
+ .cra_exit = rfc4106_exit,
+ .cra_u = {
+ .aead = {
+ .setkey = rfc4106_set_key,
+ .setauthsize = rfc4106_set_authsize,
+ .encrypt = rfc4106_encrypt,
+ .decrypt = rfc4106_decrypt,
+ .geniv = "seqiv",
+ .ivsize = 8,
+ .maxauthsize = 16,
+ },
+ },
+};
+
+static int __driver_rfc4106_encrypt(struct aead_request *req)
+{
+ u8 one_entry_in_sg = 0;
+ u8 *src, *dst, *assoc;
+ __be32 counter = cpu_to_be32(1);
+ struct crypto_aead *tfm = crypto_aead_reqtfm(req);
+ struct aesni_rfc4106_gcm_ctx *ctx = aesni_rfc4106_gcm_ctx_get(tfm);
+ void *aes_ctx = &(ctx->aes_key_expanded);
+ unsigned long auth_tag_len = crypto_aead_authsize(tfm);
+ u8 iv_tab[16+AESNI_ALIGN];
+ u8* iv = (u8 *) PTR_ALIGN((u8 *)iv_tab, AESNI_ALIGN);
+ struct scatter_walk src_sg_walk;
+ struct scatter_walk assoc_sg_walk;
+ struct scatter_walk dst_sg_walk;
+ unsigned int i;
+
+ /* Assuming we are supporting rfc4106 64-bit extended */
+ /* sequence numbers We need to have the AAD length equal */
+ /* to 8 or 12 bytes */
+ if (unlikely(req->assoclen != 8 && req->assoclen != 12))
+ return -EINVAL;
+ /* IV below built */
+ for (i = 0; i < 4; i++)
+ *(iv+i) = ctx->nonce[i];
+ for (i = 0; i < 8; i++)
+ *(iv+4+i) = req->iv[i];
+ *((__be32 *)(iv+12)) = counter;
+
+ if ((sg_is_last(req->src)) && (sg_is_last(req->assoc))) {
+ one_entry_in_sg = 1;
+ scatterwalk_start(&src_sg_walk, req->src);
+ scatterwalk_start(&assoc_sg_walk, req->assoc);
+ src = scatterwalk_map(&src_sg_walk, 0);
+ assoc = scatterwalk_map(&assoc_sg_walk, 0);
+ dst = src;
+ if (unlikely(req->src != req->dst)) {
+ scatterwalk_start(&dst_sg_walk, req->dst);
+ dst = scatterwalk_map(&dst_sg_walk, 0);
+ }
+
+ } else {
+ /* Allocate memory for src, dst, assoc */
+ src = kmalloc(req->cryptlen + auth_tag_len + req->assoclen,
+ GFP_ATOMIC);
+ if (unlikely(!src))
+ return -ENOMEM;
+ assoc = (src + req->cryptlen + auth_tag_len);
+ scatterwalk_map_and_copy(src, req->src, 0, req->cryptlen, 0);
+ scatterwalk_map_and_copy(assoc, req->assoc, 0,
+ req->assoclen, 0);
+ dst = src;
+ }
+
+ aesni_gcm_enc(aes_ctx, dst, src, (unsigned long)req->cryptlen, iv,
+ ctx->hash_subkey, assoc, (unsigned long)req->assoclen, dst
+ + ((unsigned long)req->cryptlen), auth_tag_len);
+
+ /* The authTag (aka the Integrity Check Value) needs to be written
+ * back to the packet. */
+ if (one_entry_in_sg) {
+ if (unlikely(req->src != req->dst)) {
+ scatterwalk_unmap(dst, 0);
+ scatterwalk_done(&dst_sg_walk, 0, 0);
+ }
+ scatterwalk_unmap(src, 0);
+ scatterwalk_unmap(assoc, 0);
+ scatterwalk_done(&src_sg_walk, 0, 0);
+ scatterwalk_done(&assoc_sg_walk, 0, 0);
+ } else {
+ scatterwalk_map_and_copy(dst, req->dst, 0,
+ req->cryptlen + auth_tag_len, 1);
+ kfree(src);
+ }
+ return 0;
+}
+
+static int __driver_rfc4106_decrypt(struct aead_request *req)
+{
+ u8 one_entry_in_sg = 0;
+ u8 *src, *dst, *assoc;
+ unsigned long tempCipherLen = 0;
+ __be32 counter = cpu_to_be32(1);
+ int retval = 0;
+ struct crypto_aead *tfm = crypto_aead_reqtfm(req);
+ struct aesni_rfc4106_gcm_ctx *ctx = aesni_rfc4106_gcm_ctx_get(tfm);
+ void *aes_ctx = &(ctx->aes_key_expanded);
+ unsigned long auth_tag_len = crypto_aead_authsize(tfm);
+ u8 iv_and_authTag[32+AESNI_ALIGN];
+ u8 *iv = (u8 *) PTR_ALIGN((u8 *)iv_and_authTag, AESNI_ALIGN);
+ u8 *authTag = iv + 16;
+ struct scatter_walk src_sg_walk;
+ struct scatter_walk assoc_sg_walk;
+ struct scatter_walk dst_sg_walk;
+ unsigned int i;
+
+ if (unlikely((req->cryptlen < auth_tag_len) ||
+ (req->assoclen != 8 && req->assoclen != 12)))
+ return -EINVAL;
+ /* Assuming we are supporting rfc4106 64-bit extended */
+ /* sequence numbers We need to have the AAD length */
+ /* equal to 8 or 12 bytes */
+
+ tempCipherLen = (unsigned long)(req->cryptlen - auth_tag_len);
+ /* IV below built */
+ for (i = 0; i < 4; i++)
+ *(iv+i) = ctx->nonce[i];
+ for (i = 0; i < 8; i++)
+ *(iv+4+i) = req->iv[i];
+ *((__be32 *)(iv+12)) = counter;
+
+ if ((sg_is_last(req->src)) && (sg_is_last(req->assoc))) {
+ one_entry_in_sg = 1;
+ scatterwalk_start(&src_sg_walk, req->src);
+ scatterwalk_start(&assoc_sg_walk, req->assoc);
+ src = scatterwalk_map(&src_sg_walk, 0);
+ assoc = scatterwalk_map(&assoc_sg_walk, 0);
+ dst = src;
+ if (unlikely(req->src != req->dst)) {
+ scatterwalk_start(&dst_sg_walk, req->dst);
+ dst = scatterwalk_map(&dst_sg_walk, 0);
+ }
+
+ } else {
+ /* Allocate memory for src, dst, assoc */
+ src = kmalloc(req->cryptlen + req->assoclen, GFP_ATOMIC);
+ if (!src)
+ return -ENOMEM;
+ assoc = (src + req->cryptlen + auth_tag_len);
+ scatterwalk_map_and_copy(src, req->src, 0, req->cryptlen, 0);
+ scatterwalk_map_and_copy(assoc, req->assoc, 0,
+ req->assoclen, 0);
+ dst = src;
+ }
+
+ aesni_gcm_dec(aes_ctx, dst, src, tempCipherLen, iv,
+ ctx->hash_subkey, assoc, (unsigned long)req->assoclen,
+ authTag, auth_tag_len);
+
+ /* Compare generated tag with passed in tag. */
+ retval = memcmp(src + tempCipherLen, authTag, auth_tag_len) ?
+ -EBADMSG : 0;
+
+ if (one_entry_in_sg) {
+ if (unlikely(req->src != req->dst)) {
+ scatterwalk_unmap(dst, 0);
+ scatterwalk_done(&dst_sg_walk, 0, 0);
+ }
+ scatterwalk_unmap(src, 0);
+ scatterwalk_unmap(assoc, 0);
+ scatterwalk_done(&src_sg_walk, 0, 0);
+ scatterwalk_done(&assoc_sg_walk, 0, 0);
+ } else {
+ scatterwalk_map_and_copy(dst, req->dst, 0, req->cryptlen, 1);
+ kfree(src);
+ }
+ return retval;
+}
+
+static struct crypto_alg __rfc4106_alg = {
+ .cra_name = "__gcm-aes-aesni",
+ .cra_driver_name = "__driver-gcm-aes-aesni",
+ .cra_priority = 0,
+ .cra_flags = CRYPTO_ALG_TYPE_AEAD,
+ .cra_blocksize = 1,
+ .cra_ctxsize = sizeof(struct aesni_rfc4106_gcm_ctx) + AESNI_ALIGN,
+ .cra_alignmask = 0,
+ .cra_type = &crypto_aead_type,
+ .cra_module = THIS_MODULE,
+ .cra_list = LIST_HEAD_INIT(__rfc4106_alg.cra_list),
+ .cra_u = {
+ .aead = {
+ .encrypt = __driver_rfc4106_encrypt,
+ .decrypt = __driver_rfc4106_decrypt,
+ },
+ },
+};
+#endif
+
static int __init aesni_init(void)
{
int err;
@@ -738,6 +1248,7 @@
printk(KERN_INFO "Intel AES-NI instructions are not detected.\n");
return -ENODEV;
}
+
if ((err = crypto_register_alg(&aesni_alg)))
goto aes_err;
if ((err = crypto_register_alg(&__aesni_alg)))
@@ -746,18 +1257,24 @@
goto blk_ecb_err;
if ((err = crypto_register_alg(&blk_cbc_alg)))
goto blk_cbc_err;
- if ((err = crypto_register_alg(&blk_ctr_alg)))
- goto blk_ctr_err;
if ((err = crypto_register_alg(&ablk_ecb_alg)))
goto ablk_ecb_err;
if ((err = crypto_register_alg(&ablk_cbc_alg)))
goto ablk_cbc_err;
+#ifdef CONFIG_X86_64
+ if ((err = crypto_register_alg(&blk_ctr_alg)))
+ goto blk_ctr_err;
if ((err = crypto_register_alg(&ablk_ctr_alg)))
goto ablk_ctr_err;
+ if ((err = crypto_register_alg(&__rfc4106_alg)))
+ goto __aead_gcm_err;
+ if ((err = crypto_register_alg(&rfc4106_alg)))
+ goto aead_gcm_err;
#ifdef HAS_CTR
if ((err = crypto_register_alg(&ablk_rfc3686_ctr_alg)))
goto ablk_rfc3686_ctr_err;
#endif
+#endif
#ifdef HAS_LRW
if ((err = crypto_register_alg(&ablk_lrw_alg)))
goto ablk_lrw_err;
@@ -770,7 +1287,6 @@
if ((err = crypto_register_alg(&ablk_xts_alg)))
goto ablk_xts_err;
#endif
-
return err;
#ifdef HAS_XTS
@@ -784,18 +1300,24 @@
crypto_unregister_alg(&ablk_lrw_alg);
ablk_lrw_err:
#endif
+#ifdef CONFIG_X86_64
#ifdef HAS_CTR
crypto_unregister_alg(&ablk_rfc3686_ctr_alg);
ablk_rfc3686_ctr_err:
#endif
+ crypto_unregister_alg(&rfc4106_alg);
+aead_gcm_err:
+ crypto_unregister_alg(&__rfc4106_alg);
+__aead_gcm_err:
crypto_unregister_alg(&ablk_ctr_alg);
ablk_ctr_err:
+ crypto_unregister_alg(&blk_ctr_alg);
+blk_ctr_err:
+#endif
crypto_unregister_alg(&ablk_cbc_alg);
ablk_cbc_err:
crypto_unregister_alg(&ablk_ecb_alg);
ablk_ecb_err:
- crypto_unregister_alg(&blk_ctr_alg);
-blk_ctr_err:
crypto_unregister_alg(&blk_cbc_alg);
blk_cbc_err:
crypto_unregister_alg(&blk_ecb_alg);
@@ -818,13 +1340,17 @@
#ifdef HAS_LRW
crypto_unregister_alg(&ablk_lrw_alg);
#endif
+#ifdef CONFIG_X86_64
#ifdef HAS_CTR
crypto_unregister_alg(&ablk_rfc3686_ctr_alg);
#endif
+ crypto_unregister_alg(&rfc4106_alg);
+ crypto_unregister_alg(&__rfc4106_alg);
crypto_unregister_alg(&ablk_ctr_alg);
+ crypto_unregister_alg(&blk_ctr_alg);
+#endif
crypto_unregister_alg(&ablk_cbc_alg);
crypto_unregister_alg(&ablk_ecb_alg);
- crypto_unregister_alg(&blk_ctr_alg);
crypto_unregister_alg(&blk_cbc_alg);
crypto_unregister_alg(&blk_ecb_alg);
crypto_unregister_alg(&__aesni_alg);
diff --git a/arch/x86/include/asm/boot.h b/arch/x86/include/asm/boot.h
index 3b62ab5..5e1a2ee 100644
--- a/arch/x86/include/asm/boot.h
+++ b/arch/x86/include/asm/boot.h
@@ -32,11 +32,7 @@
#define BOOT_HEAP_SIZE 0x400000
#else /* !CONFIG_KERNEL_BZIP2 */
-#ifdef CONFIG_X86_64
-#define BOOT_HEAP_SIZE 0x7000
-#else
-#define BOOT_HEAP_SIZE 0x4000
-#endif
+#define BOOT_HEAP_SIZE 0x8000
#endif /* !CONFIG_KERNEL_BZIP2 */
diff --git a/arch/x86/include/asm/irq.h b/arch/x86/include/asm/irq.h
index ba870bb..c704b38 100644
--- a/arch/x86/include/asm/irq.h
+++ b/arch/x86/include/asm/irq.h
@@ -10,6 +10,9 @@
#include <asm/apicdef.h>
#include <asm/irq_vectors.h>
+/* Even though we don't support this, supply it to appease OF */
+static inline void irq_dispose_mapping(unsigned int virq) { }
+
static inline int irq_canonicalize(int irq)
{
return ((irq == 2) ? 9 : irq);
diff --git a/arch/x86/include/asm/kvm_emulate.h b/arch/x86/include/asm/kvm_emulate.h
index b36c6b3..8e37deb 100644
--- a/arch/x86/include/asm/kvm_emulate.h
+++ b/arch/x86/include/asm/kvm_emulate.h
@@ -15,6 +15,14 @@
struct x86_emulate_ctxt;
+struct x86_exception {
+ u8 vector;
+ bool error_code_valid;
+ u16 error_code;
+ bool nested_page_fault;
+ u64 address; /* cr2 or nested page fault gpa */
+};
+
/*
* x86_emulate_ops:
*
@@ -64,7 +72,8 @@
* @bytes: [IN ] Number of bytes to read from memory.
*/
int (*read_std)(unsigned long addr, void *val,
- unsigned int bytes, struct kvm_vcpu *vcpu, u32 *error);
+ unsigned int bytes, struct kvm_vcpu *vcpu,
+ struct x86_exception *fault);
/*
* write_std: Write bytes of standard (non-emulated/special) memory.
@@ -74,7 +83,8 @@
* @bytes: [IN ] Number of bytes to write to memory.
*/
int (*write_std)(unsigned long addr, void *val,
- unsigned int bytes, struct kvm_vcpu *vcpu, u32 *error);
+ unsigned int bytes, struct kvm_vcpu *vcpu,
+ struct x86_exception *fault);
/*
* fetch: Read bytes of standard (non-emulated/special) memory.
* Used for instruction fetch.
@@ -83,7 +93,8 @@
* @bytes: [IN ] Number of bytes to read from memory.
*/
int (*fetch)(unsigned long addr, void *val,
- unsigned int bytes, struct kvm_vcpu *vcpu, u32 *error);
+ unsigned int bytes, struct kvm_vcpu *vcpu,
+ struct x86_exception *fault);
/*
* read_emulated: Read bytes from emulated/special memory area.
@@ -94,7 +105,7 @@
int (*read_emulated)(unsigned long addr,
void *val,
unsigned int bytes,
- unsigned int *error,
+ struct x86_exception *fault,
struct kvm_vcpu *vcpu);
/*
@@ -107,7 +118,7 @@
int (*write_emulated)(unsigned long addr,
const void *val,
unsigned int bytes,
- unsigned int *error,
+ struct x86_exception *fault,
struct kvm_vcpu *vcpu);
/*
@@ -122,7 +133,7 @@
const void *old,
const void *new,
unsigned int bytes,
- unsigned int *error,
+ struct x86_exception *fault,
struct kvm_vcpu *vcpu);
int (*pio_in_emulated)(int size, unsigned short port, void *val,
@@ -159,7 +170,10 @@
};
union {
unsigned long *reg;
- unsigned long mem;
+ struct segmented_address {
+ ulong ea;
+ unsigned seg;
+ } mem;
} addr;
union {
unsigned long val;
@@ -226,9 +240,8 @@
bool perm_ok; /* do not check permissions if true */
- int exception; /* exception that happens during emulation or -1 */
- u32 error_code; /* error code for exception */
- bool error_code_valid;
+ bool have_exception;
+ struct x86_exception exception;
/* decode cache */
struct decode_cache decode;
@@ -252,7 +265,7 @@
#define X86EMUL_MODE_HOST X86EMUL_MODE_PROT64
#endif
-int x86_decode_insn(struct x86_emulate_ctxt *ctxt);
+int x86_decode_insn(struct x86_emulate_ctxt *ctxt, void *insn, int insn_len);
#define EMULATION_FAILED -1
#define EMULATION_OK 0
#define EMULATION_RESTART 1
diff --git a/arch/x86/include/asm/kvm_host.h b/arch/x86/include/asm/kvm_host.h
index f702f82..aa75f21 100644
--- a/arch/x86/include/asm/kvm_host.h
+++ b/arch/x86/include/asm/kvm_host.h
@@ -83,11 +83,14 @@
#define KVM_NR_FIXED_MTRR_REGION 88
#define KVM_NR_VAR_MTRR 8
+#define ASYNC_PF_PER_VCPU 64
+
extern spinlock_t kvm_lock;
extern struct list_head vm_list;
struct kvm_vcpu;
struct kvm;
+struct kvm_async_pf;
enum kvm_reg {
VCPU_REGS_RAX = 0,
@@ -114,6 +117,7 @@
enum kvm_reg_ex {
VCPU_EXREG_PDPTR = NR_VCPU_REGS,
+ VCPU_EXREG_CR3,
};
enum {
@@ -238,16 +242,18 @@
void (*new_cr3)(struct kvm_vcpu *vcpu);
void (*set_cr3)(struct kvm_vcpu *vcpu, unsigned long root);
unsigned long (*get_cr3)(struct kvm_vcpu *vcpu);
- int (*page_fault)(struct kvm_vcpu *vcpu, gva_t gva, u32 err);
- void (*inject_page_fault)(struct kvm_vcpu *vcpu);
+ int (*page_fault)(struct kvm_vcpu *vcpu, gva_t gva, u32 err,
+ bool prefault);
+ void (*inject_page_fault)(struct kvm_vcpu *vcpu,
+ struct x86_exception *fault);
void (*free)(struct kvm_vcpu *vcpu);
gpa_t (*gva_to_gpa)(struct kvm_vcpu *vcpu, gva_t gva, u32 access,
- u32 *error);
+ struct x86_exception *exception);
gpa_t (*translate_gpa)(struct kvm_vcpu *vcpu, gpa_t gpa, u32 access);
void (*prefetch_page)(struct kvm_vcpu *vcpu,
struct kvm_mmu_page *page);
int (*sync_page)(struct kvm_vcpu *vcpu,
- struct kvm_mmu_page *sp, bool clear_unsync);
+ struct kvm_mmu_page *sp);
void (*invlpg)(struct kvm_vcpu *vcpu, gva_t gva);
hpa_t root_hpa;
int root_level;
@@ -315,16 +321,6 @@
*/
struct kvm_mmu *walk_mmu;
- /*
- * This struct is filled with the necessary information to propagate a
- * page fault into the guest
- */
- struct {
- u64 address;
- unsigned error_code;
- bool nested;
- } fault;
-
/* only needed in kvm_pv_mmu_op() path, but it's hot so
* put it here to avoid allocation */
struct kvm_pv_mmu_op_buffer mmu_op_buffer;
@@ -412,6 +408,15 @@
u64 hv_vapic;
cpumask_var_t wbinvd_dirty_mask;
+
+ struct {
+ bool halted;
+ gfn_t gfns[roundup_pow_of_two(ASYNC_PF_PER_VCPU)];
+ struct gfn_to_hva_cache data;
+ u64 msr_val;
+ u32 id;
+ bool send_user_only;
+ } apf;
};
struct kvm_arch {
@@ -456,6 +461,10 @@
/* fields used by HYPER-V emulation */
u64 hv_guest_os_id;
u64 hv_hypercall;
+
+ #ifdef CONFIG_KVM_MMU_AUDIT
+ int audit_point;
+ #endif
};
struct kvm_vm_stat {
@@ -529,6 +538,7 @@
struct kvm_segment *var, int seg);
void (*get_cs_db_l_bits)(struct kvm_vcpu *vcpu, int *db, int *l);
void (*decache_cr0_guest_bits)(struct kvm_vcpu *vcpu);
+ void (*decache_cr3)(struct kvm_vcpu *vcpu);
void (*decache_cr4_guest_bits)(struct kvm_vcpu *vcpu);
void (*set_cr0)(struct kvm_vcpu *vcpu, unsigned long cr0);
void (*set_cr3)(struct kvm_vcpu *vcpu, unsigned long cr3);
@@ -582,9 +592,17 @@
void (*write_tsc_offset)(struct kvm_vcpu *vcpu, u64 offset);
+ void (*get_exit_info)(struct kvm_vcpu *vcpu, u64 *info1, u64 *info2);
const struct trace_print_flags *exit_reasons_str;
};
+struct kvm_arch_async_pf {
+ u32 token;
+ gfn_t gfn;
+ unsigned long cr3;
+ bool direct_map;
+};
+
extern struct kvm_x86_ops *kvm_x86_ops;
int kvm_mmu_module_init(void);
@@ -594,7 +612,6 @@
int kvm_mmu_create(struct kvm_vcpu *vcpu);
int kvm_mmu_setup(struct kvm_vcpu *vcpu);
void kvm_mmu_set_nonpresent_ptes(u64 trap_pte, u64 notrap_pte);
-void kvm_mmu_set_base_ptes(u64 base_pte);
void kvm_mmu_set_mask_ptes(u64 user_mask, u64 accessed_mask,
u64 dirty_mask, u64 nx_mask, u64 x_mask);
@@ -623,8 +640,15 @@
#define EMULTYPE_NO_DECODE (1 << 0)
#define EMULTYPE_TRAP_UD (1 << 1)
#define EMULTYPE_SKIP (1 << 2)
-int emulate_instruction(struct kvm_vcpu *vcpu,
- unsigned long cr2, u16 error_code, int emulation_type);
+int x86_emulate_instruction(struct kvm_vcpu *vcpu, unsigned long cr2,
+ int emulation_type, void *insn, int insn_len);
+
+static inline int emulate_instruction(struct kvm_vcpu *vcpu,
+ int emulation_type)
+{
+ return x86_emulate_instruction(vcpu, 0, emulation_type, NULL, 0);
+}
+
void realmode_lgdt(struct kvm_vcpu *vcpu, u16 size, unsigned long address);
void realmode_lidt(struct kvm_vcpu *vcpu, u16 size, unsigned long address);
@@ -650,7 +674,7 @@
int kvm_set_cr0(struct kvm_vcpu *vcpu, unsigned long cr0);
int kvm_set_cr3(struct kvm_vcpu *vcpu, unsigned long cr3);
int kvm_set_cr4(struct kvm_vcpu *vcpu, unsigned long cr4);
-void kvm_set_cr8(struct kvm_vcpu *vcpu, unsigned long cr8);
+int kvm_set_cr8(struct kvm_vcpu *vcpu, unsigned long cr8);
int kvm_set_dr(struct kvm_vcpu *vcpu, int dr, unsigned long val);
int kvm_get_dr(struct kvm_vcpu *vcpu, int dr, unsigned long *val);
unsigned long kvm_get_cr8(struct kvm_vcpu *vcpu);
@@ -668,11 +692,11 @@
void kvm_queue_exception_e(struct kvm_vcpu *vcpu, unsigned nr, u32 error_code);
void kvm_requeue_exception(struct kvm_vcpu *vcpu, unsigned nr);
void kvm_requeue_exception_e(struct kvm_vcpu *vcpu, unsigned nr, u32 error_code);
-void kvm_inject_page_fault(struct kvm_vcpu *vcpu);
+void kvm_inject_page_fault(struct kvm_vcpu *vcpu, struct x86_exception *fault);
int kvm_read_guest_page_mmu(struct kvm_vcpu *vcpu, struct kvm_mmu *mmu,
gfn_t gfn, void *data, int offset, int len,
u32 access);
-void kvm_propagate_fault(struct kvm_vcpu *vcpu);
+void kvm_propagate_fault(struct kvm_vcpu *vcpu, struct x86_exception *fault);
bool kvm_require_cpl(struct kvm_vcpu *vcpu, int required_cpl);
int kvm_pic_set_irq(void *opaque, int irq, int level);
@@ -690,16 +714,21 @@
int kvm_mmu_load(struct kvm_vcpu *vcpu);
void kvm_mmu_unload(struct kvm_vcpu *vcpu);
void kvm_mmu_sync_roots(struct kvm_vcpu *vcpu);
-gpa_t kvm_mmu_gva_to_gpa_read(struct kvm_vcpu *vcpu, gva_t gva, u32 *error);
-gpa_t kvm_mmu_gva_to_gpa_fetch(struct kvm_vcpu *vcpu, gva_t gva, u32 *error);
-gpa_t kvm_mmu_gva_to_gpa_write(struct kvm_vcpu *vcpu, gva_t gva, u32 *error);
-gpa_t kvm_mmu_gva_to_gpa_system(struct kvm_vcpu *vcpu, gva_t gva, u32 *error);
+gpa_t kvm_mmu_gva_to_gpa_read(struct kvm_vcpu *vcpu, gva_t gva,
+ struct x86_exception *exception);
+gpa_t kvm_mmu_gva_to_gpa_fetch(struct kvm_vcpu *vcpu, gva_t gva,
+ struct x86_exception *exception);
+gpa_t kvm_mmu_gva_to_gpa_write(struct kvm_vcpu *vcpu, gva_t gva,
+ struct x86_exception *exception);
+gpa_t kvm_mmu_gva_to_gpa_system(struct kvm_vcpu *vcpu, gva_t gva,
+ struct x86_exception *exception);
int kvm_emulate_hypercall(struct kvm_vcpu *vcpu);
int kvm_fix_hypercall(struct kvm_vcpu *vcpu);
-int kvm_mmu_page_fault(struct kvm_vcpu *vcpu, gva_t gva, u32 error_code);
+int kvm_mmu_page_fault(struct kvm_vcpu *vcpu, gva_t gva, u32 error_code,
+ void *insn, int insn_len);
void kvm_mmu_invlpg(struct kvm_vcpu *vcpu, gva_t gva);
void kvm_enable_tdp(void);
@@ -766,20 +795,25 @@
#define HF_VINTR_MASK (1 << 2)
#define HF_NMI_MASK (1 << 3)
#define HF_IRET_MASK (1 << 4)
+#define HF_GUEST_MASK (1 << 5) /* VCPU is in guest-mode */
/*
* Hardware virtualization extension instructions may fault if a
* reboot turns off virtualization while processes are running.
* Trap the fault and ignore the instruction if that happens.
*/
-asmlinkage void kvm_handle_fault_on_reboot(void);
+asmlinkage void kvm_spurious_fault(void);
+extern bool kvm_rebooting;
#define __kvm_handle_fault_on_reboot(insn) \
"666: " insn "\n\t" \
+ "668: \n\t" \
".pushsection .fixup, \"ax\" \n" \
"667: \n\t" \
+ "cmpb $0, kvm_rebooting \n\t" \
+ "jne 668b \n\t" \
__ASM_SIZE(push) " $666b \n\t" \
- "jmp kvm_handle_fault_on_reboot \n\t" \
+ "call kvm_spurious_fault \n\t" \
".popsection \n\t" \
".pushsection __ex_table, \"a\" \n\t" \
_ASM_PTR " 666b, 667b \n\t" \
@@ -799,4 +833,15 @@
bool kvm_is_linear_rip(struct kvm_vcpu *vcpu, unsigned long linear_rip);
+void kvm_arch_async_page_not_present(struct kvm_vcpu *vcpu,
+ struct kvm_async_pf *work);
+void kvm_arch_async_page_present(struct kvm_vcpu *vcpu,
+ struct kvm_async_pf *work);
+void kvm_arch_async_page_ready(struct kvm_vcpu *vcpu,
+ struct kvm_async_pf *work);
+bool kvm_arch_can_inject_async_page_present(struct kvm_vcpu *vcpu);
+extern bool kvm_find_async_pf_gfn(struct kvm_vcpu *vcpu, gfn_t gfn);
+
+void kvm_complete_insn_gp(struct kvm_vcpu *vcpu, int err);
+
#endif /* _ASM_X86_KVM_HOST_H */
diff --git a/arch/x86/include/asm/kvm_para.h b/arch/x86/include/asm/kvm_para.h
index 7b562b6..a427bf7 100644
--- a/arch/x86/include/asm/kvm_para.h
+++ b/arch/x86/include/asm/kvm_para.h
@@ -20,6 +20,7 @@
* are available. The use of 0x11 and 0x12 is deprecated
*/
#define KVM_FEATURE_CLOCKSOURCE2 3
+#define KVM_FEATURE_ASYNC_PF 4
/* The last 8 bits are used to indicate how to interpret the flags field
* in pvclock structure. If no bits are set, all flags are ignored.
@@ -32,9 +33,13 @@
/* Custom MSRs falls in the range 0x4b564d00-0x4b564dff */
#define MSR_KVM_WALL_CLOCK_NEW 0x4b564d00
#define MSR_KVM_SYSTEM_TIME_NEW 0x4b564d01
+#define MSR_KVM_ASYNC_PF_EN 0x4b564d02
#define KVM_MAX_MMU_OP_BATCH 32
+#define KVM_ASYNC_PF_ENABLED (1 << 0)
+#define KVM_ASYNC_PF_SEND_ALWAYS (1 << 1)
+
/* Operations for KVM_HC_MMU_OP */
#define KVM_MMU_OP_WRITE_PTE 1
#define KVM_MMU_OP_FLUSH_TLB 2
@@ -61,10 +66,20 @@
__u64 pt_phys;
};
+#define KVM_PV_REASON_PAGE_NOT_PRESENT 1
+#define KVM_PV_REASON_PAGE_READY 2
+
+struct kvm_vcpu_pv_apf_data {
+ __u32 reason;
+ __u8 pad[60];
+ __u32 enabled;
+};
+
#ifdef __KERNEL__
#include <asm/processor.h>
extern void kvmclock_init(void);
+extern int kvm_register_clock(char *txt);
/* This instruction is vmcall. On non-VT architectures, it will generate a
@@ -160,8 +175,17 @@
#ifdef CONFIG_KVM_GUEST
void __init kvm_guest_init(void);
+void kvm_async_pf_task_wait(u32 token);
+void kvm_async_pf_task_wake(u32 token);
+u32 kvm_read_and_reset_pf_reason(void);
#else
#define kvm_guest_init() do { } while (0)
+#define kvm_async_pf_task_wait(T) do {} while(0)
+#define kvm_async_pf_task_wake(T) do {} while(0)
+static inline u32 kvm_read_and_reset_pf_reason(void)
+{
+ return 0;
+}
#endif
#endif /* __KERNEL__ */
diff --git a/arch/x86/include/asm/olpc.h b/arch/x86/include/asm/olpc.h
index 42a978c..f482010 100644
--- a/arch/x86/include/asm/olpc.h
+++ b/arch/x86/include/asm/olpc.h
@@ -107,10 +107,14 @@
/* GPIO assignments */
#define OLPC_GPIO_MIC_AC 1
-#define OLPC_GPIO_DCON_IRQ geode_gpio(7)
+#define OLPC_GPIO_DCON_STAT0 5
+#define OLPC_GPIO_DCON_STAT1 6
+#define OLPC_GPIO_DCON_IRQ 7
#define OLPC_GPIO_THRM_ALRM geode_gpio(10)
-#define OLPC_GPIO_SMB_CLK geode_gpio(14)
-#define OLPC_GPIO_SMB_DATA geode_gpio(15)
+#define OLPC_GPIO_DCON_LOAD 11
+#define OLPC_GPIO_DCON_BLANK 12
+#define OLPC_GPIO_SMB_CLK 14
+#define OLPC_GPIO_SMB_DATA 15
#define OLPC_GPIO_WORKAUX geode_gpio(24)
#define OLPC_GPIO_LID geode_gpio(26)
#define OLPC_GPIO_ECSCI geode_gpio(27)
diff --git a/arch/x86/include/asm/olpc_ofw.h b/arch/x86/include/asm/olpc_ofw.h
index 2a84781..641988e 100644
--- a/arch/x86/include/asm/olpc_ofw.h
+++ b/arch/x86/include/asm/olpc_ofw.h
@@ -8,6 +8,8 @@
#ifdef CONFIG_OLPC_OPENFIRMWARE
+extern bool olpc_ofw_is_installed(void);
+
/* run an OFW command by calling into the firmware */
#define olpc_ofw(name, args, res) \
__olpc_ofw((name), ARRAY_SIZE(args), args, ARRAY_SIZE(res), res)
@@ -26,10 +28,17 @@
#else /* !CONFIG_OLPC_OPENFIRMWARE */
+static inline bool olpc_ofw_is_installed(void) { return false; }
static inline void olpc_ofw_detect(void) { }
static inline void setup_olpc_ofw_pgd(void) { }
static inline bool olpc_ofw_present(void) { return false; }
#endif /* !CONFIG_OLPC_OPENFIRMWARE */
+#ifdef CONFIG_OLPC_OPENFIRMWARE_DT
+extern void olpc_dt_build_devicetree(void);
+#else
+static inline void olpc_dt_build_devicetree(void) { }
+#endif /* CONFIG_OLPC_OPENFIRMWARE_DT */
+
#endif /* _ASM_X86_OLPC_OFW_H */
diff --git a/arch/x86/include/asm/pgalloc.h b/arch/x86/include/asm/pgalloc.h
index 271de94..b4389a4 100644
--- a/arch/x86/include/asm/pgalloc.h
+++ b/arch/x86/include/asm/pgalloc.h
@@ -92,7 +92,7 @@
extern void ___pmd_free_tlb(struct mmu_gather *tlb, pmd_t *pmd);
static inline void __pmd_free_tlb(struct mmu_gather *tlb, pmd_t *pmd,
- unsigned long adddress)
+ unsigned long address)
{
___pmd_free_tlb(tlb, pmd);
}
diff --git a/arch/x86/include/asm/processor.h b/arch/x86/include/asm/processor.h
index c6efecf..53fd1d5 100644
--- a/arch/x86/include/asm/processor.h
+++ b/arch/x86/include/asm/processor.h
@@ -901,7 +901,7 @@
/*
* The below -8 is to reserve 8 bytes on top of the ring0 stack.
* This is necessary to guarantee that the entire "struct pt_regs"
- * is accessable even if the CPU haven't stored the SS/ESP registers
+ * is accessible even if the CPU haven't stored the SS/ESP registers
* on the stack (interrupt gate does not save these registers
* when switching to the same priv ring).
* Therefore beware: accessing the ss/esp fields of the
diff --git a/arch/x86/include/asm/prom.h b/arch/x86/include/asm/prom.h
new file mode 100644
index 0000000..b4ec95f
--- /dev/null
+++ b/arch/x86/include/asm/prom.h
@@ -0,0 +1 @@
+/* dummy prom.h; here to make linux/of.h's #includes happy */
diff --git a/arch/x86/include/asm/svm.h b/arch/x86/include/asm/svm.h
index 0e83105..f2b83bc 100644
--- a/arch/x86/include/asm/svm.h
+++ b/arch/x86/include/asm/svm.h
@@ -47,14 +47,13 @@
INTERCEPT_MONITOR,
INTERCEPT_MWAIT,
INTERCEPT_MWAIT_COND,
+ INTERCEPT_XSETBV,
};
struct __attribute__ ((__packed__)) vmcb_control_area {
- u16 intercept_cr_read;
- u16 intercept_cr_write;
- u16 intercept_dr_read;
- u16 intercept_dr_write;
+ u32 intercept_cr;
+ u32 intercept_dr;
u32 intercept_exceptions;
u64 intercept;
u8 reserved_1[42];
@@ -81,14 +80,19 @@
u32 event_inj_err;
u64 nested_cr3;
u64 lbr_ctl;
- u64 reserved_5;
+ u32 clean;
+ u32 reserved_5;
u64 next_rip;
- u8 reserved_6[816];
+ u8 insn_len;
+ u8 insn_bytes[15];
+ u8 reserved_6[800];
};
#define TLB_CONTROL_DO_NOTHING 0
#define TLB_CONTROL_FLUSH_ALL_ASID 1
+#define TLB_CONTROL_FLUSH_ASID 3
+#define TLB_CONTROL_FLUSH_ASID_LOCAL 7
#define V_TPR_MASK 0x0f
@@ -204,19 +208,31 @@
#define SVM_SELECTOR_READ_MASK SVM_SELECTOR_WRITE_MASK
#define SVM_SELECTOR_CODE_MASK (1 << 3)
-#define INTERCEPT_CR0_MASK 1
-#define INTERCEPT_CR3_MASK (1 << 3)
-#define INTERCEPT_CR4_MASK (1 << 4)
-#define INTERCEPT_CR8_MASK (1 << 8)
+#define INTERCEPT_CR0_READ 0
+#define INTERCEPT_CR3_READ 3
+#define INTERCEPT_CR4_READ 4
+#define INTERCEPT_CR8_READ 8
+#define INTERCEPT_CR0_WRITE (16 + 0)
+#define INTERCEPT_CR3_WRITE (16 + 3)
+#define INTERCEPT_CR4_WRITE (16 + 4)
+#define INTERCEPT_CR8_WRITE (16 + 8)
-#define INTERCEPT_DR0_MASK 1
-#define INTERCEPT_DR1_MASK (1 << 1)
-#define INTERCEPT_DR2_MASK (1 << 2)
-#define INTERCEPT_DR3_MASK (1 << 3)
-#define INTERCEPT_DR4_MASK (1 << 4)
-#define INTERCEPT_DR5_MASK (1 << 5)
-#define INTERCEPT_DR6_MASK (1 << 6)
-#define INTERCEPT_DR7_MASK (1 << 7)
+#define INTERCEPT_DR0_READ 0
+#define INTERCEPT_DR1_READ 1
+#define INTERCEPT_DR2_READ 2
+#define INTERCEPT_DR3_READ 3
+#define INTERCEPT_DR4_READ 4
+#define INTERCEPT_DR5_READ 5
+#define INTERCEPT_DR6_READ 6
+#define INTERCEPT_DR7_READ 7
+#define INTERCEPT_DR0_WRITE (16 + 0)
+#define INTERCEPT_DR1_WRITE (16 + 1)
+#define INTERCEPT_DR2_WRITE (16 + 2)
+#define INTERCEPT_DR3_WRITE (16 + 3)
+#define INTERCEPT_DR4_WRITE (16 + 4)
+#define INTERCEPT_DR5_WRITE (16 + 5)
+#define INTERCEPT_DR6_WRITE (16 + 6)
+#define INTERCEPT_DR7_WRITE (16 + 7)
#define SVM_EVTINJ_VEC_MASK 0xff
@@ -246,6 +262,8 @@
#define SVM_EXITINFOSHIFT_TS_REASON_JMP 38
#define SVM_EXITINFOSHIFT_TS_HAS_ERROR_CODE 44
+#define SVM_EXITINFO_REG_MASK 0x0F
+
#define SVM_EXIT_READ_CR0 0x000
#define SVM_EXIT_READ_CR3 0x003
#define SVM_EXIT_READ_CR4 0x004
@@ -316,6 +334,7 @@
#define SVM_EXIT_MONITOR 0x08a
#define SVM_EXIT_MWAIT 0x08b
#define SVM_EXIT_MWAIT_COND 0x08c
+#define SVM_EXIT_XSETBV 0x08d
#define SVM_EXIT_NPF 0x400
#define SVM_EXIT_ERR -1
diff --git a/arch/x86/include/asm/traps.h b/arch/x86/include/asm/traps.h
index f66cda5..0310da6 100644
--- a/arch/x86/include/asm/traps.h
+++ b/arch/x86/include/asm/traps.h
@@ -30,6 +30,7 @@
asmlinkage void stack_segment(void);
asmlinkage void general_protection(void);
asmlinkage void page_fault(void);
+asmlinkage void async_page_fault(void);
asmlinkage void spurious_interrupt_bug(void);
asmlinkage void coprocessor_error(void);
asmlinkage void alignment_check(void);
diff --git a/arch/x86/include/asm/vmx.h b/arch/x86/include/asm/vmx.h
index 9f0cbd9..84471b8 100644
--- a/arch/x86/include/asm/vmx.h
+++ b/arch/x86/include/asm/vmx.h
@@ -66,15 +66,23 @@
#define PIN_BASED_NMI_EXITING 0x00000008
#define PIN_BASED_VIRTUAL_NMIS 0x00000020
+#define VM_EXIT_SAVE_DEBUG_CONTROLS 0x00000002
#define VM_EXIT_HOST_ADDR_SPACE_SIZE 0x00000200
+#define VM_EXIT_LOAD_IA32_PERF_GLOBAL_CTRL 0x00001000
#define VM_EXIT_ACK_INTR_ON_EXIT 0x00008000
#define VM_EXIT_SAVE_IA32_PAT 0x00040000
#define VM_EXIT_LOAD_IA32_PAT 0x00080000
+#define VM_EXIT_SAVE_IA32_EFER 0x00100000
+#define VM_EXIT_LOAD_IA32_EFER 0x00200000
+#define VM_EXIT_SAVE_VMX_PREEMPTION_TIMER 0x00400000
+#define VM_ENTRY_LOAD_DEBUG_CONTROLS 0x00000002
#define VM_ENTRY_IA32E_MODE 0x00000200
#define VM_ENTRY_SMM 0x00000400
#define VM_ENTRY_DEACT_DUAL_MONITOR 0x00000800
+#define VM_ENTRY_LOAD_IA32_PERF_GLOBAL_CTRL 0x00002000
#define VM_ENTRY_LOAD_IA32_PAT 0x00004000
+#define VM_ENTRY_LOAD_IA32_EFER 0x00008000
/* VMCS Encodings */
enum vmcs_field {
@@ -239,6 +247,7 @@
#define EXIT_REASON_TASK_SWITCH 9
#define EXIT_REASON_CPUID 10
#define EXIT_REASON_HLT 12
+#define EXIT_REASON_INVD 13
#define EXIT_REASON_INVLPG 14
#define EXIT_REASON_RDPMC 15
#define EXIT_REASON_RDTSC 16
@@ -296,6 +305,12 @@
#define GUEST_INTR_STATE_SMI 0x00000004
#define GUEST_INTR_STATE_NMI 0x00000008
+/* GUEST_ACTIVITY_STATE flags */
+#define GUEST_ACTIVITY_ACTIVE 0
+#define GUEST_ACTIVITY_HLT 1
+#define GUEST_ACTIVITY_SHUTDOWN 2
+#define GUEST_ACTIVITY_WAIT_SIPI 3
+
/*
* Exit Qualifications for MOV for Control Register Access
*/
diff --git a/arch/x86/kernel/amd_iommu.c b/arch/x86/kernel/amd_iommu.c
index d2fdb08..57ca777 100644
--- a/arch/x86/kernel/amd_iommu.c
+++ b/arch/x86/kernel/amd_iommu.c
@@ -1086,7 +1086,7 @@
dma_dom->aperture_size += APERTURE_RANGE_SIZE;
- /* Intialize the exclusion range if necessary */
+ /* Initialize the exclusion range if necessary */
for_each_iommu(iommu) {
if (iommu->exclusion_start &&
iommu->exclusion_start >= dma_dom->aperture[index]->offset
@@ -1353,7 +1353,7 @@
/*
* Allocates a new protection domain usable for the dma_ops functions.
- * It also intializes the page table and the address allocator data
+ * It also initializes the page table and the address allocator data
* structures required for the dma_ops interface
*/
static struct dma_ops_domain *dma_ops_domain_alloc(void)
diff --git a/arch/x86/kernel/entry_32.S b/arch/x86/kernel/entry_32.S
index 591e601..c8b4efa 100644
--- a/arch/x86/kernel/entry_32.S
+++ b/arch/x86/kernel/entry_32.S
@@ -1406,6 +1406,16 @@
CFI_ENDPROC
END(general_protection)
+#ifdef CONFIG_KVM_GUEST
+ENTRY(async_page_fault)
+ RING0_EC_FRAME
+ pushl $do_async_page_fault
+ CFI_ADJUST_CFA_OFFSET 4
+ jmp error_code
+ CFI_ENDPROC
+END(apf_page_fault)
+#endif
+
/*
* End of kprobes section
*/
diff --git a/arch/x86/kernel/entry_64.S b/arch/x86/kernel/entry_64.S
index d3b895f..aed1ffb 100644
--- a/arch/x86/kernel/entry_64.S
+++ b/arch/x86/kernel/entry_64.S
@@ -1329,6 +1329,9 @@
#endif
errorentry general_protection do_general_protection
errorentry page_fault do_page_fault
+#ifdef CONFIG_KVM_GUEST
+errorentry async_page_fault do_async_page_fault
+#endif
#ifdef CONFIG_X86_MCE
paranoidzeroentry machine_check *machine_check_vector(%rip)
#endif
diff --git a/arch/x86/kernel/head_32.S b/arch/x86/kernel/head_32.S
index 9f54b20..fc293dc 100644
--- a/arch/x86/kernel/head_32.S
+++ b/arch/x86/kernel/head_32.S
@@ -126,7 +126,7 @@
movsl
movl pa(boot_params) + NEW_CL_POINTER,%esi
andl %esi,%esi
- jz 1f # No comand line
+ jz 1f # No command line
movl $pa(boot_command_line),%edi
movl $(COMMAND_LINE_SIZE/4),%ecx
rep
diff --git a/arch/x86/kernel/i387.c b/arch/x86/kernel/i387.c
index 58bb239..e60c38c 100644
--- a/arch/x86/kernel/i387.c
+++ b/arch/x86/kernel/i387.c
@@ -169,6 +169,7 @@
set_stopped_child_used_math(tsk);
return 0;
}
+EXPORT_SYMBOL_GPL(init_fpu);
/*
* The xstateregs_active() routine is the same as the fpregs_active() routine,
diff --git a/arch/x86/kernel/irq.c b/arch/x86/kernel/irq.c
index 3a43caa..52945da 100644
--- a/arch/x86/kernel/irq.c
+++ b/arch/x86/kernel/irq.c
@@ -4,6 +4,7 @@
#include <linux/cpu.h>
#include <linux/interrupt.h>
#include <linux/kernel_stat.h>
+#include <linux/of.h>
#include <linux/seq_file.h>
#include <linux/smp.h>
#include <linux/ftrace.h>
@@ -275,6 +276,15 @@
EXPORT_SYMBOL_GPL(vector_used_by_percpu_irq);
+#ifdef CONFIG_OF
+unsigned int irq_create_of_mapping(struct device_node *controller,
+ const u32 *intspec, unsigned int intsize)
+{
+ return intspec[0];
+}
+EXPORT_SYMBOL_GPL(irq_create_of_mapping);
+#endif
+
#ifdef CONFIG_HOTPLUG_CPU
/* A cpu has been removed from cpu_online_mask. Reset irq affinities. */
void fixup_irqs(void)
diff --git a/arch/x86/kernel/kvm.c b/arch/x86/kernel/kvm.c
index 63b0ec8..8dc4466 100644
--- a/arch/x86/kernel/kvm.c
+++ b/arch/x86/kernel/kvm.c
@@ -27,16 +27,37 @@
#include <linux/mm.h>
#include <linux/highmem.h>
#include <linux/hardirq.h>
+#include <linux/notifier.h>
+#include <linux/reboot.h>
+#include <linux/hash.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/kprobes.h>
#include <asm/timer.h>
+#include <asm/cpu.h>
+#include <asm/traps.h>
+#include <asm/desc.h>
+#include <asm/tlbflush.h>
#define MMU_QUEUE_SIZE 1024
+static int kvmapf = 1;
+
+static int parse_no_kvmapf(char *arg)
+{
+ kvmapf = 0;
+ return 0;
+}
+
+early_param("no-kvmapf", parse_no_kvmapf);
+
struct kvm_para_state {
u8 mmu_queue[MMU_QUEUE_SIZE];
int mmu_queue_len;
};
static DEFINE_PER_CPU(struct kvm_para_state, para_state);
+static DEFINE_PER_CPU(struct kvm_vcpu_pv_apf_data, apf_reason) __aligned(64);
static struct kvm_para_state *kvm_para_state(void)
{
@@ -50,6 +71,195 @@
{
}
+#define KVM_TASK_SLEEP_HASHBITS 8
+#define KVM_TASK_SLEEP_HASHSIZE (1<<KVM_TASK_SLEEP_HASHBITS)
+
+struct kvm_task_sleep_node {
+ struct hlist_node link;
+ wait_queue_head_t wq;
+ u32 token;
+ int cpu;
+ bool halted;
+ struct mm_struct *mm;
+};
+
+static struct kvm_task_sleep_head {
+ spinlock_t lock;
+ struct hlist_head list;
+} async_pf_sleepers[KVM_TASK_SLEEP_HASHSIZE];
+
+static struct kvm_task_sleep_node *_find_apf_task(struct kvm_task_sleep_head *b,
+ u32 token)
+{
+ struct hlist_node *p;
+
+ hlist_for_each(p, &b->list) {
+ struct kvm_task_sleep_node *n =
+ hlist_entry(p, typeof(*n), link);
+ if (n->token == token)
+ return n;
+ }
+
+ return NULL;
+}
+
+void kvm_async_pf_task_wait(u32 token)
+{
+ u32 key = hash_32(token, KVM_TASK_SLEEP_HASHBITS);
+ struct kvm_task_sleep_head *b = &async_pf_sleepers[key];
+ struct kvm_task_sleep_node n, *e;
+ DEFINE_WAIT(wait);
+ int cpu, idle;
+
+ cpu = get_cpu();
+ idle = idle_cpu(cpu);
+ put_cpu();
+
+ spin_lock(&b->lock);
+ e = _find_apf_task(b, token);
+ if (e) {
+ /* dummy entry exist -> wake up was delivered ahead of PF */
+ hlist_del(&e->link);
+ kfree(e);
+ spin_unlock(&b->lock);
+ return;
+ }
+
+ n.token = token;
+ n.cpu = smp_processor_id();
+ n.mm = current->active_mm;
+ n.halted = idle || preempt_count() > 1;
+ atomic_inc(&n.mm->mm_count);
+ init_waitqueue_head(&n.wq);
+ hlist_add_head(&n.link, &b->list);
+ spin_unlock(&b->lock);
+
+ for (;;) {
+ if (!n.halted)
+ prepare_to_wait(&n.wq, &wait, TASK_UNINTERRUPTIBLE);
+ if (hlist_unhashed(&n.link))
+ break;
+
+ if (!n.halted) {
+ local_irq_enable();
+ schedule();
+ local_irq_disable();
+ } else {
+ /*
+ * We cannot reschedule. So halt.
+ */
+ native_safe_halt();
+ local_irq_disable();
+ }
+ }
+ if (!n.halted)
+ finish_wait(&n.wq, &wait);
+
+ return;
+}
+EXPORT_SYMBOL_GPL(kvm_async_pf_task_wait);
+
+static void apf_task_wake_one(struct kvm_task_sleep_node *n)
+{
+ hlist_del_init(&n->link);
+ if (!n->mm)
+ return;
+ mmdrop(n->mm);
+ if (n->halted)
+ smp_send_reschedule(n->cpu);
+ else if (waitqueue_active(&n->wq))
+ wake_up(&n->wq);
+}
+
+static void apf_task_wake_all(void)
+{
+ int i;
+
+ for (i = 0; i < KVM_TASK_SLEEP_HASHSIZE; i++) {
+ struct hlist_node *p, *next;
+ struct kvm_task_sleep_head *b = &async_pf_sleepers[i];
+ spin_lock(&b->lock);
+ hlist_for_each_safe(p, next, &b->list) {
+ struct kvm_task_sleep_node *n =
+ hlist_entry(p, typeof(*n), link);
+ if (n->cpu == smp_processor_id())
+ apf_task_wake_one(n);
+ }
+ spin_unlock(&b->lock);
+ }
+}
+
+void kvm_async_pf_task_wake(u32 token)
+{
+ u32 key = hash_32(token, KVM_TASK_SLEEP_HASHBITS);
+ struct kvm_task_sleep_head *b = &async_pf_sleepers[key];
+ struct kvm_task_sleep_node *n;
+
+ if (token == ~0) {
+ apf_task_wake_all();
+ return;
+ }
+
+again:
+ spin_lock(&b->lock);
+ n = _find_apf_task(b, token);
+ if (!n) {
+ /*
+ * async PF was not yet handled.
+ * Add dummy entry for the token.
+ */
+ n = kmalloc(sizeof(*n), GFP_ATOMIC);
+ if (!n) {
+ /*
+ * Allocation failed! Busy wait while other cpu
+ * handles async PF.
+ */
+ spin_unlock(&b->lock);
+ cpu_relax();
+ goto again;
+ }
+ n->token = token;
+ n->cpu = smp_processor_id();
+ n->mm = NULL;
+ init_waitqueue_head(&n->wq);
+ hlist_add_head(&n->link, &b->list);
+ } else
+ apf_task_wake_one(n);
+ spin_unlock(&b->lock);
+ return;
+}
+EXPORT_SYMBOL_GPL(kvm_async_pf_task_wake);
+
+u32 kvm_read_and_reset_pf_reason(void)
+{
+ u32 reason = 0;
+
+ if (__get_cpu_var(apf_reason).enabled) {
+ reason = __get_cpu_var(apf_reason).reason;
+ __get_cpu_var(apf_reason).reason = 0;
+ }
+
+ return reason;
+}
+EXPORT_SYMBOL_GPL(kvm_read_and_reset_pf_reason);
+
+dotraplinkage void __kprobes
+do_async_page_fault(struct pt_regs *regs, unsigned long error_code)
+{
+ switch (kvm_read_and_reset_pf_reason()) {
+ default:
+ do_page_fault(regs, error_code);
+ break;
+ case KVM_PV_REASON_PAGE_NOT_PRESENT:
+ /* page is swapped out by the host. */
+ kvm_async_pf_task_wait((u32)read_cr2());
+ break;
+ case KVM_PV_REASON_PAGE_READY:
+ kvm_async_pf_task_wake((u32)read_cr2());
+ break;
+ }
+}
+
static void kvm_mmu_op(void *buffer, unsigned len)
{
int r;
@@ -231,10 +441,117 @@
#endif
}
-void __init kvm_guest_init(void)
+void __cpuinit kvm_guest_cpu_init(void)
{
if (!kvm_para_available())
return;
+ if (kvm_para_has_feature(KVM_FEATURE_ASYNC_PF) && kvmapf) {
+ u64 pa = __pa(&__get_cpu_var(apf_reason));
+
+#ifdef CONFIG_PREEMPT
+ pa |= KVM_ASYNC_PF_SEND_ALWAYS;
+#endif
+ wrmsrl(MSR_KVM_ASYNC_PF_EN, pa | KVM_ASYNC_PF_ENABLED);
+ __get_cpu_var(apf_reason).enabled = 1;
+ printk(KERN_INFO"KVM setup async PF for cpu %d\n",
+ smp_processor_id());
+ }
+}
+
+static void kvm_pv_disable_apf(void *unused)
+{
+ if (!__get_cpu_var(apf_reason).enabled)
+ return;
+
+ wrmsrl(MSR_KVM_ASYNC_PF_EN, 0);
+ __get_cpu_var(apf_reason).enabled = 0;
+
+ printk(KERN_INFO"Unregister pv shared memory for cpu %d\n",
+ smp_processor_id());
+}
+
+static int kvm_pv_reboot_notify(struct notifier_block *nb,
+ unsigned long code, void *unused)
+{
+ if (code == SYS_RESTART)
+ on_each_cpu(kvm_pv_disable_apf, NULL, 1);
+ return NOTIFY_DONE;
+}
+
+static struct notifier_block kvm_pv_reboot_nb = {
+ .notifier_call = kvm_pv_reboot_notify,
+};
+
+#ifdef CONFIG_SMP
+static void __init kvm_smp_prepare_boot_cpu(void)
+{
+#ifdef CONFIG_KVM_CLOCK
+ WARN_ON(kvm_register_clock("primary cpu clock"));
+#endif
+ kvm_guest_cpu_init();
+ native_smp_prepare_boot_cpu();
+}
+
+static void kvm_guest_cpu_online(void *dummy)
+{
+ kvm_guest_cpu_init();
+}
+
+static void kvm_guest_cpu_offline(void *dummy)
+{
+ kvm_pv_disable_apf(NULL);
+ apf_task_wake_all();
+}
+
+static int __cpuinit kvm_cpu_notify(struct notifier_block *self,
+ unsigned long action, void *hcpu)
+{
+ int cpu = (unsigned long)hcpu;
+ switch (action) {
+ case CPU_ONLINE:
+ case CPU_DOWN_FAILED:
+ case CPU_ONLINE_FROZEN:
+ smp_call_function_single(cpu, kvm_guest_cpu_online, NULL, 0);
+ break;
+ case CPU_DOWN_PREPARE:
+ case CPU_DOWN_PREPARE_FROZEN:
+ smp_call_function_single(cpu, kvm_guest_cpu_offline, NULL, 1);
+ break;
+ default:
+ break;
+ }
+ return NOTIFY_OK;
+}
+
+static struct notifier_block __cpuinitdata kvm_cpu_notifier = {
+ .notifier_call = kvm_cpu_notify,
+};
+#endif
+
+static void __init kvm_apf_trap_init(void)
+{
+ set_intr_gate(14, &async_page_fault);
+}
+
+void __init kvm_guest_init(void)
+{
+ int i;
+
+ if (!kvm_para_available())
+ return;
+
paravirt_ops_setup();
+ register_reboot_notifier(&kvm_pv_reboot_nb);
+ for (i = 0; i < KVM_TASK_SLEEP_HASHSIZE; i++)
+ spin_lock_init(&async_pf_sleepers[i].lock);
+ if (kvm_para_has_feature(KVM_FEATURE_ASYNC_PF))
+ x86_init.irqs.trap_init = kvm_apf_trap_init;
+
+#ifdef CONFIG_SMP
+ smp_ops.smp_prepare_boot_cpu = kvm_smp_prepare_boot_cpu;
+ register_cpu_notifier(&kvm_cpu_notifier);
+#else
+ kvm_guest_cpu_init();
+#endif
}
diff --git a/arch/x86/kernel/kvmclock.c b/arch/x86/kernel/kvmclock.c
index ca43ce3..f98d3ea 100644
--- a/arch/x86/kernel/kvmclock.c
+++ b/arch/x86/kernel/kvmclock.c
@@ -125,7 +125,7 @@
.flags = CLOCK_SOURCE_IS_CONTINUOUS,
};
-static int kvm_register_clock(char *txt)
+int kvm_register_clock(char *txt)
{
int cpu = smp_processor_id();
int low, high, ret;
@@ -152,14 +152,6 @@
}
#endif
-#ifdef CONFIG_SMP
-static void __init kvm_smp_prepare_boot_cpu(void)
-{
- WARN_ON(kvm_register_clock("primary cpu clock"));
- native_smp_prepare_boot_cpu();
-}
-#endif
-
/*
* After the clock is registered, the host will keep writing to the
* registered memory location. If the guest happens to shutdown, this memory
@@ -206,9 +198,6 @@
x86_cpuinit.setup_percpu_clockev =
kvm_setup_secondary_clock;
#endif
-#ifdef CONFIG_SMP
- smp_ops.smp_prepare_boot_cpu = kvm_smp_prepare_boot_cpu;
-#endif
machine_ops.shutdown = kvm_shutdown;
#ifdef CONFIG_KEXEC
machine_ops.crash_shutdown = kvm_crash_shutdown;
diff --git a/arch/x86/kernel/rtc.c b/arch/x86/kernel/rtc.c
index 1cfbbfc..6f39cab 100644
--- a/arch/x86/kernel/rtc.c
+++ b/arch/x86/kernel/rtc.c
@@ -76,7 +76,7 @@
CMOS_WRITE(real_seconds, RTC_SECONDS);
CMOS_WRITE(real_minutes, RTC_MINUTES);
} else {
- printk(KERN_WARNING
+ printk_once(KERN_NOTICE
"set_rtc_mmss: can't update from %d to %d\n",
cmos_minutes, real_minutes);
retval = -1;
diff --git a/arch/x86/kvm/Kconfig b/arch/x86/kvm/Kconfig
index ddc131f..50f6364 100644
--- a/arch/x86/kvm/Kconfig
+++ b/arch/x86/kvm/Kconfig
@@ -28,6 +28,7 @@
select HAVE_KVM_IRQCHIP
select HAVE_KVM_EVENTFD
select KVM_APIC_ARCHITECTURE
+ select KVM_ASYNC_PF
select USER_RETURN_NOTIFIER
select KVM_MMIO
---help---
diff --git a/arch/x86/kvm/Makefile b/arch/x86/kvm/Makefile
index 31a7035..f15501f43 100644
--- a/arch/x86/kvm/Makefile
+++ b/arch/x86/kvm/Makefile
@@ -1,5 +1,5 @@
-EXTRA_CFLAGS += -Ivirt/kvm -Iarch/x86/kvm
+ccflags-y += -Ivirt/kvm -Iarch/x86/kvm
CFLAGS_x86.o := -I.
CFLAGS_svm.o := -I.
@@ -9,6 +9,7 @@
coalesced_mmio.o irq_comm.o eventfd.o \
assigned-dev.o)
kvm-$(CONFIG_IOMMU_API) += $(addprefix ../../../virt/kvm/, iommu.o)
+kvm-$(CONFIG_KVM_ASYNC_PF) += $(addprefix ../../../virt/kvm/, async_pf.o)
kvm-y += x86.o mmu.o emulate.o i8259.o irq.o lapic.o \
i8254.o timer.o
diff --git a/arch/x86/kvm/emulate.c b/arch/x86/kvm/emulate.c
index 38b6e8d..caf9667 100644
--- a/arch/x86/kvm/emulate.c
+++ b/arch/x86/kvm/emulate.c
@@ -20,16 +20,8 @@
* From: xen-unstable 10676:af9809f51f81a3c43f276f00c81a52ef558afda4
*/
-#ifndef __KERNEL__
-#include <stdio.h>
-#include <stdint.h>
-#include <public/xen.h>
-#define DPRINTF(_f, _a ...) printf(_f , ## _a)
-#else
#include <linux/kvm_host.h>
#include "kvm_cache_regs.h"
-#define DPRINTF(x...) do {} while (0)
-#endif
#include <linux/module.h>
#include <asm/kvm_emulate.h>
@@ -418,9 +410,9 @@
}
static inline unsigned long
-register_address(struct decode_cache *c, unsigned long base, unsigned long reg)
+register_address(struct decode_cache *c, unsigned long reg)
{
- return base + address_mask(c, reg);
+ return address_mask(c, reg);
}
static inline void
@@ -452,60 +444,55 @@
return ops->get_cached_segment_base(seg, ctxt->vcpu);
}
-static unsigned long seg_override_base(struct x86_emulate_ctxt *ctxt,
- struct x86_emulate_ops *ops,
- struct decode_cache *c)
+static unsigned seg_override(struct x86_emulate_ctxt *ctxt,
+ struct x86_emulate_ops *ops,
+ struct decode_cache *c)
{
if (!c->has_seg_override)
return 0;
- return seg_base(ctxt, ops, c->seg_override);
+ return c->seg_override;
}
-static unsigned long es_base(struct x86_emulate_ctxt *ctxt,
- struct x86_emulate_ops *ops)
+static ulong linear(struct x86_emulate_ctxt *ctxt,
+ struct segmented_address addr)
{
- return seg_base(ctxt, ops, VCPU_SREG_ES);
+ struct decode_cache *c = &ctxt->decode;
+ ulong la;
+
+ la = seg_base(ctxt, ctxt->ops, addr.seg) + addr.ea;
+ if (c->ad_bytes != 8)
+ la &= (u32)-1;
+ return la;
}
-static unsigned long ss_base(struct x86_emulate_ctxt *ctxt,
- struct x86_emulate_ops *ops)
+static int emulate_exception(struct x86_emulate_ctxt *ctxt, int vec,
+ u32 error, bool valid)
{
- return seg_base(ctxt, ops, VCPU_SREG_SS);
+ ctxt->exception.vector = vec;
+ ctxt->exception.error_code = error;
+ ctxt->exception.error_code_valid = valid;
+ return X86EMUL_PROPAGATE_FAULT;
}
-static void emulate_exception(struct x86_emulate_ctxt *ctxt, int vec,
- u32 error, bool valid)
+static int emulate_gp(struct x86_emulate_ctxt *ctxt, int err)
{
- ctxt->exception = vec;
- ctxt->error_code = error;
- ctxt->error_code_valid = valid;
+ return emulate_exception(ctxt, GP_VECTOR, err, true);
}
-static void emulate_gp(struct x86_emulate_ctxt *ctxt, int err)
+static int emulate_ud(struct x86_emulate_ctxt *ctxt)
{
- emulate_exception(ctxt, GP_VECTOR, err, true);
+ return emulate_exception(ctxt, UD_VECTOR, 0, false);
}
-static void emulate_pf(struct x86_emulate_ctxt *ctxt)
+static int emulate_ts(struct x86_emulate_ctxt *ctxt, int err)
{
- emulate_exception(ctxt, PF_VECTOR, 0, true);
-}
-
-static void emulate_ud(struct x86_emulate_ctxt *ctxt)
-{
- emulate_exception(ctxt, UD_VECTOR, 0, false);
-}
-
-static void emulate_ts(struct x86_emulate_ctxt *ctxt, int err)
-{
- emulate_exception(ctxt, TS_VECTOR, err, true);
+ return emulate_exception(ctxt, TS_VECTOR, err, true);
}
static int emulate_de(struct x86_emulate_ctxt *ctxt)
{
- emulate_exception(ctxt, DE_VECTOR, 0, false);
- return X86EMUL_PROPAGATE_FAULT;
+ return emulate_exception(ctxt, DE_VECTOR, 0, false);
}
static int do_fetch_insn_byte(struct x86_emulate_ctxt *ctxt,
@@ -520,7 +507,7 @@
cur_size = fc->end - fc->start;
size = min(15UL - cur_size, PAGE_SIZE - offset_in_page(eip));
rc = ops->fetch(ctxt->cs_base + eip, fc->data + cur_size,
- size, ctxt->vcpu, NULL);
+ size, ctxt->vcpu, &ctxt->exception);
if (rc != X86EMUL_CONTINUE)
return rc;
fc->end += size;
@@ -564,7 +551,7 @@
static int read_descriptor(struct x86_emulate_ctxt *ctxt,
struct x86_emulate_ops *ops,
- ulong addr,
+ struct segmented_address addr,
u16 *size, unsigned long *address, int op_bytes)
{
int rc;
@@ -572,10 +559,13 @@
if (op_bytes == 2)
op_bytes = 3;
*address = 0;
- rc = ops->read_std(addr, (unsigned long *)size, 2, ctxt->vcpu, NULL);
+ rc = ops->read_std(linear(ctxt, addr), (unsigned long *)size, 2,
+ ctxt->vcpu, &ctxt->exception);
if (rc != X86EMUL_CONTINUE)
return rc;
- rc = ops->read_std(addr + 2, address, op_bytes, ctxt->vcpu, NULL);
+ addr.ea += 2;
+ rc = ops->read_std(linear(ctxt, addr), address, op_bytes,
+ ctxt->vcpu, &ctxt->exception);
return rc;
}
@@ -768,7 +758,7 @@
break;
}
}
- op->addr.mem = modrm_ea;
+ op->addr.mem.ea = modrm_ea;
done:
return rc;
}
@@ -783,13 +773,13 @@
op->type = OP_MEM;
switch (c->ad_bytes) {
case 2:
- op->addr.mem = insn_fetch(u16, 2, c->eip);
+ op->addr.mem.ea = insn_fetch(u16, 2, c->eip);
break;
case 4:
- op->addr.mem = insn_fetch(u32, 4, c->eip);
+ op->addr.mem.ea = insn_fetch(u32, 4, c->eip);
break;
case 8:
- op->addr.mem = insn_fetch(u64, 8, c->eip);
+ op->addr.mem.ea = insn_fetch(u64, 8, c->eip);
break;
}
done:
@@ -808,7 +798,7 @@
else if (c->src.bytes == 4)
sv = (s32)c->src.val & (s32)mask;
- c->dst.addr.mem += (sv >> 3);
+ c->dst.addr.mem.ea += (sv >> 3);
}
/* only subword offset */
@@ -821,7 +811,6 @@
{
int rc;
struct read_cache *mc = &ctxt->decode.mem_read;
- u32 err;
while (size) {
int n = min(size, 8u);
@@ -829,10 +818,8 @@
if (mc->pos < mc->end)
goto read_cached;
- rc = ops->read_emulated(addr, mc->data + mc->end, n, &err,
- ctxt->vcpu);
- if (rc == X86EMUL_PROPAGATE_FAULT)
- emulate_pf(ctxt);
+ rc = ops->read_emulated(addr, mc->data + mc->end, n,
+ &ctxt->exception, ctxt->vcpu);
if (rc != X86EMUL_CONTINUE)
return rc;
mc->end += n;
@@ -907,19 +894,15 @@
struct desc_ptr dt;
u16 index = selector >> 3;
int ret;
- u32 err;
ulong addr;
get_descriptor_table_ptr(ctxt, ops, selector, &dt);
- if (dt.size < index * 8 + 7) {
- emulate_gp(ctxt, selector & 0xfffc);
- return X86EMUL_PROPAGATE_FAULT;
- }
+ if (dt.size < index * 8 + 7)
+ return emulate_gp(ctxt, selector & 0xfffc);
addr = dt.address + index * 8;
- ret = ops->read_std(addr, desc, sizeof *desc, ctxt->vcpu, &err);
- if (ret == X86EMUL_PROPAGATE_FAULT)
- emulate_pf(ctxt);
+ ret = ops->read_std(addr, desc, sizeof *desc, ctxt->vcpu,
+ &ctxt->exception);
return ret;
}
@@ -931,21 +914,17 @@
{
struct desc_ptr dt;
u16 index = selector >> 3;
- u32 err;
ulong addr;
int ret;
get_descriptor_table_ptr(ctxt, ops, selector, &dt);
- if (dt.size < index * 8 + 7) {
- emulate_gp(ctxt, selector & 0xfffc);
- return X86EMUL_PROPAGATE_FAULT;
- }
+ if (dt.size < index * 8 + 7)
+ return emulate_gp(ctxt, selector & 0xfffc);
addr = dt.address + index * 8;
- ret = ops->write_std(addr, desc, sizeof *desc, ctxt->vcpu, &err);
- if (ret == X86EMUL_PROPAGATE_FAULT)
- emulate_pf(ctxt);
+ ret = ops->write_std(addr, desc, sizeof *desc, ctxt->vcpu,
+ &ctxt->exception);
return ret;
}
@@ -1092,7 +1071,6 @@
{
int rc;
struct decode_cache *c = &ctxt->decode;
- u32 err;
switch (c->dst.type) {
case OP_REG:
@@ -1101,21 +1079,19 @@
case OP_MEM:
if (c->lock_prefix)
rc = ops->cmpxchg_emulated(
- c->dst.addr.mem,
+ linear(ctxt, c->dst.addr.mem),
&c->dst.orig_val,
&c->dst.val,
c->dst.bytes,
- &err,
+ &ctxt->exception,
ctxt->vcpu);
else
rc = ops->write_emulated(
- c->dst.addr.mem,
+ linear(ctxt, c->dst.addr.mem),
&c->dst.val,
c->dst.bytes,
- &err,
+ &ctxt->exception,
ctxt->vcpu);
- if (rc == X86EMUL_PROPAGATE_FAULT)
- emulate_pf(ctxt);
if (rc != X86EMUL_CONTINUE)
return rc;
break;
@@ -1137,8 +1113,8 @@
c->dst.bytes = c->op_bytes;
c->dst.val = c->src.val;
register_address_increment(c, &c->regs[VCPU_REGS_RSP], -c->op_bytes);
- c->dst.addr.mem = register_address(c, ss_base(ctxt, ops),
- c->regs[VCPU_REGS_RSP]);
+ c->dst.addr.mem.ea = register_address(c, c->regs[VCPU_REGS_RSP]);
+ c->dst.addr.mem.seg = VCPU_SREG_SS;
}
static int emulate_pop(struct x86_emulate_ctxt *ctxt,
@@ -1147,10 +1123,11 @@
{
struct decode_cache *c = &ctxt->decode;
int rc;
+ struct segmented_address addr;
- rc = read_emulated(ctxt, ops, register_address(c, ss_base(ctxt, ops),
- c->regs[VCPU_REGS_RSP]),
- dest, len);
+ addr.ea = register_address(c, c->regs[VCPU_REGS_RSP]);
+ addr.seg = VCPU_SREG_SS;
+ rc = read_emulated(ctxt, ops, linear(ctxt, addr), dest, len);
if (rc != X86EMUL_CONTINUE)
return rc;
@@ -1184,10 +1161,8 @@
change_mask |= EFLG_IF;
break;
case X86EMUL_MODE_VM86:
- if (iopl < 3) {
- emulate_gp(ctxt, 0);
- return X86EMUL_PROPAGATE_FAULT;
- }
+ if (iopl < 3)
+ return emulate_gp(ctxt, 0);
change_mask |= EFLG_IF;
break;
default: /* real mode */
@@ -1198,9 +1173,6 @@
*(unsigned long *)dest =
(ctxt->eflags & ~change_mask) | (val & change_mask);
- if (rc == X86EMUL_PROPAGATE_FAULT)
- emulate_pf(ctxt);
-
return rc;
}
@@ -1287,7 +1259,6 @@
gva_t cs_addr;
gva_t eip_addr;
u16 cs, eip;
- u32 err;
/* TODO: Add limit checks */
c->src.val = ctxt->eflags;
@@ -1317,11 +1288,11 @@
eip_addr = dt.address + (irq << 2);
cs_addr = dt.address + (irq << 2) + 2;
- rc = ops->read_std(cs_addr, &cs, 2, ctxt->vcpu, &err);
+ rc = ops->read_std(cs_addr, &cs, 2, ctxt->vcpu, &ctxt->exception);
if (rc != X86EMUL_CONTINUE)
return rc;
- rc = ops->read_std(eip_addr, &eip, 2, ctxt->vcpu, &err);
+ rc = ops->read_std(eip_addr, &eip, 2, ctxt->vcpu, &ctxt->exception);
if (rc != X86EMUL_CONTINUE)
return rc;
@@ -1370,10 +1341,8 @@
if (rc != X86EMUL_CONTINUE)
return rc;
- if (temp_eip & ~0xffff) {
- emulate_gp(ctxt, 0);
- return X86EMUL_PROPAGATE_FAULT;
- }
+ if (temp_eip & ~0xffff)
+ return emulate_gp(ctxt, 0);
rc = emulate_pop(ctxt, ops, &cs, c->op_bytes);
@@ -1624,10 +1593,8 @@
/* syscall is not available in real mode */
if (ctxt->mode == X86EMUL_MODE_REAL ||
- ctxt->mode == X86EMUL_MODE_VM86) {
- emulate_ud(ctxt);
- return X86EMUL_PROPAGATE_FAULT;
- }
+ ctxt->mode == X86EMUL_MODE_VM86)
+ return emulate_ud(ctxt);
setup_syscalls_segments(ctxt, ops, &cs, &ss);
@@ -1678,34 +1645,26 @@
u16 cs_sel, ss_sel;
/* inject #GP if in real mode */
- if (ctxt->mode == X86EMUL_MODE_REAL) {
- emulate_gp(ctxt, 0);
- return X86EMUL_PROPAGATE_FAULT;
- }
+ if (ctxt->mode == X86EMUL_MODE_REAL)
+ return emulate_gp(ctxt, 0);
/* XXX sysenter/sysexit have not been tested in 64bit mode.
* Therefore, we inject an #UD.
*/
- if (ctxt->mode == X86EMUL_MODE_PROT64) {
- emulate_ud(ctxt);
- return X86EMUL_PROPAGATE_FAULT;
- }
+ if (ctxt->mode == X86EMUL_MODE_PROT64)
+ return emulate_ud(ctxt);
setup_syscalls_segments(ctxt, ops, &cs, &ss);
ops->get_msr(ctxt->vcpu, MSR_IA32_SYSENTER_CS, &msr_data);
switch (ctxt->mode) {
case X86EMUL_MODE_PROT32:
- if ((msr_data & 0xfffc) == 0x0) {
- emulate_gp(ctxt, 0);
- return X86EMUL_PROPAGATE_FAULT;
- }
+ if ((msr_data & 0xfffc) == 0x0)
+ return emulate_gp(ctxt, 0);
break;
case X86EMUL_MODE_PROT64:
- if (msr_data == 0x0) {
- emulate_gp(ctxt, 0);
- return X86EMUL_PROPAGATE_FAULT;
- }
+ if (msr_data == 0x0)
+ return emulate_gp(ctxt, 0);
break;
}
@@ -1745,10 +1704,8 @@
/* inject #GP if in real mode or Virtual 8086 mode */
if (ctxt->mode == X86EMUL_MODE_REAL ||
- ctxt->mode == X86EMUL_MODE_VM86) {
- emulate_gp(ctxt, 0);
- return X86EMUL_PROPAGATE_FAULT;
- }
+ ctxt->mode == X86EMUL_MODE_VM86)
+ return emulate_gp(ctxt, 0);
setup_syscalls_segments(ctxt, ops, &cs, &ss);
@@ -1763,18 +1720,14 @@
switch (usermode) {
case X86EMUL_MODE_PROT32:
cs_sel = (u16)(msr_data + 16);
- if ((msr_data & 0xfffc) == 0x0) {
- emulate_gp(ctxt, 0);
- return X86EMUL_PROPAGATE_FAULT;
- }
+ if ((msr_data & 0xfffc) == 0x0)
+ return emulate_gp(ctxt, 0);
ss_sel = (u16)(msr_data + 24);
break;
case X86EMUL_MODE_PROT64:
cs_sel = (u16)(msr_data + 32);
- if (msr_data == 0x0) {
- emulate_gp(ctxt, 0);
- return X86EMUL_PROPAGATE_FAULT;
- }
+ if (msr_data == 0x0)
+ return emulate_gp(ctxt, 0);
ss_sel = cs_sel + 8;
cs.d = 0;
cs.l = 1;
@@ -1934,33 +1887,27 @@
{
struct tss_segment_16 tss_seg;
int ret;
- u32 err, new_tss_base = get_desc_base(new_desc);
+ u32 new_tss_base = get_desc_base(new_desc);
ret = ops->read_std(old_tss_base, &tss_seg, sizeof tss_seg, ctxt->vcpu,
- &err);
- if (ret == X86EMUL_PROPAGATE_FAULT) {
+ &ctxt->exception);
+ if (ret != X86EMUL_CONTINUE)
/* FIXME: need to provide precise fault address */
- emulate_pf(ctxt);
return ret;
- }
save_state_to_tss16(ctxt, ops, &tss_seg);
ret = ops->write_std(old_tss_base, &tss_seg, sizeof tss_seg, ctxt->vcpu,
- &err);
- if (ret == X86EMUL_PROPAGATE_FAULT) {
+ &ctxt->exception);
+ if (ret != X86EMUL_CONTINUE)
/* FIXME: need to provide precise fault address */
- emulate_pf(ctxt);
return ret;
- }
ret = ops->read_std(new_tss_base, &tss_seg, sizeof tss_seg, ctxt->vcpu,
- &err);
- if (ret == X86EMUL_PROPAGATE_FAULT) {
+ &ctxt->exception);
+ if (ret != X86EMUL_CONTINUE)
/* FIXME: need to provide precise fault address */
- emulate_pf(ctxt);
return ret;
- }
if (old_tss_sel != 0xffff) {
tss_seg.prev_task_link = old_tss_sel;
@@ -1968,12 +1915,10 @@
ret = ops->write_std(new_tss_base,
&tss_seg.prev_task_link,
sizeof tss_seg.prev_task_link,
- ctxt->vcpu, &err);
- if (ret == X86EMUL_PROPAGATE_FAULT) {
+ ctxt->vcpu, &ctxt->exception);
+ if (ret != X86EMUL_CONTINUE)
/* FIXME: need to provide precise fault address */
- emulate_pf(ctxt);
return ret;
- }
}
return load_state_from_tss16(ctxt, ops, &tss_seg);
@@ -2013,10 +1958,8 @@
struct decode_cache *c = &ctxt->decode;
int ret;
- if (ops->set_cr(3, tss->cr3, ctxt->vcpu)) {
- emulate_gp(ctxt, 0);
- return X86EMUL_PROPAGATE_FAULT;
- }
+ if (ops->set_cr(3, tss->cr3, ctxt->vcpu))
+ return emulate_gp(ctxt, 0);
c->eip = tss->eip;
ctxt->eflags = tss->eflags | 2;
c->regs[VCPU_REGS_RAX] = tss->eax;
@@ -2076,33 +2019,27 @@
{
struct tss_segment_32 tss_seg;
int ret;
- u32 err, new_tss_base = get_desc_base(new_desc);
+ u32 new_tss_base = get_desc_base(new_desc);
ret = ops->read_std(old_tss_base, &tss_seg, sizeof tss_seg, ctxt->vcpu,
- &err);
- if (ret == X86EMUL_PROPAGATE_FAULT) {
+ &ctxt->exception);
+ if (ret != X86EMUL_CONTINUE)
/* FIXME: need to provide precise fault address */
- emulate_pf(ctxt);
return ret;
- }
save_state_to_tss32(ctxt, ops, &tss_seg);
ret = ops->write_std(old_tss_base, &tss_seg, sizeof tss_seg, ctxt->vcpu,
- &err);
- if (ret == X86EMUL_PROPAGATE_FAULT) {
+ &ctxt->exception);
+ if (ret != X86EMUL_CONTINUE)
/* FIXME: need to provide precise fault address */
- emulate_pf(ctxt);
return ret;
- }
ret = ops->read_std(new_tss_base, &tss_seg, sizeof tss_seg, ctxt->vcpu,
- &err);
- if (ret == X86EMUL_PROPAGATE_FAULT) {
+ &ctxt->exception);
+ if (ret != X86EMUL_CONTINUE)
/* FIXME: need to provide precise fault address */
- emulate_pf(ctxt);
return ret;
- }
if (old_tss_sel != 0xffff) {
tss_seg.prev_task_link = old_tss_sel;
@@ -2110,12 +2047,10 @@
ret = ops->write_std(new_tss_base,
&tss_seg.prev_task_link,
sizeof tss_seg.prev_task_link,
- ctxt->vcpu, &err);
- if (ret == X86EMUL_PROPAGATE_FAULT) {
+ ctxt->vcpu, &ctxt->exception);
+ if (ret != X86EMUL_CONTINUE)
/* FIXME: need to provide precise fault address */
- emulate_pf(ctxt);
return ret;
- }
}
return load_state_from_tss32(ctxt, ops, &tss_seg);
@@ -2146,10 +2081,8 @@
if (reason != TASK_SWITCH_IRET) {
if ((tss_selector & 3) > next_tss_desc.dpl ||
- ops->cpl(ctxt->vcpu) > next_tss_desc.dpl) {
- emulate_gp(ctxt, 0);
- return X86EMUL_PROPAGATE_FAULT;
- }
+ ops->cpl(ctxt->vcpu) > next_tss_desc.dpl)
+ return emulate_gp(ctxt, 0);
}
desc_limit = desc_limit_scaled(&next_tss_desc);
@@ -2231,14 +2164,15 @@
return (rc == X86EMUL_UNHANDLEABLE) ? -1 : 0;
}
-static void string_addr_inc(struct x86_emulate_ctxt *ctxt, unsigned long base,
+static void string_addr_inc(struct x86_emulate_ctxt *ctxt, unsigned seg,
int reg, struct operand *op)
{
struct decode_cache *c = &ctxt->decode;
int df = (ctxt->eflags & EFLG_DF) ? -1 : 1;
register_address_increment(c, &c->regs[reg], df * op->bytes);
- op->addr.mem = register_address(c, base, c->regs[reg]);
+ op->addr.mem.ea = register_address(c, c->regs[reg]);
+ op->addr.mem.seg = seg;
}
static int em_push(struct x86_emulate_ctxt *ctxt)
@@ -2369,10 +2303,8 @@
struct decode_cache *c = &ctxt->decode;
u64 tsc = 0;
- if (cpl > 0 && (ctxt->ops->get_cr(4, ctxt->vcpu) & X86_CR4_TSD)) {
- emulate_gp(ctxt, 0);
- return X86EMUL_PROPAGATE_FAULT;
- }
+ if (cpl > 0 && (ctxt->ops->get_cr(4, ctxt->vcpu) & X86_CR4_TSD))
+ return emulate_gp(ctxt, 0);
ctxt->ops->get_msr(ctxt->vcpu, MSR_IA32_TSC, &tsc);
c->regs[VCPU_REGS_RAX] = (u32)tsc;
c->regs[VCPU_REGS_RDX] = tsc >> 32;
@@ -2647,7 +2579,7 @@
op->type = OP_IMM;
op->bytes = size;
- op->addr.mem = c->eip;
+ op->addr.mem.ea = c->eip;
/* NB. Immediates are sign-extended as necessary. */
switch (op->bytes) {
case 1:
@@ -2678,7 +2610,7 @@
}
int
-x86_decode_insn(struct x86_emulate_ctxt *ctxt)
+x86_decode_insn(struct x86_emulate_ctxt *ctxt, void *insn, int insn_len)
{
struct x86_emulate_ops *ops = ctxt->ops;
struct decode_cache *c = &ctxt->decode;
@@ -2689,7 +2621,10 @@
struct operand memop = { .type = OP_NONE };
c->eip = ctxt->eip;
- c->fetch.start = c->fetch.end = c->eip;
+ c->fetch.start = c->eip;
+ c->fetch.end = c->fetch.start + insn_len;
+ if (insn_len > 0)
+ memcpy(c->fetch.data, insn, insn_len);
ctxt->cs_base = seg_base(ctxt, ops, VCPU_SREG_CS);
switch (mode) {
@@ -2803,10 +2738,8 @@
c->execute = opcode.u.execute;
/* Unrecognised? */
- if (c->d == 0 || (c->d & Undefined)) {
- DPRINTF("Cannot emulate %02x\n", c->b);
+ if (c->d == 0 || (c->d & Undefined))
return -1;
- }
if (mode == X86EMUL_MODE_PROT64 && (c->d & Stack))
c->op_bytes = 8;
@@ -2831,14 +2764,13 @@
if (!c->has_seg_override)
set_seg_override(c, VCPU_SREG_DS);
- if (memop.type == OP_MEM && !(!c->twobyte && c->b == 0x8d))
- memop.addr.mem += seg_override_base(ctxt, ops, c);
+ memop.addr.mem.seg = seg_override(ctxt, ops, c);
if (memop.type == OP_MEM && c->ad_bytes != 8)
- memop.addr.mem = (u32)memop.addr.mem;
+ memop.addr.mem.ea = (u32)memop.addr.mem.ea;
if (memop.type == OP_MEM && c->rip_relative)
- memop.addr.mem += c->eip;
+ memop.addr.mem.ea += c->eip;
/*
* Decode and fetch the source operand: register, memory
@@ -2890,14 +2822,14 @@
case SrcSI:
c->src.type = OP_MEM;
c->src.bytes = (c->d & ByteOp) ? 1 : c->op_bytes;
- c->src.addr.mem =
- register_address(c, seg_override_base(ctxt, ops, c),
- c->regs[VCPU_REGS_RSI]);
+ c->src.addr.mem.ea =
+ register_address(c, c->regs[VCPU_REGS_RSI]);
+ c->src.addr.mem.seg = seg_override(ctxt, ops, c),
c->src.val = 0;
break;
case SrcImmFAddr:
c->src.type = OP_IMM;
- c->src.addr.mem = c->eip;
+ c->src.addr.mem.ea = c->eip;
c->src.bytes = c->op_bytes + 2;
insn_fetch_arr(c->src.valptr, c->src.bytes, c->eip);
break;
@@ -2944,7 +2876,7 @@
break;
case DstImmUByte:
c->dst.type = OP_IMM;
- c->dst.addr.mem = c->eip;
+ c->dst.addr.mem.ea = c->eip;
c->dst.bytes = 1;
c->dst.val = insn_fetch(u8, 1, c->eip);
break;
@@ -2969,9 +2901,9 @@
case DstDI:
c->dst.type = OP_MEM;
c->dst.bytes = (c->d & ByteOp) ? 1 : c->op_bytes;
- c->dst.addr.mem =
- register_address(c, es_base(ctxt, ops),
- c->regs[VCPU_REGS_RDI]);
+ c->dst.addr.mem.ea =
+ register_address(c, c->regs[VCPU_REGS_RDI]);
+ c->dst.addr.mem.seg = VCPU_SREG_ES;
c->dst.val = 0;
break;
case ImplicitOps:
@@ -3020,24 +2952,24 @@
ctxt->decode.mem_read.pos = 0;
if (ctxt->mode == X86EMUL_MODE_PROT64 && (c->d & No64)) {
- emulate_ud(ctxt);
+ rc = emulate_ud(ctxt);
goto done;
}
/* LOCK prefix is allowed only with some instructions */
if (c->lock_prefix && (!(c->d & Lock) || c->dst.type != OP_MEM)) {
- emulate_ud(ctxt);
+ rc = emulate_ud(ctxt);
goto done;
}
if ((c->d & SrcMask) == SrcMemFAddr && c->src.type != OP_MEM) {
- emulate_ud(ctxt);
+ rc = emulate_ud(ctxt);
goto done;
}
/* Privileged instruction can be executed only in CPL=0 */
if ((c->d & Priv) && ops->cpl(ctxt->vcpu)) {
- emulate_gp(ctxt, 0);
+ rc = emulate_gp(ctxt, 0);
goto done;
}
@@ -3050,7 +2982,7 @@
}
if ((c->src.type == OP_MEM) && !(c->d & NoAccess)) {
- rc = read_emulated(ctxt, ops, c->src.addr.mem,
+ rc = read_emulated(ctxt, ops, linear(ctxt, c->src.addr.mem),
c->src.valptr, c->src.bytes);
if (rc != X86EMUL_CONTINUE)
goto done;
@@ -3058,7 +2990,7 @@
}
if (c->src2.type == OP_MEM) {
- rc = read_emulated(ctxt, ops, c->src2.addr.mem,
+ rc = read_emulated(ctxt, ops, linear(ctxt, c->src2.addr.mem),
&c->src2.val, c->src2.bytes);
if (rc != X86EMUL_CONTINUE)
goto done;
@@ -3070,7 +3002,7 @@
if ((c->dst.type == OP_MEM) && !(c->d & Mov)) {
/* optimisation - avoid slow emulated read if Mov */
- rc = read_emulated(ctxt, ops, c->dst.addr.mem,
+ rc = read_emulated(ctxt, ops, linear(ctxt, c->dst.addr.mem),
&c->dst.val, c->dst.bytes);
if (rc != X86EMUL_CONTINUE)
goto done;
@@ -3215,13 +3147,13 @@
break;
case 0x8c: /* mov r/m, sreg */
if (c->modrm_reg > VCPU_SREG_GS) {
- emulate_ud(ctxt);
+ rc = emulate_ud(ctxt);
goto done;
}
c->dst.val = ops->get_segment_selector(c->modrm_reg, ctxt->vcpu);
break;
case 0x8d: /* lea r16/r32, m */
- c->dst.val = c->src.addr.mem;
+ c->dst.val = c->src.addr.mem.ea;
break;
case 0x8e: { /* mov seg, r/m16 */
uint16_t sel;
@@ -3230,7 +3162,7 @@
if (c->modrm_reg == VCPU_SREG_CS ||
c->modrm_reg > VCPU_SREG_GS) {
- emulate_ud(ctxt);
+ rc = emulate_ud(ctxt);
goto done;
}
@@ -3268,7 +3200,6 @@
break;
case 0xa6 ... 0xa7: /* cmps */
c->dst.type = OP_NONE; /* Disable writeback. */
- DPRINTF("cmps: mem1=0x%p mem2=0x%p\n", c->src.addr.mem, c->dst.addr.mem);
goto cmp;
case 0xa8 ... 0xa9: /* test ax, imm */
goto test;
@@ -3363,7 +3294,7 @@
do_io_in:
c->dst.bytes = min(c->dst.bytes, 4u);
if (!emulator_io_permited(ctxt, ops, c->src.val, c->dst.bytes)) {
- emulate_gp(ctxt, 0);
+ rc = emulate_gp(ctxt, 0);
goto done;
}
if (!pio_in_emulated(ctxt, ops, c->dst.bytes, c->src.val,
@@ -3377,7 +3308,7 @@
c->src.bytes = min(c->src.bytes, 4u);
if (!emulator_io_permited(ctxt, ops, c->dst.val,
c->src.bytes)) {
- emulate_gp(ctxt, 0);
+ rc = emulate_gp(ctxt, 0);
goto done;
}
ops->pio_out_emulated(c->src.bytes, c->dst.val,
@@ -3402,14 +3333,14 @@
break;
case 0xfa: /* cli */
if (emulator_bad_iopl(ctxt, ops)) {
- emulate_gp(ctxt, 0);
+ rc = emulate_gp(ctxt, 0);
goto done;
} else
ctxt->eflags &= ~X86_EFLAGS_IF;
break;
case 0xfb: /* sti */
if (emulator_bad_iopl(ctxt, ops)) {
- emulate_gp(ctxt, 0);
+ rc = emulate_gp(ctxt, 0);
goto done;
} else {
ctxt->interruptibility = KVM_X86_SHADOW_INT_STI;
@@ -3449,11 +3380,11 @@
c->dst.type = saved_dst_type;
if ((c->d & SrcMask) == SrcSI)
- string_addr_inc(ctxt, seg_override_base(ctxt, ops, c),
+ string_addr_inc(ctxt, seg_override(ctxt, ops, c),
VCPU_REGS_RSI, &c->src);
if ((c->d & DstMask) == DstDI)
- string_addr_inc(ctxt, es_base(ctxt, ops), VCPU_REGS_RDI,
+ string_addr_inc(ctxt, VCPU_SREG_ES, VCPU_REGS_RDI,
&c->dst);
if (c->rep_prefix && (c->d & String)) {
@@ -3482,6 +3413,8 @@
ctxt->eip = c->eip;
done:
+ if (rc == X86EMUL_PROPAGATE_FAULT)
+ ctxt->have_exception = true;
return (rc == X86EMUL_UNHANDLEABLE) ? EMULATION_FAILED : EMULATION_OK;
twobyte_insn:
@@ -3544,9 +3477,11 @@
break;
case 5: /* not defined */
emulate_ud(ctxt);
+ rc = X86EMUL_PROPAGATE_FAULT;
goto done;
case 7: /* invlpg*/
- emulate_invlpg(ctxt->vcpu, c->src.addr.mem);
+ emulate_invlpg(ctxt->vcpu,
+ linear(ctxt, c->src.addr.mem));
/* Disable writeback. */
c->dst.type = OP_NONE;
break;
@@ -3573,6 +3508,7 @@
case 5 ... 7:
case 9 ... 15:
emulate_ud(ctxt);
+ rc = X86EMUL_PROPAGATE_FAULT;
goto done;
}
c->dst.val = ops->get_cr(c->modrm_reg, ctxt->vcpu);
@@ -3581,6 +3517,7 @@
if ((ops->get_cr(4, ctxt->vcpu) & X86_CR4_DE) &&
(c->modrm_reg == 4 || c->modrm_reg == 5)) {
emulate_ud(ctxt);
+ rc = X86EMUL_PROPAGATE_FAULT;
goto done;
}
ops->get_dr(c->modrm_reg, &c->dst.val, ctxt->vcpu);
@@ -3588,6 +3525,7 @@
case 0x22: /* mov reg, cr */
if (ops->set_cr(c->modrm_reg, c->src.val, ctxt->vcpu)) {
emulate_gp(ctxt, 0);
+ rc = X86EMUL_PROPAGATE_FAULT;
goto done;
}
c->dst.type = OP_NONE;
@@ -3596,6 +3534,7 @@
if ((ops->get_cr(4, ctxt->vcpu) & X86_CR4_DE) &&
(c->modrm_reg == 4 || c->modrm_reg == 5)) {
emulate_ud(ctxt);
+ rc = X86EMUL_PROPAGATE_FAULT;
goto done;
}
@@ -3604,6 +3543,7 @@
~0ULL : ~0U), ctxt->vcpu) < 0) {
/* #UD condition is already handled by the code above */
emulate_gp(ctxt, 0);
+ rc = X86EMUL_PROPAGATE_FAULT;
goto done;
}
@@ -3615,6 +3555,7 @@
| ((u64)c->regs[VCPU_REGS_RDX] << 32);
if (ops->set_msr(ctxt->vcpu, c->regs[VCPU_REGS_RCX], msr_data)) {
emulate_gp(ctxt, 0);
+ rc = X86EMUL_PROPAGATE_FAULT;
goto done;
}
rc = X86EMUL_CONTINUE;
@@ -3623,6 +3564,7 @@
/* rdmsr */
if (ops->get_msr(ctxt->vcpu, c->regs[VCPU_REGS_RCX], &msr_data)) {
emulate_gp(ctxt, 0);
+ rc = X86EMUL_PROPAGATE_FAULT;
goto done;
} else {
c->regs[VCPU_REGS_RAX] = (u32)msr_data;
@@ -3785,6 +3727,5 @@
goto writeback;
cannot_emulate:
- DPRINTF("Cannot emulate %02x\n", c->b);
return -1;
}
diff --git a/arch/x86/kvm/kvm_cache_regs.h b/arch/x86/kvm/kvm_cache_regs.h
index 975bb45..3377d53 100644
--- a/arch/x86/kvm/kvm_cache_regs.h
+++ b/arch/x86/kvm/kvm_cache_regs.h
@@ -73,6 +73,13 @@
return vcpu->arch.cr4 & mask;
}
+static inline ulong kvm_read_cr3(struct kvm_vcpu *vcpu)
+{
+ if (!test_bit(VCPU_EXREG_CR3, (ulong *)&vcpu->arch.regs_avail))
+ kvm_x86_ops->decache_cr3(vcpu);
+ return vcpu->arch.cr3;
+}
+
static inline ulong kvm_read_cr4(struct kvm_vcpu *vcpu)
{
return kvm_read_cr4_bits(vcpu, ~0UL);
@@ -84,4 +91,19 @@
| ((u64)(kvm_register_read(vcpu, VCPU_REGS_RDX) & -1u) << 32);
}
+static inline void enter_guest_mode(struct kvm_vcpu *vcpu)
+{
+ vcpu->arch.hflags |= HF_GUEST_MASK;
+}
+
+static inline void leave_guest_mode(struct kvm_vcpu *vcpu)
+{
+ vcpu->arch.hflags &= ~HF_GUEST_MASK;
+}
+
+static inline bool is_guest_mode(struct kvm_vcpu *vcpu)
+{
+ return vcpu->arch.hflags & HF_GUEST_MASK;
+}
+
#endif
diff --git a/arch/x86/kvm/lapic.c b/arch/x86/kvm/lapic.c
index 413f897..93cf9d0 100644
--- a/arch/x86/kvm/lapic.c
+++ b/arch/x86/kvm/lapic.c
@@ -277,7 +277,8 @@
if (old_ppr != ppr) {
apic_set_reg(apic, APIC_PROCPRI, ppr);
- kvm_make_request(KVM_REQ_EVENT, apic->vcpu);
+ if (ppr < old_ppr)
+ kvm_make_request(KVM_REQ_EVENT, apic->vcpu);
}
}
diff --git a/arch/x86/kvm/mmu.c b/arch/x86/kvm/mmu.c
index fbb04ae..9cafbb4 100644
--- a/arch/x86/kvm/mmu.c
+++ b/arch/x86/kvm/mmu.c
@@ -18,9 +18,11 @@
*
*/
+#include "irq.h"
#include "mmu.h"
#include "x86.h"
#include "kvm_cache_regs.h"
+#include "x86.h"
#include <linux/kvm_host.h>
#include <linux/types.h>
@@ -194,7 +196,6 @@
static u64 __read_mostly shadow_trap_nonpresent_pte;
static u64 __read_mostly shadow_notrap_nonpresent_pte;
-static u64 __read_mostly shadow_base_present_pte;
static u64 __read_mostly shadow_nx_mask;
static u64 __read_mostly shadow_x_mask; /* mutual exclusive with nx_mask */
static u64 __read_mostly shadow_user_mask;
@@ -213,12 +214,6 @@
}
EXPORT_SYMBOL_GPL(kvm_mmu_set_nonpresent_ptes);
-void kvm_mmu_set_base_ptes(u64 base_pte)
-{
- shadow_base_present_pte = base_pte;
-}
-EXPORT_SYMBOL_GPL(kvm_mmu_set_base_ptes);
-
void kvm_mmu_set_mask_ptes(u64 user_mask, u64 accessed_mask,
u64 dirty_mask, u64 nx_mask, u64 x_mask)
{
@@ -482,46 +477,46 @@
}
/*
- * Return the pointer to the largepage write count for a given
- * gfn, handling slots that are not large page aligned.
+ * Return the pointer to the large page information for a given gfn,
+ * handling slots that are not large page aligned.
*/
-static int *slot_largepage_idx(gfn_t gfn,
- struct kvm_memory_slot *slot,
- int level)
+static struct kvm_lpage_info *lpage_info_slot(gfn_t gfn,
+ struct kvm_memory_slot *slot,
+ int level)
{
unsigned long idx;
idx = (gfn >> KVM_HPAGE_GFN_SHIFT(level)) -
(slot->base_gfn >> KVM_HPAGE_GFN_SHIFT(level));
- return &slot->lpage_info[level - 2][idx].write_count;
+ return &slot->lpage_info[level - 2][idx];
}
static void account_shadowed(struct kvm *kvm, gfn_t gfn)
{
struct kvm_memory_slot *slot;
- int *write_count;
+ struct kvm_lpage_info *linfo;
int i;
slot = gfn_to_memslot(kvm, gfn);
for (i = PT_DIRECTORY_LEVEL;
i < PT_PAGE_TABLE_LEVEL + KVM_NR_PAGE_SIZES; ++i) {
- write_count = slot_largepage_idx(gfn, slot, i);
- *write_count += 1;
+ linfo = lpage_info_slot(gfn, slot, i);
+ linfo->write_count += 1;
}
}
static void unaccount_shadowed(struct kvm *kvm, gfn_t gfn)
{
struct kvm_memory_slot *slot;
- int *write_count;
+ struct kvm_lpage_info *linfo;
int i;
slot = gfn_to_memslot(kvm, gfn);
for (i = PT_DIRECTORY_LEVEL;
i < PT_PAGE_TABLE_LEVEL + KVM_NR_PAGE_SIZES; ++i) {
- write_count = slot_largepage_idx(gfn, slot, i);
- *write_count -= 1;
- WARN_ON(*write_count < 0);
+ linfo = lpage_info_slot(gfn, slot, i);
+ linfo->write_count -= 1;
+ WARN_ON(linfo->write_count < 0);
}
}
@@ -530,12 +525,12 @@
int level)
{
struct kvm_memory_slot *slot;
- int *largepage_idx;
+ struct kvm_lpage_info *linfo;
slot = gfn_to_memslot(kvm, gfn);
if (slot) {
- largepage_idx = slot_largepage_idx(gfn, slot, level);
- return *largepage_idx;
+ linfo = lpage_info_slot(gfn, slot, level);
+ return linfo->write_count;
}
return 1;
@@ -590,16 +585,15 @@
static unsigned long *gfn_to_rmap(struct kvm *kvm, gfn_t gfn, int level)
{
struct kvm_memory_slot *slot;
- unsigned long idx;
+ struct kvm_lpage_info *linfo;
slot = gfn_to_memslot(kvm, gfn);
if (likely(level == PT_PAGE_TABLE_LEVEL))
return &slot->rmap[gfn - slot->base_gfn];
- idx = (gfn >> KVM_HPAGE_GFN_SHIFT(level)) -
- (slot->base_gfn >> KVM_HPAGE_GFN_SHIFT(level));
+ linfo = lpage_info_slot(gfn, slot, level);
- return &slot->lpage_info[level - 2][idx].rmap_pde;
+ return &linfo->rmap_pde;
}
/*
@@ -887,19 +881,16 @@
end = start + (memslot->npages << PAGE_SHIFT);
if (hva >= start && hva < end) {
gfn_t gfn_offset = (hva - start) >> PAGE_SHIFT;
+ gfn_t gfn = memslot->base_gfn + gfn_offset;
ret = handler(kvm, &memslot->rmap[gfn_offset], data);
for (j = 0; j < KVM_NR_PAGE_SIZES - 1; ++j) {
- unsigned long idx;
- int sh;
+ struct kvm_lpage_info *linfo;
- sh = KVM_HPAGE_GFN_SHIFT(PT_DIRECTORY_LEVEL+j);
- idx = ((memslot->base_gfn+gfn_offset) >> sh) -
- (memslot->base_gfn >> sh);
- ret |= handler(kvm,
- &memslot->lpage_info[j][idx].rmap_pde,
- data);
+ linfo = lpage_info_slot(gfn, memslot,
+ PT_DIRECTORY_LEVEL + j);
+ ret |= handler(kvm, &linfo->rmap_pde, data);
}
trace_kvm_age_page(hva, memslot, ret);
retval |= ret;
@@ -1161,7 +1152,7 @@
}
static int nonpaging_sync_page(struct kvm_vcpu *vcpu,
- struct kvm_mmu_page *sp, bool clear_unsync)
+ struct kvm_mmu_page *sp)
{
return 1;
}
@@ -1291,7 +1282,7 @@
if (clear_unsync)
kvm_unlink_unsync_page(vcpu->kvm, sp);
- if (vcpu->arch.mmu.sync_page(vcpu, sp, clear_unsync)) {
+ if (vcpu->arch.mmu.sync_page(vcpu, sp)) {
kvm_mmu_prepare_zap_page(vcpu->kvm, sp, invalid_list);
return 1;
}
@@ -1332,12 +1323,12 @@
continue;
WARN_ON(s->role.level != PT_PAGE_TABLE_LEVEL);
+ kvm_unlink_unsync_page(vcpu->kvm, s);
if ((s->role.cr4_pae != !!is_pae(vcpu)) ||
- (vcpu->arch.mmu.sync_page(vcpu, s, true))) {
+ (vcpu->arch.mmu.sync_page(vcpu, s))) {
kvm_mmu_prepare_zap_page(vcpu->kvm, s, &invalid_list);
continue;
}
- kvm_unlink_unsync_page(vcpu->kvm, s);
flush = true;
}
@@ -1963,9 +1954,9 @@
unsigned pte_access, int user_fault,
int write_fault, int dirty, int level,
gfn_t gfn, pfn_t pfn, bool speculative,
- bool can_unsync, bool reset_host_protection)
+ bool can_unsync, bool host_writable)
{
- u64 spte;
+ u64 spte, entry = *sptep;
int ret = 0;
/*
@@ -1973,7 +1964,7 @@
* whether the guest actually used the pte (in order to detect
* demand paging).
*/
- spte = shadow_base_present_pte;
+ spte = PT_PRESENT_MASK;
if (!speculative)
spte |= shadow_accessed_mask;
if (!dirty)
@@ -1990,8 +1981,10 @@
spte |= kvm_x86_ops->get_mt_mask(vcpu, gfn,
kvm_is_mmio_pfn(pfn));
- if (reset_host_protection)
+ if (host_writable)
spte |= SPTE_HOST_WRITEABLE;
+ else
+ pte_access &= ~ACC_WRITE_MASK;
spte |= (u64)pfn << PAGE_SHIFT;
@@ -2036,6 +2029,14 @@
set_pte:
update_spte(sptep, spte);
+ /*
+ * If we overwrite a writable spte with a read-only one we
+ * should flush remote TLBs. Otherwise rmap_write_protect
+ * will find a read-only spte, even though the writable spte
+ * might be cached on a CPU's TLB.
+ */
+ if (is_writable_pte(entry) && !is_writable_pte(*sptep))
+ kvm_flush_remote_tlbs(vcpu->kvm);
done:
return ret;
}
@@ -2045,7 +2046,7 @@
int user_fault, int write_fault, int dirty,
int *ptwrite, int level, gfn_t gfn,
pfn_t pfn, bool speculative,
- bool reset_host_protection)
+ bool host_writable)
{
int was_rmapped = 0;
int rmap_count;
@@ -2080,7 +2081,7 @@
if (set_spte(vcpu, sptep, pte_access, user_fault, write_fault,
dirty, level, gfn, pfn, speculative, true,
- reset_host_protection)) {
+ host_writable)) {
if (write_fault)
*ptwrite = 1;
kvm_mmu_flush_tlb(vcpu);
@@ -2211,7 +2212,8 @@
}
static int __direct_map(struct kvm_vcpu *vcpu, gpa_t v, int write,
- int level, gfn_t gfn, pfn_t pfn)
+ int map_writable, int level, gfn_t gfn, pfn_t pfn,
+ bool prefault)
{
struct kvm_shadow_walk_iterator iterator;
struct kvm_mmu_page *sp;
@@ -2220,9 +2222,11 @@
for_each_shadow_entry(vcpu, (u64)gfn << PAGE_SHIFT, iterator) {
if (iterator.level == level) {
- mmu_set_spte(vcpu, iterator.sptep, ACC_ALL, ACC_ALL,
+ unsigned pte_access = ACC_ALL;
+
+ mmu_set_spte(vcpu, iterator.sptep, ACC_ALL, pte_access,
0, write, 1, &pt_write,
- level, gfn, pfn, false, true);
+ level, gfn, pfn, prefault, map_writable);
direct_pte_prefetch(vcpu, iterator.sptep);
++vcpu->stat.pf_fixed;
break;
@@ -2277,12 +2281,17 @@
return 1;
}
-static int nonpaging_map(struct kvm_vcpu *vcpu, gva_t v, int write, gfn_t gfn)
+static bool try_async_pf(struct kvm_vcpu *vcpu, bool prefault, gfn_t gfn,
+ gva_t gva, pfn_t *pfn, bool write, bool *writable);
+
+static int nonpaging_map(struct kvm_vcpu *vcpu, gva_t v, int write, gfn_t gfn,
+ bool prefault)
{
int r;
int level;
pfn_t pfn;
unsigned long mmu_seq;
+ bool map_writable;
level = mapping_level(vcpu, gfn);
@@ -2297,7 +2306,9 @@
mmu_seq = vcpu->kvm->mmu_notifier_seq;
smp_rmb();
- pfn = gfn_to_pfn(vcpu->kvm, gfn);
+
+ if (try_async_pf(vcpu, prefault, gfn, v, &pfn, write, &map_writable))
+ return 0;
/* mmio */
if (is_error_pfn(pfn))
@@ -2307,7 +2318,8 @@
if (mmu_notifier_retry(vcpu, mmu_seq))
goto out_unlock;
kvm_mmu_free_some_pages(vcpu);
- r = __direct_map(vcpu, v, write, level, gfn, pfn);
+ r = __direct_map(vcpu, v, write, map_writable, level, gfn, pfn,
+ prefault);
spin_unlock(&vcpu->kvm->mmu_lock);
@@ -2530,6 +2542,7 @@
hpa_t root = vcpu->arch.mmu.root_hpa;
sp = page_header(root);
mmu_sync_children(vcpu, sp);
+ trace_kvm_mmu_audit(vcpu, AUDIT_POST_SYNC);
return;
}
for (i = 0; i < 4; ++i) {
@@ -2552,23 +2565,24 @@
}
static gpa_t nonpaging_gva_to_gpa(struct kvm_vcpu *vcpu, gva_t vaddr,
- u32 access, u32 *error)
+ u32 access, struct x86_exception *exception)
{
- if (error)
- *error = 0;
+ if (exception)
+ exception->error_code = 0;
return vaddr;
}
static gpa_t nonpaging_gva_to_gpa_nested(struct kvm_vcpu *vcpu, gva_t vaddr,
- u32 access, u32 *error)
+ u32 access,
+ struct x86_exception *exception)
{
- if (error)
- *error = 0;
+ if (exception)
+ exception->error_code = 0;
return vcpu->arch.nested_mmu.translate_gpa(vcpu, vaddr, access);
}
static int nonpaging_page_fault(struct kvm_vcpu *vcpu, gva_t gva,
- u32 error_code)
+ u32 error_code, bool prefault)
{
gfn_t gfn;
int r;
@@ -2584,17 +2598,67 @@
gfn = gva >> PAGE_SHIFT;
return nonpaging_map(vcpu, gva & PAGE_MASK,
- error_code & PFERR_WRITE_MASK, gfn);
+ error_code & PFERR_WRITE_MASK, gfn, prefault);
}
-static int tdp_page_fault(struct kvm_vcpu *vcpu, gva_t gpa,
- u32 error_code)
+static int kvm_arch_setup_async_pf(struct kvm_vcpu *vcpu, gva_t gva, gfn_t gfn)
+{
+ struct kvm_arch_async_pf arch;
+
+ arch.token = (vcpu->arch.apf.id++ << 12) | vcpu->vcpu_id;
+ arch.gfn = gfn;
+ arch.direct_map = vcpu->arch.mmu.direct_map;
+ arch.cr3 = vcpu->arch.mmu.get_cr3(vcpu);
+
+ return kvm_setup_async_pf(vcpu, gva, gfn, &arch);
+}
+
+static bool can_do_async_pf(struct kvm_vcpu *vcpu)
+{
+ if (unlikely(!irqchip_in_kernel(vcpu->kvm) ||
+ kvm_event_needs_reinjection(vcpu)))
+ return false;
+
+ return kvm_x86_ops->interrupt_allowed(vcpu);
+}
+
+static bool try_async_pf(struct kvm_vcpu *vcpu, bool prefault, gfn_t gfn,
+ gva_t gva, pfn_t *pfn, bool write, bool *writable)
+{
+ bool async;
+
+ *pfn = gfn_to_pfn_async(vcpu->kvm, gfn, &async, write, writable);
+
+ if (!async)
+ return false; /* *pfn has correct page already */
+
+ put_page(pfn_to_page(*pfn));
+
+ if (!prefault && can_do_async_pf(vcpu)) {
+ trace_kvm_try_async_get_page(gva, gfn);
+ if (kvm_find_async_pf_gfn(vcpu, gfn)) {
+ trace_kvm_async_pf_doublefault(gva, gfn);
+ kvm_make_request(KVM_REQ_APF_HALT, vcpu);
+ return true;
+ } else if (kvm_arch_setup_async_pf(vcpu, gva, gfn))
+ return true;
+ }
+
+ *pfn = gfn_to_pfn_prot(vcpu->kvm, gfn, write, writable);
+
+ return false;
+}
+
+static int tdp_page_fault(struct kvm_vcpu *vcpu, gva_t gpa, u32 error_code,
+ bool prefault)
{
pfn_t pfn;
int r;
int level;
gfn_t gfn = gpa >> PAGE_SHIFT;
unsigned long mmu_seq;
+ int write = error_code & PFERR_WRITE_MASK;
+ bool map_writable;
ASSERT(vcpu);
ASSERT(VALID_PAGE(vcpu->arch.mmu.root_hpa));
@@ -2609,15 +2673,19 @@
mmu_seq = vcpu->kvm->mmu_notifier_seq;
smp_rmb();
- pfn = gfn_to_pfn(vcpu->kvm, gfn);
+
+ if (try_async_pf(vcpu, prefault, gfn, gpa, &pfn, write, &map_writable))
+ return 0;
+
+ /* mmio */
if (is_error_pfn(pfn))
return kvm_handle_bad_page(vcpu->kvm, gfn, pfn);
spin_lock(&vcpu->kvm->mmu_lock);
if (mmu_notifier_retry(vcpu, mmu_seq))
goto out_unlock;
kvm_mmu_free_some_pages(vcpu);
- r = __direct_map(vcpu, gpa, error_code & PFERR_WRITE_MASK,
- level, gfn, pfn);
+ r = __direct_map(vcpu, gpa, write, map_writable,
+ level, gfn, pfn, prefault);
spin_unlock(&vcpu->kvm->mmu_lock);
return r;
@@ -2659,18 +2727,19 @@
static void paging_new_cr3(struct kvm_vcpu *vcpu)
{
- pgprintk("%s: cr3 %lx\n", __func__, vcpu->arch.cr3);
+ pgprintk("%s: cr3 %lx\n", __func__, kvm_read_cr3(vcpu));
mmu_free_roots(vcpu);
}
static unsigned long get_cr3(struct kvm_vcpu *vcpu)
{
- return vcpu->arch.cr3;
+ return kvm_read_cr3(vcpu);
}
-static void inject_page_fault(struct kvm_vcpu *vcpu)
+static void inject_page_fault(struct kvm_vcpu *vcpu,
+ struct x86_exception *fault)
{
- vcpu->arch.mmu.inject_page_fault(vcpu);
+ vcpu->arch.mmu.inject_page_fault(vcpu, fault);
}
static void paging_free(struct kvm_vcpu *vcpu)
@@ -2816,6 +2885,7 @@
{
struct kvm_mmu *context = vcpu->arch.walk_mmu;
+ context->base_role.word = 0;
context->new_cr3 = nonpaging_new_cr3;
context->page_fault = tdp_page_fault;
context->free = nonpaging_free;
@@ -3008,9 +3078,6 @@
return;
}
- if (is_rsvd_bits_set(&vcpu->arch.mmu, *(u64 *)new, PT_PAGE_TABLE_LEVEL))
- return;
-
++vcpu->kvm->stat.mmu_pte_updated;
if (!sp->role.cr4_pae)
paging32_update_pte(vcpu, sp, spte, new);
@@ -3264,12 +3331,13 @@
}
}
-int kvm_mmu_page_fault(struct kvm_vcpu *vcpu, gva_t cr2, u32 error_code)
+int kvm_mmu_page_fault(struct kvm_vcpu *vcpu, gva_t cr2, u32 error_code,
+ void *insn, int insn_len)
{
int r;
enum emulation_result er;
- r = vcpu->arch.mmu.page_fault(vcpu, cr2, error_code);
+ r = vcpu->arch.mmu.page_fault(vcpu, cr2, error_code, false);
if (r < 0)
goto out;
@@ -3282,7 +3350,7 @@
if (r)
goto out;
- er = emulate_instruction(vcpu, cr2, error_code, 0);
+ er = x86_emulate_instruction(vcpu, cr2, 0, insn, insn_len);
switch (er) {
case EMULATE_DONE:
@@ -3377,11 +3445,14 @@
if (!test_bit(slot, sp->slot_bitmap))
continue;
+ if (sp->role.level != PT_PAGE_TABLE_LEVEL)
+ continue;
+
pt = sp->spt;
for (i = 0; i < PT64_ENT_PER_PAGE; ++i)
/* avoid RMW */
if (is_writable_pte(pt[i]))
- pt[i] &= ~PT_WRITABLE_MASK;
+ update_spte(&pt[i], pt[i] & ~PT_WRITABLE_MASK);
}
kvm_flush_remote_tlbs(kvm);
}
@@ -3463,13 +3534,6 @@
kmem_cache_destroy(mmu_page_header_cache);
}
-void kvm_mmu_module_exit(void)
-{
- mmu_destroy_caches();
- percpu_counter_destroy(&kvm_total_used_mmu_pages);
- unregister_shrinker(&mmu_shrinker);
-}
-
int kvm_mmu_module_init(void)
{
pte_chain_cache = kmem_cache_create("kvm_pte_chain",
@@ -3566,7 +3630,7 @@
static int kvm_pv_mmu_flush_tlb(struct kvm_vcpu *vcpu)
{
- (void)kvm_set_cr3(vcpu, vcpu->arch.cr3);
+ (void)kvm_set_cr3(vcpu, kvm_read_cr3(vcpu));
return 1;
}
@@ -3662,12 +3726,6 @@
}
EXPORT_SYMBOL_GPL(kvm_mmu_get_spte_hierarchy);
-#ifdef CONFIG_KVM_MMU_AUDIT
-#include "mmu_audit.c"
-#else
-static void mmu_audit_disable(void) { }
-#endif
-
void kvm_mmu_destroy(struct kvm_vcpu *vcpu)
{
ASSERT(vcpu);
@@ -3675,5 +3733,18 @@
destroy_kvm_mmu(vcpu);
free_mmu_pages(vcpu);
mmu_free_memory_caches(vcpu);
+}
+
+#ifdef CONFIG_KVM_MMU_AUDIT
+#include "mmu_audit.c"
+#else
+static void mmu_audit_disable(void) { }
+#endif
+
+void kvm_mmu_module_exit(void)
+{
+ mmu_destroy_caches();
+ percpu_counter_destroy(&kvm_total_used_mmu_pages);
+ unregister_shrinker(&mmu_shrinker);
mmu_audit_disable();
}
diff --git a/arch/x86/kvm/mmu_audit.c b/arch/x86/kvm/mmu_audit.c
index ba2bcdd..5f6223b 100644
--- a/arch/x86/kvm/mmu_audit.c
+++ b/arch/x86/kvm/mmu_audit.c
@@ -19,11 +19,9 @@
#include <linux/ratelimit.h>
-static int audit_point;
-
-#define audit_printk(fmt, args...) \
+#define audit_printk(kvm, fmt, args...) \
printk(KERN_ERR "audit: (%s) error: " \
- fmt, audit_point_name[audit_point], ##args)
+ fmt, audit_point_name[kvm->arch.audit_point], ##args)
typedef void (*inspect_spte_fn) (struct kvm_vcpu *vcpu, u64 *sptep, int level);
@@ -97,18 +95,21 @@
if (sp->unsync) {
if (level != PT_PAGE_TABLE_LEVEL) {
- audit_printk("unsync sp: %p level = %d\n", sp, level);
+ audit_printk(vcpu->kvm, "unsync sp: %p "
+ "level = %d\n", sp, level);
return;
}
if (*sptep == shadow_notrap_nonpresent_pte) {
- audit_printk("notrap spte in unsync sp: %p\n", sp);
+ audit_printk(vcpu->kvm, "notrap spte in unsync "
+ "sp: %p\n", sp);
return;
}
}
if (sp->role.direct && *sptep == shadow_notrap_nonpresent_pte) {
- audit_printk("notrap spte in direct sp: %p\n", sp);
+ audit_printk(vcpu->kvm, "notrap spte in direct sp: %p\n",
+ sp);
return;
}
@@ -125,8 +126,9 @@
hpa = pfn << PAGE_SHIFT;
if ((*sptep & PT64_BASE_ADDR_MASK) != hpa)
- audit_printk("levels %d pfn %llx hpa %llx ent %llxn",
- vcpu->arch.mmu.root_level, pfn, hpa, *sptep);
+ audit_printk(vcpu->kvm, "levels %d pfn %llx hpa %llx "
+ "ent %llxn", vcpu->arch.mmu.root_level, pfn,
+ hpa, *sptep);
}
static void inspect_spte_has_rmap(struct kvm *kvm, u64 *sptep)
@@ -142,8 +144,8 @@
if (!gfn_to_memslot(kvm, gfn)) {
if (!printk_ratelimit())
return;
- audit_printk("no memslot for gfn %llx\n", gfn);
- audit_printk("index %ld of sp (gfn=%llx)\n",
+ audit_printk(kvm, "no memslot for gfn %llx\n", gfn);
+ audit_printk(kvm, "index %ld of sp (gfn=%llx)\n",
(long int)(sptep - rev_sp->spt), rev_sp->gfn);
dump_stack();
return;
@@ -153,7 +155,8 @@
if (!*rmapp) {
if (!printk_ratelimit())
return;
- audit_printk("no rmap for writable spte %llx\n", *sptep);
+ audit_printk(kvm, "no rmap for writable spte %llx\n",
+ *sptep);
dump_stack();
}
}
@@ -168,8 +171,9 @@
{
struct kvm_mmu_page *sp = page_header(__pa(sptep));
- if (audit_point == AUDIT_POST_SYNC && sp->unsync)
- audit_printk("meet unsync sp(%p) after sync root.\n", sp);
+ if (vcpu->kvm->arch.audit_point == AUDIT_POST_SYNC && sp->unsync)
+ audit_printk(vcpu->kvm, "meet unsync sp(%p) after sync "
+ "root.\n", sp);
}
static void check_mappings_rmap(struct kvm *kvm, struct kvm_mmu_page *sp)
@@ -202,8 +206,9 @@
spte = rmap_next(kvm, rmapp, NULL);
while (spte) {
if (is_writable_pte(*spte))
- audit_printk("shadow page has writable mappings: gfn "
- "%llx role %x\n", sp->gfn, sp->role.word);
+ audit_printk(kvm, "shadow page has writable "
+ "mappings: gfn %llx role %x\n",
+ sp->gfn, sp->role.word);
spte = rmap_next(kvm, rmapp, spte);
}
}
@@ -238,7 +243,7 @@
if (!__ratelimit(&ratelimit_state))
return;
- audit_point = point;
+ vcpu->kvm->arch.audit_point = point;
audit_all_active_sps(vcpu->kvm);
audit_vcpu_spte(vcpu);
}
diff --git a/arch/x86/kvm/paging_tmpl.h b/arch/x86/kvm/paging_tmpl.h
index cd7a833..53210f1 100644
--- a/arch/x86/kvm/paging_tmpl.h
+++ b/arch/x86/kvm/paging_tmpl.h
@@ -72,7 +72,7 @@
unsigned pt_access;
unsigned pte_access;
gfn_t gfn;
- u32 error_code;
+ struct x86_exception fault;
};
static gfn_t gpte_to_gfn_lvl(pt_element_t gpte, int lvl)
@@ -266,21 +266,23 @@
return 1;
error:
- walker->error_code = 0;
+ walker->fault.vector = PF_VECTOR;
+ walker->fault.error_code_valid = true;
+ walker->fault.error_code = 0;
if (present)
- walker->error_code |= PFERR_PRESENT_MASK;
+ walker->fault.error_code |= PFERR_PRESENT_MASK;
- walker->error_code |= write_fault | user_fault;
+ walker->fault.error_code |= write_fault | user_fault;
if (fetch_fault && mmu->nx)
- walker->error_code |= PFERR_FETCH_MASK;
+ walker->fault.error_code |= PFERR_FETCH_MASK;
if (rsvd_fault)
- walker->error_code |= PFERR_RSVD_MASK;
+ walker->fault.error_code |= PFERR_RSVD_MASK;
- vcpu->arch.fault.address = addr;
- vcpu->arch.fault.error_code = walker->error_code;
+ walker->fault.address = addr;
+ walker->fault.nested_page_fault = mmu != vcpu->arch.walk_mmu;
- trace_kvm_mmu_walker_error(walker->error_code);
+ trace_kvm_mmu_walker_error(walker->fault.error_code);
return 0;
}
@@ -299,25 +301,42 @@
addr, access);
}
+static bool FNAME(prefetch_invalid_gpte)(struct kvm_vcpu *vcpu,
+ struct kvm_mmu_page *sp, u64 *spte,
+ pt_element_t gpte)
+{
+ u64 nonpresent = shadow_trap_nonpresent_pte;
+
+ if (is_rsvd_bits_set(&vcpu->arch.mmu, gpte, PT_PAGE_TABLE_LEVEL))
+ goto no_present;
+
+ if (!is_present_gpte(gpte)) {
+ if (!sp->unsync)
+ nonpresent = shadow_notrap_nonpresent_pte;
+ goto no_present;
+ }
+
+ if (!(gpte & PT_ACCESSED_MASK))
+ goto no_present;
+
+ return false;
+
+no_present:
+ drop_spte(vcpu->kvm, spte, nonpresent);
+ return true;
+}
+
static void FNAME(update_pte)(struct kvm_vcpu *vcpu, struct kvm_mmu_page *sp,
u64 *spte, const void *pte)
{
pt_element_t gpte;
unsigned pte_access;
pfn_t pfn;
- u64 new_spte;
gpte = *(const pt_element_t *)pte;
- if (~gpte & (PT_PRESENT_MASK | PT_ACCESSED_MASK)) {
- if (!is_present_gpte(gpte)) {
- if (sp->unsync)
- new_spte = shadow_trap_nonpresent_pte;
- else
- new_spte = shadow_notrap_nonpresent_pte;
- __set_spte(spte, new_spte);
- }
+ if (FNAME(prefetch_invalid_gpte)(vcpu, sp, spte, gpte))
return;
- }
+
pgprintk("%s: gpte %llx spte %p\n", __func__, (u64)gpte, spte);
pte_access = sp->role.access & FNAME(gpte_access)(vcpu, gpte);
if (gpte_to_gfn(gpte) != vcpu->arch.update_pte.gfn)
@@ -329,7 +348,7 @@
return;
kvm_get_pfn(pfn);
/*
- * we call mmu_set_spte() with reset_host_protection = true beacuse that
+ * we call mmu_set_spte() with host_writable = true beacuse that
* vcpu->arch.update_pte.pfn was fetched from get_user_pages(write = 1).
*/
mmu_set_spte(vcpu, spte, sp->role.access, pte_access, 0, 0,
@@ -364,7 +383,6 @@
u64 *sptep)
{
struct kvm_mmu_page *sp;
- struct kvm_mmu *mmu = &vcpu->arch.mmu;
pt_element_t *gptep = gw->prefetch_ptes;
u64 *spte;
int i;
@@ -395,14 +413,7 @@
gpte = gptep[i];
- if (!is_present_gpte(gpte) ||
- is_rsvd_bits_set(mmu, gpte, PT_PAGE_TABLE_LEVEL)) {
- if (!sp->unsync)
- __set_spte(spte, shadow_notrap_nonpresent_pte);
- continue;
- }
-
- if (!(gpte & PT_ACCESSED_MASK))
+ if (FNAME(prefetch_invalid_gpte)(vcpu, sp, spte, gpte))
continue;
pte_access = sp->role.access & FNAME(gpte_access)(vcpu, gpte);
@@ -427,7 +438,8 @@
static u64 *FNAME(fetch)(struct kvm_vcpu *vcpu, gva_t addr,
struct guest_walker *gw,
int user_fault, int write_fault, int hlevel,
- int *ptwrite, pfn_t pfn)
+ int *ptwrite, pfn_t pfn, bool map_writable,
+ bool prefault)
{
unsigned access = gw->pt_access;
struct kvm_mmu_page *sp = NULL;
@@ -501,7 +513,7 @@
mmu_set_spte(vcpu, it.sptep, access, gw->pte_access & access,
user_fault, write_fault, dirty, ptwrite, it.level,
- gw->gfn, pfn, false, true);
+ gw->gfn, pfn, prefault, map_writable);
FNAME(pte_prefetch)(vcpu, gw, it.sptep);
return it.sptep;
@@ -527,8 +539,8 @@
* Returns: 1 if we need to emulate the instruction, 0 otherwise, or
* a negative value on error.
*/
-static int FNAME(page_fault)(struct kvm_vcpu *vcpu, gva_t addr,
- u32 error_code)
+static int FNAME(page_fault)(struct kvm_vcpu *vcpu, gva_t addr, u32 error_code,
+ bool prefault)
{
int write_fault = error_code & PFERR_WRITE_MASK;
int user_fault = error_code & PFERR_USER_MASK;
@@ -539,6 +551,7 @@
pfn_t pfn;
int level = PT_PAGE_TABLE_LEVEL;
unsigned long mmu_seq;
+ bool map_writable;
pgprintk("%s: addr %lx err %x\n", __func__, addr, error_code);
@@ -556,8 +569,11 @@
*/
if (!r) {
pgprintk("%s: guest page fault\n", __func__);
- inject_page_fault(vcpu);
- vcpu->arch.last_pt_write_count = 0; /* reset fork detector */
+ if (!prefault) {
+ inject_page_fault(vcpu, &walker.fault);
+ /* reset fork detector */
+ vcpu->arch.last_pt_write_count = 0;
+ }
return 0;
}
@@ -568,7 +584,10 @@
mmu_seq = vcpu->kvm->mmu_notifier_seq;
smp_rmb();
- pfn = gfn_to_pfn(vcpu->kvm, walker.gfn);
+
+ if (try_async_pf(vcpu, prefault, walker.gfn, addr, &pfn, write_fault,
+ &map_writable))
+ return 0;
/* mmio */
if (is_error_pfn(pfn))
@@ -581,7 +600,7 @@
trace_kvm_mmu_audit(vcpu, AUDIT_PRE_PAGE_FAULT);
kvm_mmu_free_some_pages(vcpu);
sptep = FNAME(fetch)(vcpu, addr, &walker, user_fault, write_fault,
- level, &write_pt, pfn);
+ level, &write_pt, pfn, map_writable, prefault);
(void)sptep;
pgprintk("%s: shadow pte %p %llx ptwrite %d\n", __func__,
sptep, *sptep, write_pt);
@@ -661,7 +680,7 @@
}
static gpa_t FNAME(gva_to_gpa)(struct kvm_vcpu *vcpu, gva_t vaddr, u32 access,
- u32 *error)
+ struct x86_exception *exception)
{
struct guest_walker walker;
gpa_t gpa = UNMAPPED_GVA;
@@ -672,14 +691,15 @@
if (r) {
gpa = gfn_to_gpa(walker.gfn);
gpa |= vaddr & ~PAGE_MASK;
- } else if (error)
- *error = walker.error_code;
+ } else if (exception)
+ *exception = walker.fault;
return gpa;
}
static gpa_t FNAME(gva_to_gpa_nested)(struct kvm_vcpu *vcpu, gva_t vaddr,
- u32 access, u32 *error)
+ u32 access,
+ struct x86_exception *exception)
{
struct guest_walker walker;
gpa_t gpa = UNMAPPED_GVA;
@@ -690,8 +710,8 @@
if (r) {
gpa = gfn_to_gpa(walker.gfn);
gpa |= vaddr & ~PAGE_MASK;
- } else if (error)
- *error = walker.error_code;
+ } else if (exception)
+ *exception = walker.fault;
return gpa;
}
@@ -730,12 +750,19 @@
* Using the cached information from sp->gfns is safe because:
* - The spte has a reference to the struct page, so the pfn for a given gfn
* can't change unless all sptes pointing to it are nuked first.
+ *
+ * Note:
+ * We should flush all tlbs if spte is dropped even though guest is
+ * responsible for it. Since if we don't, kvm_mmu_notifier_invalidate_page
+ * and kvm_mmu_notifier_invalidate_range_start detect the mapping page isn't
+ * used by guest then tlbs are not flushed, so guest is allowed to access the
+ * freed pages.
+ * And we increase kvm->tlbs_dirty to delay tlbs flush in this case.
*/
-static int FNAME(sync_page)(struct kvm_vcpu *vcpu, struct kvm_mmu_page *sp,
- bool clear_unsync)
+static int FNAME(sync_page)(struct kvm_vcpu *vcpu, struct kvm_mmu_page *sp)
{
int i, offset, nr_present;
- bool reset_host_protection;
+ bool host_writable;
gpa_t first_pte_gpa;
offset = nr_present = 0;
@@ -764,31 +791,27 @@
return -EINVAL;
gfn = gpte_to_gfn(gpte);
- if (is_rsvd_bits_set(&vcpu->arch.mmu, gpte, PT_PAGE_TABLE_LEVEL)
- || gfn != sp->gfns[i] || !is_present_gpte(gpte)
- || !(gpte & PT_ACCESSED_MASK)) {
- u64 nonpresent;
- if (is_present_gpte(gpte) || !clear_unsync)
- nonpresent = shadow_trap_nonpresent_pte;
- else
- nonpresent = shadow_notrap_nonpresent_pte;
- drop_spte(vcpu->kvm, &sp->spt[i], nonpresent);
+ if (FNAME(prefetch_invalid_gpte)(vcpu, sp, &sp->spt[i], gpte)) {
+ vcpu->kvm->tlbs_dirty++;
+ continue;
+ }
+
+ if (gfn != sp->gfns[i]) {
+ drop_spte(vcpu->kvm, &sp->spt[i],
+ shadow_trap_nonpresent_pte);
+ vcpu->kvm->tlbs_dirty++;
continue;
}
nr_present++;
pte_access = sp->role.access & FNAME(gpte_access)(vcpu, gpte);
- if (!(sp->spt[i] & SPTE_HOST_WRITEABLE)) {
- pte_access &= ~ACC_WRITE_MASK;
- reset_host_protection = 0;
- } else {
- reset_host_protection = 1;
- }
+ host_writable = sp->spt[i] & SPTE_HOST_WRITEABLE;
+
set_spte(vcpu, &sp->spt[i], pte_access, 0, 0,
is_dirty_gpte(gpte), PT_PAGE_TABLE_LEVEL, gfn,
spte_to_pfn(sp->spt[i]), true, false,
- reset_host_protection);
+ host_writable);
}
return !nr_present;
diff --git a/arch/x86/kvm/svm.c b/arch/x86/kvm/svm.c
index b81a9b7..25bd1bc 100644
--- a/arch/x86/kvm/svm.c
+++ b/arch/x86/kvm/svm.c
@@ -31,6 +31,7 @@
#include <asm/tlbflush.h>
#include <asm/desc.h>
+#include <asm/kvm_para.h>
#include <asm/virtext.h>
#include "trace.h"
@@ -50,6 +51,10 @@
#define SVM_FEATURE_LBRV (1 << 1)
#define SVM_FEATURE_SVML (1 << 2)
#define SVM_FEATURE_NRIP (1 << 3)
+#define SVM_FEATURE_TSC_RATE (1 << 4)
+#define SVM_FEATURE_VMCB_CLEAN (1 << 5)
+#define SVM_FEATURE_FLUSH_ASID (1 << 6)
+#define SVM_FEATURE_DECODE_ASSIST (1 << 7)
#define SVM_FEATURE_PAUSE_FILTER (1 << 10)
#define NESTED_EXIT_HOST 0 /* Exit handled on host level */
@@ -97,10 +102,8 @@
unsigned long vmexit_rax;
/* cache for intercepts of the guest */
- u16 intercept_cr_read;
- u16 intercept_cr_write;
- u16 intercept_dr_read;
- u16 intercept_dr_write;
+ u32 intercept_cr;
+ u32 intercept_dr;
u32 intercept_exceptions;
u64 intercept;
@@ -123,7 +126,12 @@
u64 next_rip;
u64 host_user_msrs[NR_HOST_SAVE_USER_MSRS];
- u64 host_gs_base;
+ struct {
+ u16 fs;
+ u16 gs;
+ u16 ldt;
+ u64 gs_base;
+ } host;
u32 *msrpm;
@@ -133,6 +141,7 @@
unsigned int3_injected;
unsigned long int3_rip;
+ u32 apf_reason;
};
#define MSR_INVALID 0xffffffffU
@@ -180,14 +189,151 @@
static int nested_svm_check_exception(struct vcpu_svm *svm, unsigned nr,
bool has_error_code, u32 error_code);
+enum {
+ VMCB_INTERCEPTS, /* Intercept vectors, TSC offset,
+ pause filter count */
+ VMCB_PERM_MAP, /* IOPM Base and MSRPM Base */
+ VMCB_ASID, /* ASID */
+ VMCB_INTR, /* int_ctl, int_vector */
+ VMCB_NPT, /* npt_en, nCR3, gPAT */
+ VMCB_CR, /* CR0, CR3, CR4, EFER */
+ VMCB_DR, /* DR6, DR7 */
+ VMCB_DT, /* GDT, IDT */
+ VMCB_SEG, /* CS, DS, SS, ES, CPL */
+ VMCB_CR2, /* CR2 only */
+ VMCB_LBR, /* DBGCTL, BR_FROM, BR_TO, LAST_EX_FROM, LAST_EX_TO */
+ VMCB_DIRTY_MAX,
+};
+
+/* TPR and CR2 are always written before VMRUN */
+#define VMCB_ALWAYS_DIRTY_MASK ((1U << VMCB_INTR) | (1U << VMCB_CR2))
+
+static inline void mark_all_dirty(struct vmcb *vmcb)
+{
+ vmcb->control.clean = 0;
+}
+
+static inline void mark_all_clean(struct vmcb *vmcb)
+{
+ vmcb->control.clean = ((1 << VMCB_DIRTY_MAX) - 1)
+ & ~VMCB_ALWAYS_DIRTY_MASK;
+}
+
+static inline void mark_dirty(struct vmcb *vmcb, int bit)
+{
+ vmcb->control.clean &= ~(1 << bit);
+}
+
static inline struct vcpu_svm *to_svm(struct kvm_vcpu *vcpu)
{
return container_of(vcpu, struct vcpu_svm, vcpu);
}
-static inline bool is_nested(struct vcpu_svm *svm)
+static void recalc_intercepts(struct vcpu_svm *svm)
{
- return svm->nested.vmcb;
+ struct vmcb_control_area *c, *h;
+ struct nested_state *g;
+
+ mark_dirty(svm->vmcb, VMCB_INTERCEPTS);
+
+ if (!is_guest_mode(&svm->vcpu))
+ return;
+
+ c = &svm->vmcb->control;
+ h = &svm->nested.hsave->control;
+ g = &svm->nested;
+
+ c->intercept_cr = h->intercept_cr | g->intercept_cr;
+ c->intercept_dr = h->intercept_dr | g->intercept_dr;
+ c->intercept_exceptions = h->intercept_exceptions | g->intercept_exceptions;
+ c->intercept = h->intercept | g->intercept;
+}
+
+static inline struct vmcb *get_host_vmcb(struct vcpu_svm *svm)
+{
+ if (is_guest_mode(&svm->vcpu))
+ return svm->nested.hsave;
+ else
+ return svm->vmcb;
+}
+
+static inline void set_cr_intercept(struct vcpu_svm *svm, int bit)
+{
+ struct vmcb *vmcb = get_host_vmcb(svm);
+
+ vmcb->control.intercept_cr |= (1U << bit);
+
+ recalc_intercepts(svm);
+}
+
+static inline void clr_cr_intercept(struct vcpu_svm *svm, int bit)
+{
+ struct vmcb *vmcb = get_host_vmcb(svm);
+
+ vmcb->control.intercept_cr &= ~(1U << bit);
+
+ recalc_intercepts(svm);
+}
+
+static inline bool is_cr_intercept(struct vcpu_svm *svm, int bit)
+{
+ struct vmcb *vmcb = get_host_vmcb(svm);
+
+ return vmcb->control.intercept_cr & (1U << bit);
+}
+
+static inline void set_dr_intercept(struct vcpu_svm *svm, int bit)
+{
+ struct vmcb *vmcb = get_host_vmcb(svm);
+
+ vmcb->control.intercept_dr |= (1U << bit);
+
+ recalc_intercepts(svm);
+}
+
+static inline void clr_dr_intercept(struct vcpu_svm *svm, int bit)
+{
+ struct vmcb *vmcb = get_host_vmcb(svm);
+
+ vmcb->control.intercept_dr &= ~(1U << bit);
+
+ recalc_intercepts(svm);
+}
+
+static inline void set_exception_intercept(struct vcpu_svm *svm, int bit)
+{
+ struct vmcb *vmcb = get_host_vmcb(svm);
+
+ vmcb->control.intercept_exceptions |= (1U << bit);
+
+ recalc_intercepts(svm);
+}
+
+static inline void clr_exception_intercept(struct vcpu_svm *svm, int bit)
+{
+ struct vmcb *vmcb = get_host_vmcb(svm);
+
+ vmcb->control.intercept_exceptions &= ~(1U << bit);
+
+ recalc_intercepts(svm);
+}
+
+static inline void set_intercept(struct vcpu_svm *svm, int bit)
+{
+ struct vmcb *vmcb = get_host_vmcb(svm);
+
+ vmcb->control.intercept |= (1ULL << bit);
+
+ recalc_intercepts(svm);
+}
+
+static inline void clr_intercept(struct vcpu_svm *svm, int bit)
+{
+ struct vmcb *vmcb = get_host_vmcb(svm);
+
+ vmcb->control.intercept &= ~(1ULL << bit);
+
+ recalc_intercepts(svm);
}
static inline void enable_gif(struct vcpu_svm *svm)
@@ -264,11 +410,6 @@
#define MAX_INST_SIZE 15
-static inline u32 svm_has(u32 feat)
-{
- return svm_features & feat;
-}
-
static inline void clgi(void)
{
asm volatile (__ex(SVM_CLGI));
@@ -284,16 +425,6 @@
asm volatile (__ex(SVM_INVLPGA) : : "a"(addr), "c"(asid));
}
-static inline void force_new_asid(struct kvm_vcpu *vcpu)
-{
- to_svm(vcpu)->asid_generation--;
-}
-
-static inline void flush_guest_tlb(struct kvm_vcpu *vcpu)
-{
- force_new_asid(vcpu);
-}
-
static int get_npt_level(void)
{
#ifdef CONFIG_X86_64
@@ -310,6 +441,7 @@
efer &= ~EFER_LME;
to_svm(vcpu)->vmcb->save.efer = efer | EFER_SVME;
+ mark_dirty(to_svm(vcpu)->vmcb, VMCB_CR);
}
static int is_external_interrupt(u32 info)
@@ -347,7 +479,7 @@
svm->next_rip = svm->vmcb->control.next_rip;
if (!svm->next_rip) {
- if (emulate_instruction(vcpu, 0, 0, EMULTYPE_SKIP) !=
+ if (emulate_instruction(vcpu, EMULTYPE_SKIP) !=
EMULATE_DONE)
printk(KERN_DEBUG "%s: NOP\n", __func__);
return;
@@ -374,7 +506,7 @@
nested_svm_check_exception(svm, nr, has_error_code, error_code))
return;
- if (nr == BP_VECTOR && !svm_has(SVM_FEATURE_NRIP)) {
+ if (nr == BP_VECTOR && !static_cpu_has(X86_FEATURE_NRIPS)) {
unsigned long rip, old_rip = kvm_rip_read(&svm->vcpu);
/*
@@ -670,7 +802,7 @@
svm_features = cpuid_edx(SVM_CPUID_FUNC);
- if (!svm_has(SVM_FEATURE_NPT))
+ if (!boot_cpu_has(X86_FEATURE_NPT))
npt_enabled = false;
if (npt_enabled && !npt) {
@@ -725,13 +857,15 @@
struct vcpu_svm *svm = to_svm(vcpu);
u64 g_tsc_offset = 0;
- if (is_nested(svm)) {
+ if (is_guest_mode(vcpu)) {
g_tsc_offset = svm->vmcb->control.tsc_offset -
svm->nested.hsave->control.tsc_offset;
svm->nested.hsave->control.tsc_offset = offset;
}
svm->vmcb->control.tsc_offset = offset + g_tsc_offset;
+
+ mark_dirty(svm->vmcb, VMCB_INTERCEPTS);
}
static void svm_adjust_tsc_offset(struct kvm_vcpu *vcpu, s64 adjustment)
@@ -739,8 +873,9 @@
struct vcpu_svm *svm = to_svm(vcpu);
svm->vmcb->control.tsc_offset += adjustment;
- if (is_nested(svm))
+ if (is_guest_mode(vcpu))
svm->nested.hsave->control.tsc_offset += adjustment;
+ mark_dirty(svm->vmcb, VMCB_INTERCEPTS);
}
static void init_vmcb(struct vcpu_svm *svm)
@@ -749,62 +884,62 @@
struct vmcb_save_area *save = &svm->vmcb->save;
svm->vcpu.fpu_active = 1;
+ svm->vcpu.arch.hflags = 0;
- control->intercept_cr_read = INTERCEPT_CR0_MASK |
- INTERCEPT_CR3_MASK |
- INTERCEPT_CR4_MASK;
+ set_cr_intercept(svm, INTERCEPT_CR0_READ);
+ set_cr_intercept(svm, INTERCEPT_CR3_READ);
+ set_cr_intercept(svm, INTERCEPT_CR4_READ);
+ set_cr_intercept(svm, INTERCEPT_CR0_WRITE);
+ set_cr_intercept(svm, INTERCEPT_CR3_WRITE);
+ set_cr_intercept(svm, INTERCEPT_CR4_WRITE);
+ set_cr_intercept(svm, INTERCEPT_CR8_WRITE);
- control->intercept_cr_write = INTERCEPT_CR0_MASK |
- INTERCEPT_CR3_MASK |
- INTERCEPT_CR4_MASK |
- INTERCEPT_CR8_MASK;
+ set_dr_intercept(svm, INTERCEPT_DR0_READ);
+ set_dr_intercept(svm, INTERCEPT_DR1_READ);
+ set_dr_intercept(svm, INTERCEPT_DR2_READ);
+ set_dr_intercept(svm, INTERCEPT_DR3_READ);
+ set_dr_intercept(svm, INTERCEPT_DR4_READ);
+ set_dr_intercept(svm, INTERCEPT_DR5_READ);
+ set_dr_intercept(svm, INTERCEPT_DR6_READ);
+ set_dr_intercept(svm, INTERCEPT_DR7_READ);
- control->intercept_dr_read = INTERCEPT_DR0_MASK |
- INTERCEPT_DR1_MASK |
- INTERCEPT_DR2_MASK |
- INTERCEPT_DR3_MASK |
- INTERCEPT_DR4_MASK |
- INTERCEPT_DR5_MASK |
- INTERCEPT_DR6_MASK |
- INTERCEPT_DR7_MASK;
+ set_dr_intercept(svm, INTERCEPT_DR0_WRITE);
+ set_dr_intercept(svm, INTERCEPT_DR1_WRITE);
+ set_dr_intercept(svm, INTERCEPT_DR2_WRITE);
+ set_dr_intercept(svm, INTERCEPT_DR3_WRITE);
+ set_dr_intercept(svm, INTERCEPT_DR4_WRITE);
+ set_dr_intercept(svm, INTERCEPT_DR5_WRITE);
+ set_dr_intercept(svm, INTERCEPT_DR6_WRITE);
+ set_dr_intercept(svm, INTERCEPT_DR7_WRITE);
- control->intercept_dr_write = INTERCEPT_DR0_MASK |
- INTERCEPT_DR1_MASK |
- INTERCEPT_DR2_MASK |
- INTERCEPT_DR3_MASK |
- INTERCEPT_DR4_MASK |
- INTERCEPT_DR5_MASK |
- INTERCEPT_DR6_MASK |
- INTERCEPT_DR7_MASK;
+ set_exception_intercept(svm, PF_VECTOR);
+ set_exception_intercept(svm, UD_VECTOR);
+ set_exception_intercept(svm, MC_VECTOR);
- control->intercept_exceptions = (1 << PF_VECTOR) |
- (1 << UD_VECTOR) |
- (1 << MC_VECTOR);
-
-
- control->intercept = (1ULL << INTERCEPT_INTR) |
- (1ULL << INTERCEPT_NMI) |
- (1ULL << INTERCEPT_SMI) |
- (1ULL << INTERCEPT_SELECTIVE_CR0) |
- (1ULL << INTERCEPT_CPUID) |
- (1ULL << INTERCEPT_INVD) |
- (1ULL << INTERCEPT_HLT) |
- (1ULL << INTERCEPT_INVLPG) |
- (1ULL << INTERCEPT_INVLPGA) |
- (1ULL << INTERCEPT_IOIO_PROT) |
- (1ULL << INTERCEPT_MSR_PROT) |
- (1ULL << INTERCEPT_TASK_SWITCH) |
- (1ULL << INTERCEPT_SHUTDOWN) |
- (1ULL << INTERCEPT_VMRUN) |
- (1ULL << INTERCEPT_VMMCALL) |
- (1ULL << INTERCEPT_VMLOAD) |
- (1ULL << INTERCEPT_VMSAVE) |
- (1ULL << INTERCEPT_STGI) |
- (1ULL << INTERCEPT_CLGI) |
- (1ULL << INTERCEPT_SKINIT) |
- (1ULL << INTERCEPT_WBINVD) |
- (1ULL << INTERCEPT_MONITOR) |
- (1ULL << INTERCEPT_MWAIT);
+ set_intercept(svm, INTERCEPT_INTR);
+ set_intercept(svm, INTERCEPT_NMI);
+ set_intercept(svm, INTERCEPT_SMI);
+ set_intercept(svm, INTERCEPT_SELECTIVE_CR0);
+ set_intercept(svm, INTERCEPT_CPUID);
+ set_intercept(svm, INTERCEPT_INVD);
+ set_intercept(svm, INTERCEPT_HLT);
+ set_intercept(svm, INTERCEPT_INVLPG);
+ set_intercept(svm, INTERCEPT_INVLPGA);
+ set_intercept(svm, INTERCEPT_IOIO_PROT);
+ set_intercept(svm, INTERCEPT_MSR_PROT);
+ set_intercept(svm, INTERCEPT_TASK_SWITCH);
+ set_intercept(svm, INTERCEPT_SHUTDOWN);
+ set_intercept(svm, INTERCEPT_VMRUN);
+ set_intercept(svm, INTERCEPT_VMMCALL);
+ set_intercept(svm, INTERCEPT_VMLOAD);
+ set_intercept(svm, INTERCEPT_VMSAVE);
+ set_intercept(svm, INTERCEPT_STGI);
+ set_intercept(svm, INTERCEPT_CLGI);
+ set_intercept(svm, INTERCEPT_SKINIT);
+ set_intercept(svm, INTERCEPT_WBINVD);
+ set_intercept(svm, INTERCEPT_MONITOR);
+ set_intercept(svm, INTERCEPT_MWAIT);
+ set_intercept(svm, INTERCEPT_XSETBV);
control->iopm_base_pa = iopm_base;
control->msrpm_base_pa = __pa(svm->msrpm);
@@ -855,25 +990,27 @@
if (npt_enabled) {
/* Setup VMCB for Nested Paging */
control->nested_ctl = 1;
- control->intercept &= ~((1ULL << INTERCEPT_TASK_SWITCH) |
- (1ULL << INTERCEPT_INVLPG));
- control->intercept_exceptions &= ~(1 << PF_VECTOR);
- control->intercept_cr_read &= ~INTERCEPT_CR3_MASK;
- control->intercept_cr_write &= ~INTERCEPT_CR3_MASK;
+ clr_intercept(svm, INTERCEPT_TASK_SWITCH);
+ clr_intercept(svm, INTERCEPT_INVLPG);
+ clr_exception_intercept(svm, PF_VECTOR);
+ clr_cr_intercept(svm, INTERCEPT_CR3_READ);
+ clr_cr_intercept(svm, INTERCEPT_CR3_WRITE);
save->g_pat = 0x0007040600070406ULL;
save->cr3 = 0;
save->cr4 = 0;
}
- force_new_asid(&svm->vcpu);
+ svm->asid_generation = 0;
svm->nested.vmcb = 0;
svm->vcpu.arch.hflags = 0;
- if (svm_has(SVM_FEATURE_PAUSE_FILTER)) {
+ if (boot_cpu_has(X86_FEATURE_PAUSEFILTER)) {
control->pause_filter_count = 3000;
- control->intercept |= (1ULL << INTERCEPT_PAUSE);
+ set_intercept(svm, INTERCEPT_PAUSE);
}
+ mark_all_dirty(svm->vmcb);
+
enable_gif(svm);
}
@@ -990,8 +1127,16 @@
if (unlikely(cpu != vcpu->cpu)) {
svm->asid_generation = 0;
+ mark_all_dirty(svm->vmcb);
}
+#ifdef CONFIG_X86_64
+ rdmsrl(MSR_GS_BASE, to_svm(vcpu)->host.gs_base);
+#endif
+ savesegment(fs, svm->host.fs);
+ savesegment(gs, svm->host.gs);
+ svm->host.ldt = kvm_read_ldt();
+
for (i = 0; i < NR_HOST_SAVE_USER_MSRS; i++)
rdmsrl(host_save_user_msrs[i], svm->host_user_msrs[i]);
}
@@ -1002,6 +1147,14 @@
int i;
++vcpu->stat.host_state_reload;
+ kvm_load_ldt(svm->host.ldt);
+#ifdef CONFIG_X86_64
+ loadsegment(fs, svm->host.fs);
+ load_gs_index(svm->host.gs);
+ wrmsrl(MSR_KERNEL_GS_BASE, current->thread.gs);
+#else
+ loadsegment(gs, svm->host.gs);
+#endif
for (i = 0; i < NR_HOST_SAVE_USER_MSRS; i++)
wrmsrl(host_save_user_msrs[i], svm->host_user_msrs[i]);
}
@@ -1021,7 +1174,7 @@
switch (reg) {
case VCPU_EXREG_PDPTR:
BUG_ON(!npt_enabled);
- load_pdptrs(vcpu, vcpu->arch.walk_mmu, vcpu->arch.cr3);
+ load_pdptrs(vcpu, vcpu->arch.walk_mmu, kvm_read_cr3(vcpu));
break;
default:
BUG();
@@ -1030,12 +1183,12 @@
static void svm_set_vintr(struct vcpu_svm *svm)
{
- svm->vmcb->control.intercept |= 1ULL << INTERCEPT_VINTR;
+ set_intercept(svm, INTERCEPT_VINTR);
}
static void svm_clear_vintr(struct vcpu_svm *svm)
{
- svm->vmcb->control.intercept &= ~(1ULL << INTERCEPT_VINTR);
+ clr_intercept(svm, INTERCEPT_VINTR);
}
static struct vmcb_seg *svm_seg(struct kvm_vcpu *vcpu, int seg)
@@ -1150,6 +1303,7 @@
svm->vmcb->save.idtr.limit = dt->size;
svm->vmcb->save.idtr.base = dt->address ;
+ mark_dirty(svm->vmcb, VMCB_DT);
}
static void svm_get_gdt(struct kvm_vcpu *vcpu, struct desc_ptr *dt)
@@ -1166,19 +1320,23 @@
svm->vmcb->save.gdtr.limit = dt->size;
svm->vmcb->save.gdtr.base = dt->address ;
+ mark_dirty(svm->vmcb, VMCB_DT);
}
static void svm_decache_cr0_guest_bits(struct kvm_vcpu *vcpu)
{
}
+static void svm_decache_cr3(struct kvm_vcpu *vcpu)
+{
+}
+
static void svm_decache_cr4_guest_bits(struct kvm_vcpu *vcpu)
{
}
static void update_cr0_intercept(struct vcpu_svm *svm)
{
- struct vmcb *vmcb = svm->vmcb;
ulong gcr0 = svm->vcpu.arch.cr0;
u64 *hcr0 = &svm->vmcb->save.cr0;
@@ -1188,27 +1346,14 @@
*hcr0 = (*hcr0 & ~SVM_CR0_SELECTIVE_MASK)
| (gcr0 & SVM_CR0_SELECTIVE_MASK);
+ mark_dirty(svm->vmcb, VMCB_CR);
if (gcr0 == *hcr0 && svm->vcpu.fpu_active) {
- vmcb->control.intercept_cr_read &= ~INTERCEPT_CR0_MASK;
- vmcb->control.intercept_cr_write &= ~INTERCEPT_CR0_MASK;
- if (is_nested(svm)) {
- struct vmcb *hsave = svm->nested.hsave;
-
- hsave->control.intercept_cr_read &= ~INTERCEPT_CR0_MASK;
- hsave->control.intercept_cr_write &= ~INTERCEPT_CR0_MASK;
- vmcb->control.intercept_cr_read |= svm->nested.intercept_cr_read;
- vmcb->control.intercept_cr_write |= svm->nested.intercept_cr_write;
- }
+ clr_cr_intercept(svm, INTERCEPT_CR0_READ);
+ clr_cr_intercept(svm, INTERCEPT_CR0_WRITE);
} else {
- svm->vmcb->control.intercept_cr_read |= INTERCEPT_CR0_MASK;
- svm->vmcb->control.intercept_cr_write |= INTERCEPT_CR0_MASK;
- if (is_nested(svm)) {
- struct vmcb *hsave = svm->nested.hsave;
-
- hsave->control.intercept_cr_read |= INTERCEPT_CR0_MASK;
- hsave->control.intercept_cr_write |= INTERCEPT_CR0_MASK;
- }
+ set_cr_intercept(svm, INTERCEPT_CR0_READ);
+ set_cr_intercept(svm, INTERCEPT_CR0_WRITE);
}
}
@@ -1216,7 +1361,7 @@
{
struct vcpu_svm *svm = to_svm(vcpu);
- if (is_nested(svm)) {
+ if (is_guest_mode(vcpu)) {
/*
* We are here because we run in nested mode, the host kvm
* intercepts cr0 writes but the l1 hypervisor does not.
@@ -1268,6 +1413,7 @@
*/
cr0 &= ~(X86_CR0_CD | X86_CR0_NW);
svm->vmcb->save.cr0 = cr0;
+ mark_dirty(svm->vmcb, VMCB_CR);
update_cr0_intercept(svm);
}
@@ -1277,13 +1423,14 @@
unsigned long old_cr4 = to_svm(vcpu)->vmcb->save.cr4;
if (npt_enabled && ((old_cr4 ^ cr4) & X86_CR4_PGE))
- force_new_asid(vcpu);
+ svm_flush_tlb(vcpu);
vcpu->arch.cr4 = cr4;
if (!npt_enabled)
cr4 |= X86_CR4_PAE;
cr4 |= host_cr4_mce;
to_svm(vcpu)->vmcb->save.cr4 = cr4;
+ mark_dirty(to_svm(vcpu)->vmcb, VMCB_CR);
}
static void svm_set_segment(struct kvm_vcpu *vcpu,
@@ -1312,26 +1459,25 @@
= (svm->vmcb->save.cs.attrib
>> SVM_SELECTOR_DPL_SHIFT) & 3;
+ mark_dirty(svm->vmcb, VMCB_SEG);
}
static void update_db_intercept(struct kvm_vcpu *vcpu)
{
struct vcpu_svm *svm = to_svm(vcpu);
- svm->vmcb->control.intercept_exceptions &=
- ~((1 << DB_VECTOR) | (1 << BP_VECTOR));
+ clr_exception_intercept(svm, DB_VECTOR);
+ clr_exception_intercept(svm, BP_VECTOR);
if (svm->nmi_singlestep)
- svm->vmcb->control.intercept_exceptions |= (1 << DB_VECTOR);
+ set_exception_intercept(svm, DB_VECTOR);
if (vcpu->guest_debug & KVM_GUESTDBG_ENABLE) {
if (vcpu->guest_debug &
(KVM_GUESTDBG_SINGLESTEP | KVM_GUESTDBG_USE_HW_BP))
- svm->vmcb->control.intercept_exceptions |=
- 1 << DB_VECTOR;
+ set_exception_intercept(svm, DB_VECTOR);
if (vcpu->guest_debug & KVM_GUESTDBG_USE_SW_BP)
- svm->vmcb->control.intercept_exceptions |=
- 1 << BP_VECTOR;
+ set_exception_intercept(svm, BP_VECTOR);
} else
vcpu->guest_debug = 0;
}
@@ -1345,23 +1491,11 @@
else
svm->vmcb->save.dr7 = vcpu->arch.dr7;
+ mark_dirty(svm->vmcb, VMCB_DR);
+
update_db_intercept(vcpu);
}
-static void load_host_msrs(struct kvm_vcpu *vcpu)
-{
-#ifdef CONFIG_X86_64
- wrmsrl(MSR_GS_BASE, to_svm(vcpu)->host_gs_base);
-#endif
-}
-
-static void save_host_msrs(struct kvm_vcpu *vcpu)
-{
-#ifdef CONFIG_X86_64
- rdmsrl(MSR_GS_BASE, to_svm(vcpu)->host_gs_base);
-#endif
-}
-
static void new_asid(struct vcpu_svm *svm, struct svm_cpu_data *sd)
{
if (sd->next_asid > sd->max_asid) {
@@ -1372,6 +1506,8 @@
svm->asid_generation = sd->asid_generation;
svm->vmcb->control.asid = sd->next_asid++;
+
+ mark_dirty(svm->vmcb, VMCB_ASID);
}
static void svm_set_dr7(struct kvm_vcpu *vcpu, unsigned long value)
@@ -1379,20 +1515,40 @@
struct vcpu_svm *svm = to_svm(vcpu);
svm->vmcb->save.dr7 = value;
+ mark_dirty(svm->vmcb, VMCB_DR);
}
static int pf_interception(struct vcpu_svm *svm)
{
- u64 fault_address;
+ u64 fault_address = svm->vmcb->control.exit_info_2;
u32 error_code;
+ int r = 1;
- fault_address = svm->vmcb->control.exit_info_2;
- error_code = svm->vmcb->control.exit_info_1;
+ switch (svm->apf_reason) {
+ default:
+ error_code = svm->vmcb->control.exit_info_1;
- trace_kvm_page_fault(fault_address, error_code);
- if (!npt_enabled && kvm_event_needs_reinjection(&svm->vcpu))
- kvm_mmu_unprotect_page_virt(&svm->vcpu, fault_address);
- return kvm_mmu_page_fault(&svm->vcpu, fault_address, error_code);
+ trace_kvm_page_fault(fault_address, error_code);
+ if (!npt_enabled && kvm_event_needs_reinjection(&svm->vcpu))
+ kvm_mmu_unprotect_page_virt(&svm->vcpu, fault_address);
+ r = kvm_mmu_page_fault(&svm->vcpu, fault_address, error_code,
+ svm->vmcb->control.insn_bytes,
+ svm->vmcb->control.insn_len);
+ break;
+ case KVM_PV_REASON_PAGE_NOT_PRESENT:
+ svm->apf_reason = 0;
+ local_irq_disable();
+ kvm_async_pf_task_wait(fault_address);
+ local_irq_enable();
+ break;
+ case KVM_PV_REASON_PAGE_READY:
+ svm->apf_reason = 0;
+ local_irq_disable();
+ kvm_async_pf_task_wake(fault_address);
+ local_irq_enable();
+ break;
+ }
+ return r;
}
static int db_interception(struct vcpu_svm *svm)
@@ -1440,7 +1596,7 @@
{
int er;
- er = emulate_instruction(&svm->vcpu, 0, 0, EMULTYPE_TRAP_UD);
+ er = emulate_instruction(&svm->vcpu, EMULTYPE_TRAP_UD);
if (er != EMULATE_DONE)
kvm_queue_exception(&svm->vcpu, UD_VECTOR);
return 1;
@@ -1449,21 +1605,8 @@
static void svm_fpu_activate(struct kvm_vcpu *vcpu)
{
struct vcpu_svm *svm = to_svm(vcpu);
- u32 excp;
- if (is_nested(svm)) {
- u32 h_excp, n_excp;
-
- h_excp = svm->nested.hsave->control.intercept_exceptions;
- n_excp = svm->nested.intercept_exceptions;
- h_excp &= ~(1 << NM_VECTOR);
- excp = h_excp | n_excp;
- } else {
- excp = svm->vmcb->control.intercept_exceptions;
- excp &= ~(1 << NM_VECTOR);
- }
-
- svm->vmcb->control.intercept_exceptions = excp;
+ clr_exception_intercept(svm, NM_VECTOR);
svm->vcpu.fpu_active = 1;
update_cr0_intercept(svm);
@@ -1570,7 +1713,7 @@
string = (io_info & SVM_IOIO_STR_MASK) != 0;
in = (io_info & SVM_IOIO_TYPE_MASK) != 0;
if (string || in)
- return emulate_instruction(vcpu, 0, 0, 0) == EMULATE_DONE;
+ return emulate_instruction(vcpu, 0) == EMULATE_DONE;
port = io_info >> 16;
size = (io_info & SVM_IOIO_SIZE_MASK) >> SVM_IOIO_SIZE_SHIFT;
@@ -1624,17 +1767,19 @@
struct vcpu_svm *svm = to_svm(vcpu);
svm->vmcb->control.nested_cr3 = root;
- force_new_asid(vcpu);
+ mark_dirty(svm->vmcb, VMCB_NPT);
+ svm_flush_tlb(vcpu);
}
-static void nested_svm_inject_npf_exit(struct kvm_vcpu *vcpu)
+static void nested_svm_inject_npf_exit(struct kvm_vcpu *vcpu,
+ struct x86_exception *fault)
{
struct vcpu_svm *svm = to_svm(vcpu);
svm->vmcb->control.exit_code = SVM_EXIT_NPF;
svm->vmcb->control.exit_code_hi = 0;
- svm->vmcb->control.exit_info_1 = vcpu->arch.fault.error_code;
- svm->vmcb->control.exit_info_2 = vcpu->arch.fault.address;
+ svm->vmcb->control.exit_info_1 = fault->error_code;
+ svm->vmcb->control.exit_info_2 = fault->address;
nested_svm_vmexit(svm);
}
@@ -1680,7 +1825,7 @@
{
int vmexit;
- if (!is_nested(svm))
+ if (!is_guest_mode(&svm->vcpu))
return 0;
svm->vmcb->control.exit_code = SVM_EXIT_EXCP_BASE + nr;
@@ -1698,7 +1843,7 @@
/* This function returns true if it is save to enable the irq window */
static inline bool nested_svm_intr(struct vcpu_svm *svm)
{
- if (!is_nested(svm))
+ if (!is_guest_mode(&svm->vcpu))
return true;
if (!(svm->vcpu.arch.hflags & HF_VINTR_MASK))
@@ -1737,7 +1882,7 @@
/* This function returns true if it is save to enable the nmi window */
static inline bool nested_svm_nmi(struct vcpu_svm *svm)
{
- if (!is_nested(svm))
+ if (!is_guest_mode(&svm->vcpu))
return true;
if (!(svm->nested.intercept & (1ULL << INTERCEPT_NMI)))
@@ -1836,8 +1981,8 @@
return NESTED_EXIT_HOST;
break;
case SVM_EXIT_EXCP_BASE + PF_VECTOR:
- /* When we're shadowing, trap PFs */
- if (!npt_enabled)
+ /* When we're shadowing, trap PFs, but not async PF */
+ if (!npt_enabled && svm->apf_reason == 0)
return NESTED_EXIT_HOST;
break;
case SVM_EXIT_EXCP_BASE + NM_VECTOR:
@@ -1865,27 +2010,15 @@
case SVM_EXIT_IOIO:
vmexit = nested_svm_intercept_ioio(svm);
break;
- case SVM_EXIT_READ_CR0 ... SVM_EXIT_READ_CR8: {
- u32 cr_bits = 1 << (exit_code - SVM_EXIT_READ_CR0);
- if (svm->nested.intercept_cr_read & cr_bits)
+ case SVM_EXIT_READ_CR0 ... SVM_EXIT_WRITE_CR8: {
+ u32 bit = 1U << (exit_code - SVM_EXIT_READ_CR0);
+ if (svm->nested.intercept_cr & bit)
vmexit = NESTED_EXIT_DONE;
break;
}
- case SVM_EXIT_WRITE_CR0 ... SVM_EXIT_WRITE_CR8: {
- u32 cr_bits = 1 << (exit_code - SVM_EXIT_WRITE_CR0);
- if (svm->nested.intercept_cr_write & cr_bits)
- vmexit = NESTED_EXIT_DONE;
- break;
- }
- case SVM_EXIT_READ_DR0 ... SVM_EXIT_READ_DR7: {
- u32 dr_bits = 1 << (exit_code - SVM_EXIT_READ_DR0);
- if (svm->nested.intercept_dr_read & dr_bits)
- vmexit = NESTED_EXIT_DONE;
- break;
- }
- case SVM_EXIT_WRITE_DR0 ... SVM_EXIT_WRITE_DR7: {
- u32 dr_bits = 1 << (exit_code - SVM_EXIT_WRITE_DR0);
- if (svm->nested.intercept_dr_write & dr_bits)
+ case SVM_EXIT_READ_DR0 ... SVM_EXIT_WRITE_DR7: {
+ u32 bit = 1U << (exit_code - SVM_EXIT_READ_DR0);
+ if (svm->nested.intercept_dr & bit)
vmexit = NESTED_EXIT_DONE;
break;
}
@@ -1893,6 +2026,10 @@
u32 excp_bits = 1 << (exit_code - SVM_EXIT_EXCP_BASE);
if (svm->nested.intercept_exceptions & excp_bits)
vmexit = NESTED_EXIT_DONE;
+ /* async page fault always cause vmexit */
+ else if ((exit_code == SVM_EXIT_EXCP_BASE + PF_VECTOR) &&
+ svm->apf_reason != 0)
+ vmexit = NESTED_EXIT_DONE;
break;
}
case SVM_EXIT_ERR: {
@@ -1926,10 +2063,8 @@
struct vmcb_control_area *dst = &dst_vmcb->control;
struct vmcb_control_area *from = &from_vmcb->control;
- dst->intercept_cr_read = from->intercept_cr_read;
- dst->intercept_cr_write = from->intercept_cr_write;
- dst->intercept_dr_read = from->intercept_dr_read;
- dst->intercept_dr_write = from->intercept_dr_write;
+ dst->intercept_cr = from->intercept_cr;
+ dst->intercept_dr = from->intercept_dr;
dst->intercept_exceptions = from->intercept_exceptions;
dst->intercept = from->intercept;
dst->iopm_base_pa = from->iopm_base_pa;
@@ -1970,7 +2105,8 @@
if (!nested_vmcb)
return 1;
- /* Exit nested SVM mode */
+ /* Exit Guest-Mode */
+ leave_guest_mode(&svm->vcpu);
svm->nested.vmcb = 0;
/* Give the current vmcb to the guest */
@@ -1984,7 +2120,7 @@
nested_vmcb->save.idtr = vmcb->save.idtr;
nested_vmcb->save.efer = svm->vcpu.arch.efer;
nested_vmcb->save.cr0 = kvm_read_cr0(&svm->vcpu);
- nested_vmcb->save.cr3 = svm->vcpu.arch.cr3;
+ nested_vmcb->save.cr3 = kvm_read_cr3(&svm->vcpu);
nested_vmcb->save.cr2 = vmcb->save.cr2;
nested_vmcb->save.cr4 = svm->vcpu.arch.cr4;
nested_vmcb->save.rflags = vmcb->save.rflags;
@@ -2061,6 +2197,8 @@
svm->vmcb->save.cpl = 0;
svm->vmcb->control.exit_int_info = 0;
+ mark_all_dirty(svm->vmcb);
+
nested_svm_unmap(page);
nested_svm_uninit_mmu_context(&svm->vcpu);
@@ -2148,8 +2286,8 @@
nested_vmcb->control.event_inj,
nested_vmcb->control.nested_ctl);
- trace_kvm_nested_intercepts(nested_vmcb->control.intercept_cr_read,
- nested_vmcb->control.intercept_cr_write,
+ trace_kvm_nested_intercepts(nested_vmcb->control.intercept_cr & 0xffff,
+ nested_vmcb->control.intercept_cr >> 16,
nested_vmcb->control.intercept_exceptions,
nested_vmcb->control.intercept);
@@ -2177,7 +2315,7 @@
if (npt_enabled)
hsave->save.cr3 = vmcb->save.cr3;
else
- hsave->save.cr3 = svm->vcpu.arch.cr3;
+ hsave->save.cr3 = kvm_read_cr3(&svm->vcpu);
copy_vmcb_control_area(hsave, vmcb);
@@ -2229,14 +2367,12 @@
svm->nested.vmcb_iopm = nested_vmcb->control.iopm_base_pa & ~0x0fffULL;
/* cache intercepts */
- svm->nested.intercept_cr_read = nested_vmcb->control.intercept_cr_read;
- svm->nested.intercept_cr_write = nested_vmcb->control.intercept_cr_write;
- svm->nested.intercept_dr_read = nested_vmcb->control.intercept_dr_read;
- svm->nested.intercept_dr_write = nested_vmcb->control.intercept_dr_write;
+ svm->nested.intercept_cr = nested_vmcb->control.intercept_cr;
+ svm->nested.intercept_dr = nested_vmcb->control.intercept_dr;
svm->nested.intercept_exceptions = nested_vmcb->control.intercept_exceptions;
svm->nested.intercept = nested_vmcb->control.intercept;
- force_new_asid(&svm->vcpu);
+ svm_flush_tlb(&svm->vcpu);
svm->vmcb->control.int_ctl = nested_vmcb->control.int_ctl | V_INTR_MASKING_MASK;
if (nested_vmcb->control.int_ctl & V_INTR_MASKING_MASK)
svm->vcpu.arch.hflags |= HF_VINTR_MASK;
@@ -2245,29 +2381,12 @@
if (svm->vcpu.arch.hflags & HF_VINTR_MASK) {
/* We only want the cr8 intercept bits of the guest */
- svm->vmcb->control.intercept_cr_read &= ~INTERCEPT_CR8_MASK;
- svm->vmcb->control.intercept_cr_write &= ~INTERCEPT_CR8_MASK;
+ clr_cr_intercept(svm, INTERCEPT_CR8_READ);
+ clr_cr_intercept(svm, INTERCEPT_CR8_WRITE);
}
/* We don't want to see VMMCALLs from a nested guest */
- svm->vmcb->control.intercept &= ~(1ULL << INTERCEPT_VMMCALL);
-
- /*
- * We don't want a nested guest to be more powerful than the guest, so
- * all intercepts are ORed
- */
- svm->vmcb->control.intercept_cr_read |=
- nested_vmcb->control.intercept_cr_read;
- svm->vmcb->control.intercept_cr_write |=
- nested_vmcb->control.intercept_cr_write;
- svm->vmcb->control.intercept_dr_read |=
- nested_vmcb->control.intercept_dr_read;
- svm->vmcb->control.intercept_dr_write |=
- nested_vmcb->control.intercept_dr_write;
- svm->vmcb->control.intercept_exceptions |=
- nested_vmcb->control.intercept_exceptions;
-
- svm->vmcb->control.intercept |= nested_vmcb->control.intercept;
+ clr_intercept(svm, INTERCEPT_VMMCALL);
svm->vmcb->control.lbr_ctl = nested_vmcb->control.lbr_ctl;
svm->vmcb->control.int_vector = nested_vmcb->control.int_vector;
@@ -2278,11 +2397,21 @@
nested_svm_unmap(page);
- /* nested_vmcb is our indicator if nested SVM is activated */
+ /* Enter Guest-Mode */
+ enter_guest_mode(&svm->vcpu);
+
+ /*
+ * Merge guest and host intercepts - must be called with vcpu in
+ * guest-mode to take affect here
+ */
+ recalc_intercepts(svm);
+
svm->nested.vmcb = vmcb_gpa;
enable_gif(svm);
+ mark_all_dirty(svm->vmcb);
+
return true;
}
@@ -2400,6 +2529,8 @@
svm_clear_vintr(svm);
svm->vmcb->control.int_ctl &= ~V_IRQ_MASK;
+ mark_dirty(svm->vmcb, VMCB_INTR);
+
return 1;
}
@@ -2426,6 +2557,19 @@
return 1;
}
+static int xsetbv_interception(struct vcpu_svm *svm)
+{
+ u64 new_bv = kvm_read_edx_eax(&svm->vcpu);
+ u32 index = kvm_register_read(&svm->vcpu, VCPU_REGS_RCX);
+
+ if (kvm_set_xcr(&svm->vcpu, index, new_bv) == 0) {
+ svm->next_rip = kvm_rip_read(&svm->vcpu) + 3;
+ skip_emulated_instruction(&svm->vcpu);
+ }
+
+ return 1;
+}
+
static int invalid_op_interception(struct vcpu_svm *svm)
{
kvm_queue_exception(&svm->vcpu, UD_VECTOR);
@@ -2507,19 +2651,92 @@
static int iret_interception(struct vcpu_svm *svm)
{
++svm->vcpu.stat.nmi_window_exits;
- svm->vmcb->control.intercept &= ~(1ULL << INTERCEPT_IRET);
+ clr_intercept(svm, INTERCEPT_IRET);
svm->vcpu.arch.hflags |= HF_IRET_MASK;
return 1;
}
static int invlpg_interception(struct vcpu_svm *svm)
{
- return emulate_instruction(&svm->vcpu, 0, 0, 0) == EMULATE_DONE;
+ if (!static_cpu_has(X86_FEATURE_DECODEASSISTS))
+ return emulate_instruction(&svm->vcpu, 0) == EMULATE_DONE;
+
+ kvm_mmu_invlpg(&svm->vcpu, svm->vmcb->control.exit_info_1);
+ skip_emulated_instruction(&svm->vcpu);
+ return 1;
}
static int emulate_on_interception(struct vcpu_svm *svm)
{
- return emulate_instruction(&svm->vcpu, 0, 0, 0) == EMULATE_DONE;
+ return emulate_instruction(&svm->vcpu, 0) == EMULATE_DONE;
+}
+
+#define CR_VALID (1ULL << 63)
+
+static int cr_interception(struct vcpu_svm *svm)
+{
+ int reg, cr;
+ unsigned long val;
+ int err;
+
+ if (!static_cpu_has(X86_FEATURE_DECODEASSISTS))
+ return emulate_on_interception(svm);
+
+ if (unlikely((svm->vmcb->control.exit_info_1 & CR_VALID) == 0))
+ return emulate_on_interception(svm);
+
+ reg = svm->vmcb->control.exit_info_1 & SVM_EXITINFO_REG_MASK;
+ cr = svm->vmcb->control.exit_code - SVM_EXIT_READ_CR0;
+
+ err = 0;
+ if (cr >= 16) { /* mov to cr */
+ cr -= 16;
+ val = kvm_register_read(&svm->vcpu, reg);
+ switch (cr) {
+ case 0:
+ err = kvm_set_cr0(&svm->vcpu, val);
+ break;
+ case 3:
+ err = kvm_set_cr3(&svm->vcpu, val);
+ break;
+ case 4:
+ err = kvm_set_cr4(&svm->vcpu, val);
+ break;
+ case 8:
+ err = kvm_set_cr8(&svm->vcpu, val);
+ break;
+ default:
+ WARN(1, "unhandled write to CR%d", cr);
+ kvm_queue_exception(&svm->vcpu, UD_VECTOR);
+ return 1;
+ }
+ } else { /* mov from cr */
+ switch (cr) {
+ case 0:
+ val = kvm_read_cr0(&svm->vcpu);
+ break;
+ case 2:
+ val = svm->vcpu.arch.cr2;
+ break;
+ case 3:
+ val = kvm_read_cr3(&svm->vcpu);
+ break;
+ case 4:
+ val = kvm_read_cr4(&svm->vcpu);
+ break;
+ case 8:
+ val = kvm_get_cr8(&svm->vcpu);
+ break;
+ default:
+ WARN(1, "unhandled read from CR%d", cr);
+ kvm_queue_exception(&svm->vcpu, UD_VECTOR);
+ return 1;
+ }
+ kvm_register_write(&svm->vcpu, reg, val);
+ }
+ kvm_complete_insn_gp(&svm->vcpu, err);
+
+ return 1;
}
static int cr0_write_interception(struct vcpu_svm *svm)
@@ -2527,7 +2744,7 @@
struct kvm_vcpu *vcpu = &svm->vcpu;
int r;
- r = emulate_instruction(&svm->vcpu, 0, 0, 0);
+ r = cr_interception(svm);
if (svm->nested.vmexit_rip) {
kvm_register_write(vcpu, VCPU_REGS_RIP, svm->nested.vmexit_rip);
@@ -2536,22 +2753,47 @@
svm->nested.vmexit_rip = 0;
}
- return r == EMULATE_DONE;
+ return r;
+}
+
+static int dr_interception(struct vcpu_svm *svm)
+{
+ int reg, dr;
+ unsigned long val;
+ int err;
+
+ if (!boot_cpu_has(X86_FEATURE_DECODEASSISTS))
+ return emulate_on_interception(svm);
+
+ reg = svm->vmcb->control.exit_info_1 & SVM_EXITINFO_REG_MASK;
+ dr = svm->vmcb->control.exit_code - SVM_EXIT_READ_DR0;
+
+ if (dr >= 16) { /* mov to DRn */
+ val = kvm_register_read(&svm->vcpu, reg);
+ kvm_set_dr(&svm->vcpu, dr - 16, val);
+ } else {
+ err = kvm_get_dr(&svm->vcpu, dr, &val);
+ if (!err)
+ kvm_register_write(&svm->vcpu, reg, val);
+ }
+
+ return 1;
}
static int cr8_write_interception(struct vcpu_svm *svm)
{
struct kvm_run *kvm_run = svm->vcpu.run;
+ int r;
u8 cr8_prev = kvm_get_cr8(&svm->vcpu);
/* instruction emulation calls kvm_set_cr8() */
- emulate_instruction(&svm->vcpu, 0, 0, 0);
+ r = cr_interception(svm);
if (irqchip_in_kernel(svm->vcpu.kvm)) {
- svm->vmcb->control.intercept_cr_write &= ~INTERCEPT_CR8_MASK;
- return 1;
+ clr_cr_intercept(svm, INTERCEPT_CR8_WRITE);
+ return r;
}
if (cr8_prev <= kvm_get_cr8(&svm->vcpu))
- return 1;
+ return r;
kvm_run->exit_reason = KVM_EXIT_SET_TPR;
return 0;
}
@@ -2562,14 +2804,9 @@
switch (ecx) {
case MSR_IA32_TSC: {
- u64 tsc_offset;
+ struct vmcb *vmcb = get_host_vmcb(svm);
- if (is_nested(svm))
- tsc_offset = svm->nested.hsave->control.tsc_offset;
- else
- tsc_offset = svm->vmcb->control.tsc_offset;
-
- *data = tsc_offset + native_read_tsc();
+ *data = vmcb->control.tsc_offset + native_read_tsc();
break;
}
case MSR_STAR:
@@ -2714,7 +2951,7 @@
svm->vmcb->save.sysenter_esp = data;
break;
case MSR_IA32_DEBUGCTLMSR:
- if (!svm_has(SVM_FEATURE_LBRV)) {
+ if (!boot_cpu_has(X86_FEATURE_LBRV)) {
pr_unimpl(vcpu, "%s: MSR_IA32_DEBUGCTL 0x%llx, nop\n",
__func__, data);
break;
@@ -2723,6 +2960,7 @@
return 1;
svm->vmcb->save.dbgctl = data;
+ mark_dirty(svm->vmcb, VMCB_LBR);
if (data & (1ULL<<0))
svm_enable_lbrv(svm);
else
@@ -2775,6 +3013,7 @@
kvm_make_request(KVM_REQ_EVENT, &svm->vcpu);
svm_clear_vintr(svm);
svm->vmcb->control.int_ctl &= ~V_IRQ_MASK;
+ mark_dirty(svm->vmcb, VMCB_INTR);
/*
* If the user space waits to inject interrupts, exit as soon as
* possible
@@ -2797,31 +3036,31 @@
}
static int (*svm_exit_handlers[])(struct vcpu_svm *svm) = {
- [SVM_EXIT_READ_CR0] = emulate_on_interception,
- [SVM_EXIT_READ_CR3] = emulate_on_interception,
- [SVM_EXIT_READ_CR4] = emulate_on_interception,
- [SVM_EXIT_READ_CR8] = emulate_on_interception,
+ [SVM_EXIT_READ_CR0] = cr_interception,
+ [SVM_EXIT_READ_CR3] = cr_interception,
+ [SVM_EXIT_READ_CR4] = cr_interception,
+ [SVM_EXIT_READ_CR8] = cr_interception,
[SVM_EXIT_CR0_SEL_WRITE] = emulate_on_interception,
[SVM_EXIT_WRITE_CR0] = cr0_write_interception,
- [SVM_EXIT_WRITE_CR3] = emulate_on_interception,
- [SVM_EXIT_WRITE_CR4] = emulate_on_interception,
+ [SVM_EXIT_WRITE_CR3] = cr_interception,
+ [SVM_EXIT_WRITE_CR4] = cr_interception,
[SVM_EXIT_WRITE_CR8] = cr8_write_interception,
- [SVM_EXIT_READ_DR0] = emulate_on_interception,
- [SVM_EXIT_READ_DR1] = emulate_on_interception,
- [SVM_EXIT_READ_DR2] = emulate_on_interception,
- [SVM_EXIT_READ_DR3] = emulate_on_interception,
- [SVM_EXIT_READ_DR4] = emulate_on_interception,
- [SVM_EXIT_READ_DR5] = emulate_on_interception,
- [SVM_EXIT_READ_DR6] = emulate_on_interception,
- [SVM_EXIT_READ_DR7] = emulate_on_interception,
- [SVM_EXIT_WRITE_DR0] = emulate_on_interception,
- [SVM_EXIT_WRITE_DR1] = emulate_on_interception,
- [SVM_EXIT_WRITE_DR2] = emulate_on_interception,
- [SVM_EXIT_WRITE_DR3] = emulate_on_interception,
- [SVM_EXIT_WRITE_DR4] = emulate_on_interception,
- [SVM_EXIT_WRITE_DR5] = emulate_on_interception,
- [SVM_EXIT_WRITE_DR6] = emulate_on_interception,
- [SVM_EXIT_WRITE_DR7] = emulate_on_interception,
+ [SVM_EXIT_READ_DR0] = dr_interception,
+ [SVM_EXIT_READ_DR1] = dr_interception,
+ [SVM_EXIT_READ_DR2] = dr_interception,
+ [SVM_EXIT_READ_DR3] = dr_interception,
+ [SVM_EXIT_READ_DR4] = dr_interception,
+ [SVM_EXIT_READ_DR5] = dr_interception,
+ [SVM_EXIT_READ_DR6] = dr_interception,
+ [SVM_EXIT_READ_DR7] = dr_interception,
+ [SVM_EXIT_WRITE_DR0] = dr_interception,
+ [SVM_EXIT_WRITE_DR1] = dr_interception,
+ [SVM_EXIT_WRITE_DR2] = dr_interception,
+ [SVM_EXIT_WRITE_DR3] = dr_interception,
+ [SVM_EXIT_WRITE_DR4] = dr_interception,
+ [SVM_EXIT_WRITE_DR5] = dr_interception,
+ [SVM_EXIT_WRITE_DR6] = dr_interception,
+ [SVM_EXIT_WRITE_DR7] = dr_interception,
[SVM_EXIT_EXCP_BASE + DB_VECTOR] = db_interception,
[SVM_EXIT_EXCP_BASE + BP_VECTOR] = bp_interception,
[SVM_EXIT_EXCP_BASE + UD_VECTOR] = ud_interception,
@@ -2854,6 +3093,7 @@
[SVM_EXIT_WBINVD] = emulate_on_interception,
[SVM_EXIT_MONITOR] = invalid_op_interception,
[SVM_EXIT_MWAIT] = invalid_op_interception,
+ [SVM_EXIT_XSETBV] = xsetbv_interception,
[SVM_EXIT_NPF] = pf_interception,
};
@@ -2864,10 +3104,10 @@
struct vmcb_save_area *save = &svm->vmcb->save;
pr_err("VMCB Control Area:\n");
- pr_err("cr_read: %04x\n", control->intercept_cr_read);
- pr_err("cr_write: %04x\n", control->intercept_cr_write);
- pr_err("dr_read: %04x\n", control->intercept_dr_read);
- pr_err("dr_write: %04x\n", control->intercept_dr_write);
+ pr_err("cr_read: %04x\n", control->intercept_cr & 0xffff);
+ pr_err("cr_write: %04x\n", control->intercept_cr >> 16);
+ pr_err("dr_read: %04x\n", control->intercept_dr & 0xffff);
+ pr_err("dr_write: %04x\n", control->intercept_dr >> 16);
pr_err("exceptions: %08x\n", control->intercept_exceptions);
pr_err("intercepts: %016llx\n", control->intercept);
pr_err("pause filter count: %d\n", control->pause_filter_count);
@@ -2950,15 +3190,23 @@
}
+static void svm_get_exit_info(struct kvm_vcpu *vcpu, u64 *info1, u64 *info2)
+{
+ struct vmcb_control_area *control = &to_svm(vcpu)->vmcb->control;
+
+ *info1 = control->exit_info_1;
+ *info2 = control->exit_info_2;
+}
+
static int handle_exit(struct kvm_vcpu *vcpu)
{
struct vcpu_svm *svm = to_svm(vcpu);
struct kvm_run *kvm_run = vcpu->run;
u32 exit_code = svm->vmcb->control.exit_code;
- trace_kvm_exit(exit_code, vcpu);
+ trace_kvm_exit(exit_code, vcpu, KVM_ISA_SVM);
- if (!(svm->vmcb->control.intercept_cr_write & INTERCEPT_CR0_MASK))
+ if (!is_cr_intercept(svm, INTERCEPT_CR0_WRITE))
vcpu->arch.cr0 = svm->vmcb->save.cr0;
if (npt_enabled)
vcpu->arch.cr3 = svm->vmcb->save.cr3;
@@ -2970,7 +3218,7 @@
return 1;
}
- if (is_nested(svm)) {
+ if (is_guest_mode(vcpu)) {
int vmexit;
trace_kvm_nested_vmexit(svm->vmcb->save.rip, exit_code,
@@ -3033,7 +3281,6 @@
struct svm_cpu_data *sd = per_cpu(svm_data, cpu);
- svm->vmcb->control.tlb_ctl = TLB_CONTROL_DO_NOTHING;
/* FIXME: handle wraparound of asid_generation */
if (svm->asid_generation != sd->asid_generation)
new_asid(svm, sd);
@@ -3045,7 +3292,7 @@
svm->vmcb->control.event_inj = SVM_EVTINJ_VALID | SVM_EVTINJ_TYPE_NMI;
vcpu->arch.hflags |= HF_NMI_MASK;
- svm->vmcb->control.intercept |= (1ULL << INTERCEPT_IRET);
+ set_intercept(svm, INTERCEPT_IRET);
++vcpu->stat.nmi_injections;
}
@@ -3058,6 +3305,7 @@
control->int_ctl &= ~V_INTR_PRIO_MASK;
control->int_ctl |= V_IRQ_MASK |
((/*control->int_vector >> 4*/ 0xf) << V_INTR_PRIO_SHIFT);
+ mark_dirty(svm->vmcb, VMCB_INTR);
}
static void svm_set_irq(struct kvm_vcpu *vcpu)
@@ -3077,14 +3325,14 @@
{
struct vcpu_svm *svm = to_svm(vcpu);
- if (is_nested(svm) && (vcpu->arch.hflags & HF_VINTR_MASK))
+ if (is_guest_mode(vcpu) && (vcpu->arch.hflags & HF_VINTR_MASK))
return;
if (irr == -1)
return;
if (tpr >= irr)
- svm->vmcb->control.intercept_cr_write |= INTERCEPT_CR8_MASK;
+ set_cr_intercept(svm, INTERCEPT_CR8_WRITE);
}
static int svm_nmi_allowed(struct kvm_vcpu *vcpu)
@@ -3112,10 +3360,10 @@
if (masked) {
svm->vcpu.arch.hflags |= HF_NMI_MASK;
- svm->vmcb->control.intercept |= (1ULL << INTERCEPT_IRET);
+ set_intercept(svm, INTERCEPT_IRET);
} else {
svm->vcpu.arch.hflags &= ~HF_NMI_MASK;
- svm->vmcb->control.intercept &= ~(1ULL << INTERCEPT_IRET);
+ clr_intercept(svm, INTERCEPT_IRET);
}
}
@@ -3131,7 +3379,7 @@
ret = !!(vmcb->save.rflags & X86_EFLAGS_IF);
- if (is_nested(svm))
+ if (is_guest_mode(vcpu))
return ret && !(svm->vcpu.arch.hflags & HF_VINTR_MASK);
return ret;
@@ -3177,7 +3425,12 @@
static void svm_flush_tlb(struct kvm_vcpu *vcpu)
{
- force_new_asid(vcpu);
+ struct vcpu_svm *svm = to_svm(vcpu);
+
+ if (static_cpu_has(X86_FEATURE_FLUSHBYASID))
+ svm->vmcb->control.tlb_ctl = TLB_CONTROL_FLUSH_ASID;
+ else
+ svm->asid_generation--;
}
static void svm_prepare_guest_switch(struct kvm_vcpu *vcpu)
@@ -3188,10 +3441,10 @@
{
struct vcpu_svm *svm = to_svm(vcpu);
- if (is_nested(svm) && (vcpu->arch.hflags & HF_VINTR_MASK))
+ if (is_guest_mode(vcpu) && (vcpu->arch.hflags & HF_VINTR_MASK))
return;
- if (!(svm->vmcb->control.intercept_cr_write & INTERCEPT_CR8_MASK)) {
+ if (!is_cr_intercept(svm, INTERCEPT_CR8_WRITE)) {
int cr8 = svm->vmcb->control.int_ctl & V_TPR_MASK;
kvm_set_cr8(vcpu, cr8);
}
@@ -3202,7 +3455,7 @@
struct vcpu_svm *svm = to_svm(vcpu);
u64 cr8;
- if (is_nested(svm) && (vcpu->arch.hflags & HF_VINTR_MASK))
+ if (is_guest_mode(vcpu) && (vcpu->arch.hflags & HF_VINTR_MASK))
return;
cr8 = kvm_get_cr8(vcpu);
@@ -3289,9 +3542,6 @@
static void svm_vcpu_run(struct kvm_vcpu *vcpu)
{
struct vcpu_svm *svm = to_svm(vcpu);
- u16 fs_selector;
- u16 gs_selector;
- u16 ldt_selector;
svm->vmcb->save.rax = vcpu->arch.regs[VCPU_REGS_RAX];
svm->vmcb->save.rsp = vcpu->arch.regs[VCPU_REGS_RSP];
@@ -3308,10 +3558,6 @@
sync_lapic_to_cr8(vcpu);
- save_host_msrs(vcpu);
- savesegment(fs, fs_selector);
- savesegment(gs, gs_selector);
- ldt_selector = kvm_read_ldt();
svm->vmcb->save.cr2 = vcpu->arch.cr2;
clgi();
@@ -3389,19 +3635,10 @@
#endif
);
- vcpu->arch.cr2 = svm->vmcb->save.cr2;
- vcpu->arch.regs[VCPU_REGS_RAX] = svm->vmcb->save.rax;
- vcpu->arch.regs[VCPU_REGS_RSP] = svm->vmcb->save.rsp;
- vcpu->arch.regs[VCPU_REGS_RIP] = svm->vmcb->save.rip;
-
- load_host_msrs(vcpu);
- kvm_load_ldt(ldt_selector);
- loadsegment(fs, fs_selector);
#ifdef CONFIG_X86_64
- load_gs_index(gs_selector);
- wrmsrl(MSR_KERNEL_GS_BASE, current->thread.gs);
+ wrmsrl(MSR_GS_BASE, svm->host.gs_base);
#else
- loadsegment(gs, gs_selector);
+ loadsegment(fs, svm->host.fs);
#endif
reload_tss(vcpu);
@@ -3410,10 +3647,21 @@
stgi();
+ vcpu->arch.cr2 = svm->vmcb->save.cr2;
+ vcpu->arch.regs[VCPU_REGS_RAX] = svm->vmcb->save.rax;
+ vcpu->arch.regs[VCPU_REGS_RSP] = svm->vmcb->save.rsp;
+ vcpu->arch.regs[VCPU_REGS_RIP] = svm->vmcb->save.rip;
+
sync_cr8_to_lapic(vcpu);
svm->next_rip = 0;
+ svm->vmcb->control.tlb_ctl = TLB_CONTROL_DO_NOTHING;
+
+ /* if exit due to PF check for async PF */
+ if (svm->vmcb->control.exit_code == SVM_EXIT_EXCP_BASE + PF_VECTOR)
+ svm->apf_reason = kvm_read_and_reset_pf_reason();
+
if (npt_enabled) {
vcpu->arch.regs_avail &= ~(1 << VCPU_EXREG_PDPTR);
vcpu->arch.regs_dirty &= ~(1 << VCPU_EXREG_PDPTR);
@@ -3426,6 +3674,8 @@
if (unlikely(svm->vmcb->control.exit_code ==
SVM_EXIT_EXCP_BASE + MC_VECTOR))
svm_handle_mce(svm);
+
+ mark_all_clean(svm->vmcb);
}
#undef R
@@ -3435,7 +3685,8 @@
struct vcpu_svm *svm = to_svm(vcpu);
svm->vmcb->save.cr3 = root;
- force_new_asid(vcpu);
+ mark_dirty(svm->vmcb, VMCB_CR);
+ svm_flush_tlb(vcpu);
}
static void set_tdp_cr3(struct kvm_vcpu *vcpu, unsigned long root)
@@ -3443,11 +3694,13 @@
struct vcpu_svm *svm = to_svm(vcpu);
svm->vmcb->control.nested_cr3 = root;
+ mark_dirty(svm->vmcb, VMCB_NPT);
/* Also sync guest cr3 here in case we live migrate */
- svm->vmcb->save.cr3 = vcpu->arch.cr3;
+ svm->vmcb->save.cr3 = kvm_read_cr3(vcpu);
+ mark_dirty(svm->vmcb, VMCB_CR);
- force_new_asid(vcpu);
+ svm_flush_tlb(vcpu);
}
static int is_disabled(void)
@@ -3494,10 +3747,6 @@
static void svm_set_supported_cpuid(u32 func, struct kvm_cpuid_entry2 *entry)
{
switch (func) {
- case 0x00000001:
- /* Mask out xsave bit as long as it is not supported by SVM */
- entry->ecx &= ~(bit(X86_FEATURE_XSAVE));
- break;
case 0x80000001:
if (nested)
entry->ecx |= (1 << 2); /* Set SVM bit */
@@ -3511,7 +3760,7 @@
additional features */
/* Support next_rip if host supports it */
- if (svm_has(SVM_FEATURE_NRIP))
+ if (boot_cpu_has(X86_FEATURE_NRIPS))
entry->edx |= SVM_FEATURE_NRIP;
/* Support NPT for the guest if enabled */
@@ -3571,6 +3820,7 @@
{ SVM_EXIT_WBINVD, "wbinvd" },
{ SVM_EXIT_MONITOR, "monitor" },
{ SVM_EXIT_MWAIT, "mwait" },
+ { SVM_EXIT_XSETBV, "xsetbv" },
{ SVM_EXIT_NPF, "npf" },
{ -1, NULL }
};
@@ -3594,9 +3844,7 @@
{
struct vcpu_svm *svm = to_svm(vcpu);
- svm->vmcb->control.intercept_exceptions |= 1 << NM_VECTOR;
- if (is_nested(svm))
- svm->nested.hsave->control.intercept_exceptions |= 1 << NM_VECTOR;
+ set_exception_intercept(svm, NM_VECTOR);
update_cr0_intercept(svm);
}
@@ -3627,6 +3875,7 @@
.get_cpl = svm_get_cpl,
.get_cs_db_l_bits = kvm_get_cs_db_l_bits,
.decache_cr0_guest_bits = svm_decache_cr0_guest_bits,
+ .decache_cr3 = svm_decache_cr3,
.decache_cr4_guest_bits = svm_decache_cr4_guest_bits,
.set_cr0 = svm_set_cr0,
.set_cr3 = svm_set_cr3,
@@ -3667,7 +3916,9 @@
.get_tdp_level = get_npt_level,
.get_mt_mask = svm_get_mt_mask,
+ .get_exit_info = svm_get_exit_info,
.exit_reasons_str = svm_exit_reasons_str,
+
.get_lpage_level = svm_get_lpage_level,
.cpuid_update = svm_cpuid_update,
diff --git a/arch/x86/kvm/trace.h b/arch/x86/kvm/trace.h
index a6544b8..1357d7c 100644
--- a/arch/x86/kvm/trace.h
+++ b/arch/x86/kvm/trace.h
@@ -178,27 +178,36 @@
#define trace_kvm_apic_read(reg, val) trace_kvm_apic(0, reg, val)
#define trace_kvm_apic_write(reg, val) trace_kvm_apic(1, reg, val)
+#define KVM_ISA_VMX 1
+#define KVM_ISA_SVM 2
+
/*
* Tracepoint for kvm guest exit:
*/
TRACE_EVENT(kvm_exit,
- TP_PROTO(unsigned int exit_reason, struct kvm_vcpu *vcpu),
- TP_ARGS(exit_reason, vcpu),
+ TP_PROTO(unsigned int exit_reason, struct kvm_vcpu *vcpu, u32 isa),
+ TP_ARGS(exit_reason, vcpu, isa),
TP_STRUCT__entry(
__field( unsigned int, exit_reason )
__field( unsigned long, guest_rip )
+ __field( u32, isa )
+ __field( u64, info1 )
+ __field( u64, info2 )
),
TP_fast_assign(
__entry->exit_reason = exit_reason;
__entry->guest_rip = kvm_rip_read(vcpu);
+ __entry->isa = isa;
+ kvm_x86_ops->get_exit_info(vcpu, &__entry->info1,
+ &__entry->info2);
),
- TP_printk("reason %s rip 0x%lx",
+ TP_printk("reason %s rip 0x%lx info %llx %llx",
ftrace_print_symbols_seq(p, __entry->exit_reason,
kvm_x86_ops->exit_reasons_str),
- __entry->guest_rip)
+ __entry->guest_rip, __entry->info1, __entry->info2)
);
/*
diff --git a/arch/x86/kvm/vmx.c b/arch/x86/kvm/vmx.c
index 81fcbe9..bf89ec2 100644
--- a/arch/x86/kvm/vmx.c
+++ b/arch/x86/kvm/vmx.c
@@ -69,6 +69,9 @@
static int __read_mostly vmm_exclusive = 1;
module_param(vmm_exclusive, bool, S_IRUGO);
+static int __read_mostly yield_on_hlt = 1;
+module_param(yield_on_hlt, bool, S_IRUGO);
+
#define KVM_GUEST_CR0_MASK_UNRESTRICTED_GUEST \
(X86_CR0_WP | X86_CR0_NE | X86_CR0_NW | X86_CR0_CD)
#define KVM_GUEST_CR0_MASK \
@@ -177,6 +180,7 @@
static u64 construct_eptp(unsigned long root_hpa);
static void kvm_cpu_vmxon(u64 addr);
static void kvm_cpu_vmxoff(void);
+static void vmx_set_cr3(struct kvm_vcpu *vcpu, unsigned long cr3);
static DEFINE_PER_CPU(struct vmcs *, vmxarea);
static DEFINE_PER_CPU(struct vmcs *, current_vmcs);
@@ -188,6 +192,8 @@
static unsigned long *vmx_msr_bitmap_legacy;
static unsigned long *vmx_msr_bitmap_longmode;
+static bool cpu_has_load_ia32_efer;
+
static DECLARE_BITMAP(vmx_vpid_bitmap, VMX_NR_VPIDS);
static DEFINE_SPINLOCK(vmx_vpid_lock);
@@ -472,7 +478,7 @@
u8 error;
asm volatile (__ex(ASM_VMX_VMCLEAR_RAX) "; setna %0"
- : "=g"(error) : "a"(&phys_addr), "m"(phys_addr)
+ : "=qm"(error) : "a"(&phys_addr), "m"(phys_addr)
: "cc", "memory");
if (error)
printk(KERN_ERR "kvm: vmclear fail: %p/%llx\n",
@@ -485,7 +491,7 @@
u8 error;
asm volatile (__ex(ASM_VMX_VMPTRLD_RAX) "; setna %0"
- : "=g"(error) : "a"(&phys_addr), "m"(phys_addr)
+ : "=qm"(error) : "a"(&phys_addr), "m"(phys_addr)
: "cc", "memory");
if (error)
printk(KERN_ERR "kvm: vmptrld %p/%llx fail\n",
@@ -565,10 +571,10 @@
static unsigned long vmcs_readl(unsigned long field)
{
- unsigned long value;
+ unsigned long value = 0;
asm volatile (__ex(ASM_VMX_VMREAD_RDX_RAX)
- : "=a"(value) : "d"(field) : "cc");
+ : "+a"(value) : "d"(field) : "cc");
return value;
}
@@ -661,6 +667,12 @@
unsigned i;
struct msr_autoload *m = &vmx->msr_autoload;
+ if (msr == MSR_EFER && cpu_has_load_ia32_efer) {
+ vmcs_clear_bits(VM_ENTRY_CONTROLS, VM_ENTRY_LOAD_IA32_EFER);
+ vmcs_clear_bits(VM_EXIT_CONTROLS, VM_EXIT_LOAD_IA32_EFER);
+ return;
+ }
+
for (i = 0; i < m->nr; ++i)
if (m->guest[i].index == msr)
break;
@@ -680,6 +692,14 @@
unsigned i;
struct msr_autoload *m = &vmx->msr_autoload;
+ if (msr == MSR_EFER && cpu_has_load_ia32_efer) {
+ vmcs_write64(GUEST_IA32_EFER, guest_val);
+ vmcs_write64(HOST_IA32_EFER, host_val);
+ vmcs_set_bits(VM_ENTRY_CONTROLS, VM_ENTRY_LOAD_IA32_EFER);
+ vmcs_set_bits(VM_EXIT_CONTROLS, VM_EXIT_LOAD_IA32_EFER);
+ return;
+ }
+
for (i = 0; i < m->nr; ++i)
if (m->guest[i].index == msr)
break;
@@ -1009,6 +1029,17 @@
vmx_set_interrupt_shadow(vcpu, 0);
}
+static void vmx_clear_hlt(struct kvm_vcpu *vcpu)
+{
+ /* Ensure that we clear the HLT state in the VMCS. We don't need to
+ * explicitly skip the instruction because if the HLT state is set, then
+ * the instruction is already executing and RIP has already been
+ * advanced. */
+ if (!yield_on_hlt &&
+ vmcs_read32(GUEST_ACTIVITY_STATE) == GUEST_ACTIVITY_HLT)
+ vmcs_write32(GUEST_ACTIVITY_STATE, GUEST_ACTIVITY_ACTIVE);
+}
+
static void vmx_queue_exception(struct kvm_vcpu *vcpu, unsigned nr,
bool has_error_code, u32 error_code,
bool reinject)
@@ -1035,6 +1066,7 @@
intr_info |= INTR_TYPE_HARD_EXCEPTION;
vmcs_write32(VM_ENTRY_INTR_INFO_FIELD, intr_info);
+ vmx_clear_hlt(vcpu);
}
static bool vmx_rdtscp_supported(void)
@@ -1305,8 +1337,11 @@
&& tboot_enabled())
return 1;
if (!(msr & FEATURE_CONTROL_VMXON_ENABLED_OUTSIDE_SMX)
- && !tboot_enabled())
+ && !tboot_enabled()) {
+ printk(KERN_WARNING "kvm: disable TXT in the BIOS or "
+ " activate TXT before enabling KVM\n");
return 1;
+ }
}
return 0;
@@ -1400,6 +1435,14 @@
return 0;
}
+static __init bool allow_1_setting(u32 msr, u32 ctl)
+{
+ u32 vmx_msr_low, vmx_msr_high;
+
+ rdmsr(msr, vmx_msr_low, vmx_msr_high);
+ return vmx_msr_high & ctl;
+}
+
static __init int setup_vmcs_config(struct vmcs_config *vmcs_conf)
{
u32 vmx_msr_low, vmx_msr_high;
@@ -1416,7 +1459,7 @@
&_pin_based_exec_control) < 0)
return -EIO;
- min = CPU_BASED_HLT_EXITING |
+ min =
#ifdef CONFIG_X86_64
CPU_BASED_CR8_LOAD_EXITING |
CPU_BASED_CR8_STORE_EXITING |
@@ -1429,6 +1472,10 @@
CPU_BASED_MWAIT_EXITING |
CPU_BASED_MONITOR_EXITING |
CPU_BASED_INVLPG_EXITING;
+
+ if (yield_on_hlt)
+ min |= CPU_BASED_HLT_EXITING;
+
opt = CPU_BASED_TPR_SHADOW |
CPU_BASED_USE_MSR_BITMAPS |
CPU_BASED_ACTIVATE_SECONDARY_CONTROLS;
@@ -1510,6 +1557,12 @@
vmcs_conf->vmexit_ctrl = _vmexit_control;
vmcs_conf->vmentry_ctrl = _vmentry_control;
+ cpu_has_load_ia32_efer =
+ allow_1_setting(MSR_IA32_VMX_ENTRY_CTLS,
+ VM_ENTRY_LOAD_IA32_EFER)
+ && allow_1_setting(MSR_IA32_VMX_EXIT_CTLS,
+ VM_EXIT_LOAD_IA32_EFER);
+
return 0;
}
@@ -1683,9 +1736,13 @@
save->limit = vmcs_read32(sf->limit);
save->ar = vmcs_read32(sf->ar_bytes);
vmcs_write16(sf->selector, save->base >> 4);
- vmcs_write32(sf->base, save->base & 0xfffff);
+ vmcs_write32(sf->base, save->base & 0xffff0);
vmcs_write32(sf->limit, 0xffff);
vmcs_write32(sf->ar_bytes, 0xf3);
+ if (save->base & 0xf)
+ printk_once(KERN_WARNING "kvm: segment base is not paragraph"
+ " aligned when entering protected mode (seg=%d)",
+ seg);
}
static void enter_rmode(struct kvm_vcpu *vcpu)
@@ -1814,6 +1871,13 @@
vcpu->arch.cr0 |= vmcs_readl(GUEST_CR0) & cr0_guest_owned_bits;
}
+static void vmx_decache_cr3(struct kvm_vcpu *vcpu)
+{
+ if (enable_ept && is_paging(vcpu))
+ vcpu->arch.cr3 = vmcs_readl(GUEST_CR3);
+ __set_bit(VCPU_EXREG_CR3, (ulong *)&vcpu->arch.regs_avail);
+}
+
static void vmx_decache_cr4_guest_bits(struct kvm_vcpu *vcpu)
{
ulong cr4_guest_owned_bits = vcpu->arch.cr4_guest_owned_bits;
@@ -1857,6 +1921,7 @@
unsigned long cr0,
struct kvm_vcpu *vcpu)
{
+ vmx_decache_cr3(vcpu);
if (!(cr0 & X86_CR0_PG)) {
/* From paging/starting to nonpaging */
vmcs_write32(CPU_BASED_VM_EXEC_CONTROL,
@@ -1937,7 +2002,7 @@
if (enable_ept) {
eptp = construct_eptp(cr3);
vmcs_write64(EPT_POINTER, eptp);
- guest_cr3 = is_paging(vcpu) ? vcpu->arch.cr3 :
+ guest_cr3 = is_paging(vcpu) ? kvm_read_cr3(vcpu) :
vcpu->kvm->arch.ept_identity_map_addr;
ept_load_pdptrs(vcpu);
}
@@ -2725,7 +2790,7 @@
vmcs_writel(GUEST_IDTR_BASE, 0);
vmcs_write32(GUEST_IDTR_LIMIT, 0xffff);
- vmcs_write32(GUEST_ACTIVITY_STATE, 0);
+ vmcs_write32(GUEST_ACTIVITY_STATE, GUEST_ACTIVITY_ACTIVE);
vmcs_write32(GUEST_INTERRUPTIBILITY_INFO, 0);
vmcs_write32(GUEST_PENDING_DBG_EXCEPTIONS, 0);
@@ -2787,6 +2852,10 @@
return;
}
+ if (vmcs_read32(GUEST_INTERRUPTIBILITY_INFO) & GUEST_INTR_STATE_STI) {
+ enable_irq_window(vcpu);
+ return;
+ }
cpu_based_vm_exec_control = vmcs_read32(CPU_BASED_VM_EXEC_CONTROL);
cpu_based_vm_exec_control |= CPU_BASED_VIRTUAL_NMI_PENDING;
vmcs_write32(CPU_BASED_VM_EXEC_CONTROL, cpu_based_vm_exec_control);
@@ -2814,6 +2883,7 @@
} else
intr |= INTR_TYPE_EXT_INTR;
vmcs_write32(VM_ENTRY_INTR_INFO_FIELD, intr);
+ vmx_clear_hlt(vcpu);
}
static void vmx_inject_nmi(struct kvm_vcpu *vcpu)
@@ -2841,6 +2911,7 @@
}
vmcs_write32(VM_ENTRY_INTR_INFO_FIELD,
INTR_TYPE_NMI_INTR | INTR_INFO_VALID_MASK | NMI_VECTOR);
+ vmx_clear_hlt(vcpu);
}
static int vmx_nmi_allowed(struct kvm_vcpu *vcpu)
@@ -2849,7 +2920,8 @@
return 0;
return !(vmcs_read32(GUEST_INTERRUPTIBILITY_INFO) &
- (GUEST_INTR_STATE_MOV_SS | GUEST_INTR_STATE_NMI));
+ (GUEST_INTR_STATE_MOV_SS | GUEST_INTR_STATE_STI
+ | GUEST_INTR_STATE_NMI));
}
static bool vmx_get_nmi_mask(struct kvm_vcpu *vcpu)
@@ -2910,7 +2982,7 @@
* Cause the #SS fault with 0 error code in VM86 mode.
*/
if (((vec == GP_VECTOR) || (vec == SS_VECTOR)) && err_code == 0)
- if (emulate_instruction(vcpu, 0, 0, 0) == EMULATE_DONE)
+ if (emulate_instruction(vcpu, 0) == EMULATE_DONE)
return 1;
/*
* Forward all other exceptions that are valid in real mode.
@@ -3007,7 +3079,7 @@
}
if (is_invalid_opcode(intr_info)) {
- er = emulate_instruction(vcpu, 0, 0, EMULTYPE_TRAP_UD);
+ er = emulate_instruction(vcpu, EMULTYPE_TRAP_UD);
if (er != EMULATE_DONE)
kvm_queue_exception(vcpu, UD_VECTOR);
return 1;
@@ -3026,7 +3098,7 @@
if (kvm_event_needs_reinjection(vcpu))
kvm_mmu_unprotect_page_virt(vcpu, cr2);
- return kvm_mmu_page_fault(vcpu, cr2, error_code);
+ return kvm_mmu_page_fault(vcpu, cr2, error_code, NULL, 0);
}
if (vmx->rmode.vm86_active &&
@@ -3098,7 +3170,7 @@
++vcpu->stat.io_exits;
if (string || in)
- return emulate_instruction(vcpu, 0, 0, 0) == EMULATE_DONE;
+ return emulate_instruction(vcpu, 0) == EMULATE_DONE;
port = exit_qualification >> 16;
size = (exit_qualification & 7) + 1;
@@ -3118,14 +3190,6 @@
hypercall[2] = 0xc1;
}
-static void complete_insn_gp(struct kvm_vcpu *vcpu, int err)
-{
- if (err)
- kvm_inject_gp(vcpu, 0);
- else
- skip_emulated_instruction(vcpu);
-}
-
static int handle_cr(struct kvm_vcpu *vcpu)
{
unsigned long exit_qualification, val;
@@ -3143,21 +3207,21 @@
switch (cr) {
case 0:
err = kvm_set_cr0(vcpu, val);
- complete_insn_gp(vcpu, err);
+ kvm_complete_insn_gp(vcpu, err);
return 1;
case 3:
err = kvm_set_cr3(vcpu, val);
- complete_insn_gp(vcpu, err);
+ kvm_complete_insn_gp(vcpu, err);
return 1;
case 4:
err = kvm_set_cr4(vcpu, val);
- complete_insn_gp(vcpu, err);
+ kvm_complete_insn_gp(vcpu, err);
return 1;
case 8: {
u8 cr8_prev = kvm_get_cr8(vcpu);
u8 cr8 = kvm_register_read(vcpu, reg);
- kvm_set_cr8(vcpu, cr8);
- skip_emulated_instruction(vcpu);
+ err = kvm_set_cr8(vcpu, cr8);
+ kvm_complete_insn_gp(vcpu, err);
if (irqchip_in_kernel(vcpu->kvm))
return 1;
if (cr8_prev <= cr8)
@@ -3176,8 +3240,9 @@
case 1: /*mov from cr*/
switch (cr) {
case 3:
- kvm_register_write(vcpu, reg, vcpu->arch.cr3);
- trace_kvm_cr_read(cr, vcpu->arch.cr3);
+ val = kvm_read_cr3(vcpu);
+ kvm_register_write(vcpu, reg, val);
+ trace_kvm_cr_read(cr, val);
skip_emulated_instruction(vcpu);
return 1;
case 8:
@@ -3349,6 +3414,11 @@
return 1;
}
+static int handle_invd(struct kvm_vcpu *vcpu)
+{
+ return emulate_instruction(vcpu, 0) == EMULATE_DONE;
+}
+
static int handle_invlpg(struct kvm_vcpu *vcpu)
{
unsigned long exit_qualification = vmcs_readl(EXIT_QUALIFICATION);
@@ -3377,7 +3447,7 @@
static int handle_apic_access(struct kvm_vcpu *vcpu)
{
- return emulate_instruction(vcpu, 0, 0, 0) == EMULATE_DONE;
+ return emulate_instruction(vcpu, 0) == EMULATE_DONE;
}
static int handle_task_switch(struct kvm_vcpu *vcpu)
@@ -3476,7 +3546,7 @@
gpa = vmcs_read64(GUEST_PHYSICAL_ADDRESS);
trace_kvm_page_fault(gpa, exit_qualification);
- return kvm_mmu_page_fault(vcpu, gpa & PAGE_MASK, 0);
+ return kvm_mmu_page_fault(vcpu, gpa, exit_qualification & 0x3, NULL, 0);
}
static u64 ept_rsvd_mask(u64 spte, int level)
@@ -3592,7 +3662,7 @@
&& (kvm_get_rflags(&vmx->vcpu) & X86_EFLAGS_IF))
return handle_interrupt_window(&vmx->vcpu);
- err = emulate_instruction(vcpu, 0, 0, 0);
+ err = emulate_instruction(vcpu, 0);
if (err == EMULATE_DO_MMIO) {
ret = 0;
@@ -3649,6 +3719,7 @@
[EXIT_REASON_MSR_WRITE] = handle_wrmsr,
[EXIT_REASON_PENDING_INTERRUPT] = handle_interrupt_window,
[EXIT_REASON_HLT] = handle_halt,
+ [EXIT_REASON_INVD] = handle_invd,
[EXIT_REASON_INVLPG] = handle_invlpg,
[EXIT_REASON_VMCALL] = handle_vmcall,
[EXIT_REASON_VMCLEAR] = handle_vmx_insn,
@@ -3676,6 +3747,12 @@
static const int kvm_vmx_max_exit_handlers =
ARRAY_SIZE(kvm_vmx_exit_handlers);
+static void vmx_get_exit_info(struct kvm_vcpu *vcpu, u64 *info1, u64 *info2)
+{
+ *info1 = vmcs_readl(EXIT_QUALIFICATION);
+ *info2 = vmcs_read32(VM_EXIT_INTR_INFO);
+}
+
/*
* The guest has exited. See if we can fix it or if we need userspace
* assistance.
@@ -3686,17 +3763,12 @@
u32 exit_reason = vmx->exit_reason;
u32 vectoring_info = vmx->idt_vectoring_info;
- trace_kvm_exit(exit_reason, vcpu);
+ trace_kvm_exit(exit_reason, vcpu, KVM_ISA_VMX);
/* If guest state is invalid, start emulating */
if (vmx->emulation_required && emulate_invalid_guest_state)
return handle_invalid_guest_state(vcpu);
- /* Access CR3 don't cause VMExit in paging mode, so we need
- * to sync with guest real CR3. */
- if (enable_ept && is_paging(vcpu))
- vcpu->arch.cr3 = vmcs_readl(GUEST_CR3);
-
if (exit_reason & VMX_EXIT_REASONS_FAILED_VMENTRY) {
vcpu->run->exit_reason = KVM_EXIT_FAIL_ENTRY;
vcpu->run->fail_entry.hardware_entry_failure_reason
@@ -4013,7 +4085,8 @@
);
vcpu->arch.regs_avail = ~((1 << VCPU_REGS_RIP) | (1 << VCPU_REGS_RSP)
- | (1 << VCPU_EXREG_PDPTR));
+ | (1 << VCPU_EXREG_PDPTR)
+ | (1 << VCPU_EXREG_CR3));
vcpu->arch.regs_dirty = 0;
vmx->idt_vectoring_info = vmcs_read32(IDT_VECTORING_INFO_FIELD);
@@ -4280,6 +4353,7 @@
.get_cpl = vmx_get_cpl,
.get_cs_db_l_bits = vmx_get_cs_db_l_bits,
.decache_cr0_guest_bits = vmx_decache_cr0_guest_bits,
+ .decache_cr3 = vmx_decache_cr3,
.decache_cr4_guest_bits = vmx_decache_cr4_guest_bits,
.set_cr0 = vmx_set_cr0,
.set_cr3 = vmx_set_cr3,
@@ -4320,7 +4394,9 @@
.get_tdp_level = get_ept_level,
.get_mt_mask = vmx_get_mt_mask,
+ .get_exit_info = vmx_get_exit_info,
.exit_reasons_str = vmx_exit_reasons_str,
+
.get_lpage_level = vmx_get_lpage_level,
.cpuid_update = vmx_cpuid_update,
@@ -4396,8 +4472,6 @@
if (enable_ept) {
bypass_guest_pf = 0;
- kvm_mmu_set_base_ptes(VMX_EPT_READABLE_MASK |
- VMX_EPT_WRITABLE_MASK);
kvm_mmu_set_mask_ptes(0ull, 0ull, 0ull, 0ull,
VMX_EPT_EXECUTABLE_MASK);
kvm_enable_tdp();
diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c
index 46a368c..bcc0efc 100644
--- a/arch/x86/kvm/x86.c
+++ b/arch/x86/kvm/x86.c
@@ -43,6 +43,7 @@
#include <linux/slab.h>
#include <linux/perf_event.h>
#include <linux/uaccess.h>
+#include <linux/hash.h>
#include <trace/events/kvm.h>
#define CREATE_TRACE_POINTS
@@ -155,6 +156,13 @@
u64 __read_mostly host_xcr0;
+static inline void kvm_async_pf_hash_reset(struct kvm_vcpu *vcpu)
+{
+ int i;
+ for (i = 0; i < roundup_pow_of_two(ASYNC_PF_PER_VCPU); i++)
+ vcpu->arch.apf.gfns[i] = ~0;
+}
+
static void kvm_on_user_return(struct user_return_notifier *urn)
{
unsigned slot;
@@ -326,23 +334,28 @@
}
EXPORT_SYMBOL_GPL(kvm_requeue_exception);
-void kvm_inject_page_fault(struct kvm_vcpu *vcpu)
+void kvm_complete_insn_gp(struct kvm_vcpu *vcpu, int err)
{
- unsigned error_code = vcpu->arch.fault.error_code;
+ if (err)
+ kvm_inject_gp(vcpu, 0);
+ else
+ kvm_x86_ops->skip_emulated_instruction(vcpu);
+}
+EXPORT_SYMBOL_GPL(kvm_complete_insn_gp);
+void kvm_inject_page_fault(struct kvm_vcpu *vcpu, struct x86_exception *fault)
+{
++vcpu->stat.pf_guest;
- vcpu->arch.cr2 = vcpu->arch.fault.address;
- kvm_queue_exception_e(vcpu, PF_VECTOR, error_code);
+ vcpu->arch.cr2 = fault->address;
+ kvm_queue_exception_e(vcpu, PF_VECTOR, fault->error_code);
}
-void kvm_propagate_fault(struct kvm_vcpu *vcpu)
+void kvm_propagate_fault(struct kvm_vcpu *vcpu, struct x86_exception *fault)
{
- if (mmu_is_nested(vcpu) && !vcpu->arch.fault.nested)
- vcpu->arch.nested_mmu.inject_page_fault(vcpu);
+ if (mmu_is_nested(vcpu) && !fault->nested_page_fault)
+ vcpu->arch.nested_mmu.inject_page_fault(vcpu, fault);
else
- vcpu->arch.mmu.inject_page_fault(vcpu);
-
- vcpu->arch.fault.nested = false;
+ vcpu->arch.mmu.inject_page_fault(vcpu, fault);
}
void kvm_inject_nmi(struct kvm_vcpu *vcpu)
@@ -460,8 +473,8 @@
(unsigned long *)&vcpu->arch.regs_avail))
return true;
- gfn = (vcpu->arch.cr3 & ~31u) >> PAGE_SHIFT;
- offset = (vcpu->arch.cr3 & ~31u) & (PAGE_SIZE - 1);
+ gfn = (kvm_read_cr3(vcpu) & ~31u) >> PAGE_SHIFT;
+ offset = (kvm_read_cr3(vcpu) & ~31u) & (PAGE_SIZE - 1);
r = kvm_read_nested_guest_page(vcpu, gfn, pdpte, offset, sizeof(pdpte),
PFERR_USER_MASK | PFERR_WRITE_MASK);
if (r < 0)
@@ -506,12 +519,15 @@
} else
#endif
if (is_pae(vcpu) && !load_pdptrs(vcpu, vcpu->arch.walk_mmu,
- vcpu->arch.cr3))
+ kvm_read_cr3(vcpu)))
return 1;
}
kvm_x86_ops->set_cr0(vcpu, cr0);
+ if ((cr0 ^ old_cr0) & X86_CR0_PG)
+ kvm_clear_async_pf_completion_queue(vcpu);
+
if ((cr0 ^ old_cr0) & update_bits)
kvm_mmu_reset_context(vcpu);
return 0;
@@ -595,7 +611,8 @@
return 1;
} else if (is_paging(vcpu) && (cr4 & X86_CR4_PAE)
&& ((cr4 ^ old_cr4) & pdptr_bits)
- && !load_pdptrs(vcpu, vcpu->arch.walk_mmu, vcpu->arch.cr3))
+ && !load_pdptrs(vcpu, vcpu->arch.walk_mmu,
+ kvm_read_cr3(vcpu)))
return 1;
if (cr4 & X86_CR4_VMXE)
@@ -615,7 +632,7 @@
int kvm_set_cr3(struct kvm_vcpu *vcpu, unsigned long cr3)
{
- if (cr3 == vcpu->arch.cr3 && !pdptrs_changed(vcpu)) {
+ if (cr3 == kvm_read_cr3(vcpu) && !pdptrs_changed(vcpu)) {
kvm_mmu_sync_roots(vcpu);
kvm_mmu_flush_tlb(vcpu);
return 0;
@@ -650,12 +667,13 @@
if (unlikely(!gfn_to_memslot(vcpu->kvm, cr3 >> PAGE_SHIFT)))
return 1;
vcpu->arch.cr3 = cr3;
+ __set_bit(VCPU_EXREG_CR3, (ulong *)&vcpu->arch.regs_avail);
vcpu->arch.mmu.new_cr3(vcpu);
return 0;
}
EXPORT_SYMBOL_GPL(kvm_set_cr3);
-int __kvm_set_cr8(struct kvm_vcpu *vcpu, unsigned long cr8)
+int kvm_set_cr8(struct kvm_vcpu *vcpu, unsigned long cr8)
{
if (cr8 & CR8_RESERVED_BITS)
return 1;
@@ -665,12 +683,6 @@
vcpu->arch.cr8 = cr8;
return 0;
}
-
-void kvm_set_cr8(struct kvm_vcpu *vcpu, unsigned long cr8)
-{
- if (__kvm_set_cr8(vcpu, cr8))
- kvm_inject_gp(vcpu, 0);
-}
EXPORT_SYMBOL_GPL(kvm_set_cr8);
unsigned long kvm_get_cr8(struct kvm_vcpu *vcpu)
@@ -775,12 +787,12 @@
* kvm-specific. Those are put in the beginning of the list.
*/
-#define KVM_SAVE_MSRS_BEGIN 7
+#define KVM_SAVE_MSRS_BEGIN 8
static u32 msrs_to_save[] = {
MSR_KVM_SYSTEM_TIME, MSR_KVM_WALL_CLOCK,
MSR_KVM_SYSTEM_TIME_NEW, MSR_KVM_WALL_CLOCK_NEW,
HV_X64_MSR_GUEST_OS_ID, HV_X64_MSR_HYPERCALL,
- HV_X64_MSR_APIC_ASSIST_PAGE,
+ HV_X64_MSR_APIC_ASSIST_PAGE, MSR_KVM_ASYNC_PF_EN,
MSR_IA32_SYSENTER_CS, MSR_IA32_SYSENTER_ESP, MSR_IA32_SYSENTER_EIP,
MSR_STAR,
#ifdef CONFIG_X86_64
@@ -830,7 +842,6 @@
kvm_x86_ops->set_efer(vcpu, efer);
vcpu->arch.mmu.base_role.nxe = (efer & EFER_NX) && !tdp_enabled;
- kvm_mmu_reset_context(vcpu);
/* Update reserved bits */
if ((efer ^ old_efer) & EFER_NX)
@@ -1418,6 +1429,30 @@
return 0;
}
+static int kvm_pv_enable_async_pf(struct kvm_vcpu *vcpu, u64 data)
+{
+ gpa_t gpa = data & ~0x3f;
+
+ /* Bits 2:5 are resrved, Should be zero */
+ if (data & 0x3c)
+ return 1;
+
+ vcpu->arch.apf.msr_val = data;
+
+ if (!(data & KVM_ASYNC_PF_ENABLED)) {
+ kvm_clear_async_pf_completion_queue(vcpu);
+ kvm_async_pf_hash_reset(vcpu);
+ return 0;
+ }
+
+ if (kvm_gfn_to_hva_cache_init(vcpu->kvm, &vcpu->arch.apf.data, gpa))
+ return 1;
+
+ vcpu->arch.apf.send_user_only = !(data & KVM_ASYNC_PF_SEND_ALWAYS);
+ kvm_async_pf_wakeup_all(vcpu);
+ return 0;
+}
+
int kvm_set_msr_common(struct kvm_vcpu *vcpu, u32 msr, u64 data)
{
switch (msr) {
@@ -1499,6 +1534,10 @@
}
break;
}
+ case MSR_KVM_ASYNC_PF_EN:
+ if (kvm_pv_enable_async_pf(vcpu, data))
+ return 1;
+ break;
case MSR_IA32_MCG_CTL:
case MSR_IA32_MCG_STATUS:
case MSR_IA32_MC0_CTL ... MSR_IA32_MC0_CTL + 4 * KVM_MAX_MCE_BANKS - 1:
@@ -1775,6 +1814,9 @@
case MSR_KVM_SYSTEM_TIME_NEW:
data = vcpu->arch.time;
break;
+ case MSR_KVM_ASYNC_PF_EN:
+ data = vcpu->arch.apf.msr_val;
+ break;
case MSR_IA32_P5_MC_ADDR:
case MSR_IA32_P5_MC_TYPE:
case MSR_IA32_MCG_CAP:
@@ -1904,6 +1946,7 @@
case KVM_CAP_NOP_IO_DELAY:
case KVM_CAP_MP_STATE:
case KVM_CAP_SYNC_MMU:
+ case KVM_CAP_USER_NMI:
case KVM_CAP_REINJECT_CONTROL:
case KVM_CAP_IRQ_INJECT_STATUS:
case KVM_CAP_ASSIGN_DEV_IRQ:
@@ -1922,6 +1965,7 @@
case KVM_CAP_DEBUGREGS:
case KVM_CAP_X86_ROBUST_SINGLESTEP:
case KVM_CAP_XSAVE:
+ case KVM_CAP_ASYNC_PF:
r = 1;
break;
case KVM_CAP_COALESCED_MMIO:
@@ -2185,6 +2229,11 @@
return r;
}
+static void cpuid_mask(u32 *word, int wordnum)
+{
+ *word &= boot_cpu_data.x86_capability[wordnum];
+}
+
static void do_cpuid_1_ent(struct kvm_cpuid_entry2 *entry, u32 function,
u32 index)
{
@@ -2259,7 +2308,9 @@
break;
case 1:
entry->edx &= kvm_supported_word0_x86_features;
+ cpuid_mask(&entry->edx, 0);
entry->ecx &= kvm_supported_word4_x86_features;
+ cpuid_mask(&entry->ecx, 4);
/* we support x2apic emulation even if host does not support
* it since we emulate x2apic in software */
entry->ecx |= F(X2APIC);
@@ -2350,7 +2401,9 @@
break;
case 0x80000001:
entry->edx &= kvm_supported_word1_x86_features;
+ cpuid_mask(&entry->edx, 1);
entry->ecx &= kvm_supported_word6_x86_features;
+ cpuid_mask(&entry->ecx, 6);
break;
}
@@ -3169,20 +3222,18 @@
struct kvm_memslots *slots, *old_slots;
unsigned long *dirty_bitmap;
- r = -ENOMEM;
- dirty_bitmap = vmalloc(n);
- if (!dirty_bitmap)
- goto out;
+ dirty_bitmap = memslot->dirty_bitmap_head;
+ if (memslot->dirty_bitmap == dirty_bitmap)
+ dirty_bitmap += n / sizeof(long);
memset(dirty_bitmap, 0, n);
r = -ENOMEM;
slots = kzalloc(sizeof(struct kvm_memslots), GFP_KERNEL);
- if (!slots) {
- vfree(dirty_bitmap);
+ if (!slots)
goto out;
- }
memcpy(slots, kvm->memslots, sizeof(struct kvm_memslots));
slots->memslots[log->slot].dirty_bitmap = dirty_bitmap;
+ slots->generation++;
old_slots = kvm->memslots;
rcu_assign_pointer(kvm->memslots, slots);
@@ -3195,11 +3246,8 @@
spin_unlock(&kvm->mmu_lock);
r = -EFAULT;
- if (copy_to_user(log->dirty_bitmap, dirty_bitmap, n)) {
- vfree(dirty_bitmap);
+ if (copy_to_user(log->dirty_bitmap, dirty_bitmap, n))
goto out;
- }
- vfree(dirty_bitmap);
} else {
r = -EFAULT;
if (clear_user(log->dirty_bitmap, n))
@@ -3266,8 +3314,10 @@
if (vpic) {
r = kvm_ioapic_init(kvm);
if (r) {
+ mutex_lock(&kvm->slots_lock);
kvm_io_bus_unregister_dev(kvm, KVM_PIO_BUS,
&vpic->dev);
+ mutex_unlock(&kvm->slots_lock);
kfree(vpic);
goto create_irqchip_unlock;
}
@@ -3278,10 +3328,12 @@
smp_wmb();
r = kvm_setup_default_irq_routing(kvm);
if (r) {
+ mutex_lock(&kvm->slots_lock);
mutex_lock(&kvm->irq_lock);
kvm_ioapic_destroy(kvm);
kvm_destroy_pic(kvm);
mutex_unlock(&kvm->irq_lock);
+ mutex_unlock(&kvm->slots_lock);
}
create_irqchip_unlock:
mutex_unlock(&kvm->lock);
@@ -3557,63 +3609,63 @@
static gpa_t translate_nested_gpa(struct kvm_vcpu *vcpu, gpa_t gpa, u32 access)
{
gpa_t t_gpa;
- u32 error;
+ struct x86_exception exception;
BUG_ON(!mmu_is_nested(vcpu));
/* NPT walks are always user-walks */
access |= PFERR_USER_MASK;
- t_gpa = vcpu->arch.mmu.gva_to_gpa(vcpu, gpa, access, &error);
- if (t_gpa == UNMAPPED_GVA)
- vcpu->arch.fault.nested = true;
+ t_gpa = vcpu->arch.mmu.gva_to_gpa(vcpu, gpa, access, &exception);
return t_gpa;
}
-gpa_t kvm_mmu_gva_to_gpa_read(struct kvm_vcpu *vcpu, gva_t gva, u32 *error)
+gpa_t kvm_mmu_gva_to_gpa_read(struct kvm_vcpu *vcpu, gva_t gva,
+ struct x86_exception *exception)
{
u32 access = (kvm_x86_ops->get_cpl(vcpu) == 3) ? PFERR_USER_MASK : 0;
- return vcpu->arch.walk_mmu->gva_to_gpa(vcpu, gva, access, error);
+ return vcpu->arch.walk_mmu->gva_to_gpa(vcpu, gva, access, exception);
}
- gpa_t kvm_mmu_gva_to_gpa_fetch(struct kvm_vcpu *vcpu, gva_t gva, u32 *error)
+ gpa_t kvm_mmu_gva_to_gpa_fetch(struct kvm_vcpu *vcpu, gva_t gva,
+ struct x86_exception *exception)
{
u32 access = (kvm_x86_ops->get_cpl(vcpu) == 3) ? PFERR_USER_MASK : 0;
access |= PFERR_FETCH_MASK;
- return vcpu->arch.walk_mmu->gva_to_gpa(vcpu, gva, access, error);
+ return vcpu->arch.walk_mmu->gva_to_gpa(vcpu, gva, access, exception);
}
-gpa_t kvm_mmu_gva_to_gpa_write(struct kvm_vcpu *vcpu, gva_t gva, u32 *error)
+gpa_t kvm_mmu_gva_to_gpa_write(struct kvm_vcpu *vcpu, gva_t gva,
+ struct x86_exception *exception)
{
u32 access = (kvm_x86_ops->get_cpl(vcpu) == 3) ? PFERR_USER_MASK : 0;
access |= PFERR_WRITE_MASK;
- return vcpu->arch.walk_mmu->gva_to_gpa(vcpu, gva, access, error);
+ return vcpu->arch.walk_mmu->gva_to_gpa(vcpu, gva, access, exception);
}
/* uses this to access any guest's mapped memory without checking CPL */
-gpa_t kvm_mmu_gva_to_gpa_system(struct kvm_vcpu *vcpu, gva_t gva, u32 *error)
+gpa_t kvm_mmu_gva_to_gpa_system(struct kvm_vcpu *vcpu, gva_t gva,
+ struct x86_exception *exception)
{
- return vcpu->arch.walk_mmu->gva_to_gpa(vcpu, gva, 0, error);
+ return vcpu->arch.walk_mmu->gva_to_gpa(vcpu, gva, 0, exception);
}
static int kvm_read_guest_virt_helper(gva_t addr, void *val, unsigned int bytes,
struct kvm_vcpu *vcpu, u32 access,
- u32 *error)
+ struct x86_exception *exception)
{
void *data = val;
int r = X86EMUL_CONTINUE;
while (bytes) {
gpa_t gpa = vcpu->arch.walk_mmu->gva_to_gpa(vcpu, addr, access,
- error);
+ exception);
unsigned offset = addr & (PAGE_SIZE-1);
unsigned toread = min(bytes, (unsigned)PAGE_SIZE - offset);
int ret;
- if (gpa == UNMAPPED_GVA) {
- r = X86EMUL_PROPAGATE_FAULT;
- goto out;
- }
+ if (gpa == UNMAPPED_GVA)
+ return X86EMUL_PROPAGATE_FAULT;
ret = kvm_read_guest(vcpu->kvm, gpa, data, toread);
if (ret < 0) {
r = X86EMUL_IO_NEEDED;
@@ -3630,31 +3682,35 @@
/* used for instruction fetching */
static int kvm_fetch_guest_virt(gva_t addr, void *val, unsigned int bytes,
- struct kvm_vcpu *vcpu, u32 *error)
+ struct kvm_vcpu *vcpu,
+ struct x86_exception *exception)
{
u32 access = (kvm_x86_ops->get_cpl(vcpu) == 3) ? PFERR_USER_MASK : 0;
return kvm_read_guest_virt_helper(addr, val, bytes, vcpu,
- access | PFERR_FETCH_MASK, error);
+ access | PFERR_FETCH_MASK,
+ exception);
}
static int kvm_read_guest_virt(gva_t addr, void *val, unsigned int bytes,
- struct kvm_vcpu *vcpu, u32 *error)
+ struct kvm_vcpu *vcpu,
+ struct x86_exception *exception)
{
u32 access = (kvm_x86_ops->get_cpl(vcpu) == 3) ? PFERR_USER_MASK : 0;
return kvm_read_guest_virt_helper(addr, val, bytes, vcpu, access,
- error);
+ exception);
}
static int kvm_read_guest_virt_system(gva_t addr, void *val, unsigned int bytes,
- struct kvm_vcpu *vcpu, u32 *error)
+ struct kvm_vcpu *vcpu,
+ struct x86_exception *exception)
{
- return kvm_read_guest_virt_helper(addr, val, bytes, vcpu, 0, error);
+ return kvm_read_guest_virt_helper(addr, val, bytes, vcpu, 0, exception);
}
static int kvm_write_guest_virt_system(gva_t addr, void *val,
unsigned int bytes,
struct kvm_vcpu *vcpu,
- u32 *error)
+ struct x86_exception *exception)
{
void *data = val;
int r = X86EMUL_CONTINUE;
@@ -3662,15 +3718,13 @@
while (bytes) {
gpa_t gpa = vcpu->arch.walk_mmu->gva_to_gpa(vcpu, addr,
PFERR_WRITE_MASK,
- error);
+ exception);
unsigned offset = addr & (PAGE_SIZE-1);
unsigned towrite = min(bytes, (unsigned)PAGE_SIZE - offset);
int ret;
- if (gpa == UNMAPPED_GVA) {
- r = X86EMUL_PROPAGATE_FAULT;
- goto out;
- }
+ if (gpa == UNMAPPED_GVA)
+ return X86EMUL_PROPAGATE_FAULT;
ret = kvm_write_guest(vcpu->kvm, gpa, data, towrite);
if (ret < 0) {
r = X86EMUL_IO_NEEDED;
@@ -3688,7 +3742,7 @@
static int emulator_read_emulated(unsigned long addr,
void *val,
unsigned int bytes,
- unsigned int *error_code,
+ struct x86_exception *exception,
struct kvm_vcpu *vcpu)
{
gpa_t gpa;
@@ -3701,7 +3755,7 @@
return X86EMUL_CONTINUE;
}
- gpa = kvm_mmu_gva_to_gpa_read(vcpu, addr, error_code);
+ gpa = kvm_mmu_gva_to_gpa_read(vcpu, addr, exception);
if (gpa == UNMAPPED_GVA)
return X86EMUL_PROPAGATE_FAULT;
@@ -3710,8 +3764,8 @@
if ((gpa & PAGE_MASK) == APIC_DEFAULT_PHYS_BASE)
goto mmio;
- if (kvm_read_guest_virt(addr, val, bytes, vcpu, NULL)
- == X86EMUL_CONTINUE)
+ if (kvm_read_guest_virt(addr, val, bytes, vcpu, exception)
+ == X86EMUL_CONTINUE)
return X86EMUL_CONTINUE;
mmio:
@@ -3735,7 +3789,7 @@
}
int emulator_write_phys(struct kvm_vcpu *vcpu, gpa_t gpa,
- const void *val, int bytes)
+ const void *val, int bytes)
{
int ret;
@@ -3749,12 +3803,12 @@
static int emulator_write_emulated_onepage(unsigned long addr,
const void *val,
unsigned int bytes,
- unsigned int *error_code,
+ struct x86_exception *exception,
struct kvm_vcpu *vcpu)
{
gpa_t gpa;
- gpa = kvm_mmu_gva_to_gpa_write(vcpu, addr, error_code);
+ gpa = kvm_mmu_gva_to_gpa_write(vcpu, addr, exception);
if (gpa == UNMAPPED_GVA)
return X86EMUL_PROPAGATE_FAULT;
@@ -3787,7 +3841,7 @@
int emulator_write_emulated(unsigned long addr,
const void *val,
unsigned int bytes,
- unsigned int *error_code,
+ struct x86_exception *exception,
struct kvm_vcpu *vcpu)
{
/* Crossing a page boundary? */
@@ -3795,7 +3849,7 @@
int rc, now;
now = -addr & ~PAGE_MASK;
- rc = emulator_write_emulated_onepage(addr, val, now, error_code,
+ rc = emulator_write_emulated_onepage(addr, val, now, exception,
vcpu);
if (rc != X86EMUL_CONTINUE)
return rc;
@@ -3803,7 +3857,7 @@
val += now;
bytes -= now;
}
- return emulator_write_emulated_onepage(addr, val, bytes, error_code,
+ return emulator_write_emulated_onepage(addr, val, bytes, exception,
vcpu);
}
@@ -3821,7 +3875,7 @@
const void *old,
const void *new,
unsigned int bytes,
- unsigned int *error_code,
+ struct x86_exception *exception,
struct kvm_vcpu *vcpu)
{
gpa_t gpa;
@@ -3879,7 +3933,7 @@
emul_write:
printk_once(KERN_WARNING "kvm: emulating exchange as write\n");
- return emulator_write_emulated(addr, new, bytes, error_code, vcpu);
+ return emulator_write_emulated(addr, new, bytes, exception, vcpu);
}
static int kernel_pio(struct kvm_vcpu *vcpu, void *pd)
@@ -3904,7 +3958,7 @@
if (vcpu->arch.pio.count)
goto data_avail;
- trace_kvm_pio(0, port, size, 1);
+ trace_kvm_pio(0, port, size, count);
vcpu->arch.pio.port = port;
vcpu->arch.pio.in = 1;
@@ -3932,7 +3986,7 @@
const void *val, unsigned int count,
struct kvm_vcpu *vcpu)
{
- trace_kvm_pio(1, port, size, 1);
+ trace_kvm_pio(1, port, size, count);
vcpu->arch.pio.port = port;
vcpu->arch.pio.in = 0;
@@ -3973,13 +4027,15 @@
return X86EMUL_CONTINUE;
if (kvm_x86_ops->has_wbinvd_exit()) {
- preempt_disable();
+ int cpu = get_cpu();
+
+ cpumask_set_cpu(cpu, vcpu->arch.wbinvd_dirty_mask);
smp_call_function_many(vcpu->arch.wbinvd_dirty_mask,
wbinvd_ipi, NULL, 1);
- preempt_enable();
+ put_cpu();
cpumask_clear(vcpu->arch.wbinvd_dirty_mask);
- }
- wbinvd();
+ } else
+ wbinvd();
return X86EMUL_CONTINUE;
}
EXPORT_SYMBOL_GPL(kvm_emulate_wbinvd);
@@ -4019,7 +4075,7 @@
value = vcpu->arch.cr2;
break;
case 3:
- value = vcpu->arch.cr3;
+ value = kvm_read_cr3(vcpu);
break;
case 4:
value = kvm_read_cr4(vcpu);
@@ -4053,7 +4109,7 @@
res = kvm_set_cr4(vcpu, mk_cr_64(kvm_read_cr4(vcpu), val));
break;
case 8:
- res = __kvm_set_cr8(vcpu, val & 0xfUL);
+ res = kvm_set_cr8(vcpu, val);
break;
default:
vcpu_printf(vcpu, "%s: unexpected cr %u\n", __func__, cr);
@@ -4206,12 +4262,13 @@
static void inject_emulated_exception(struct kvm_vcpu *vcpu)
{
struct x86_emulate_ctxt *ctxt = &vcpu->arch.emulate_ctxt;
- if (ctxt->exception == PF_VECTOR)
- kvm_propagate_fault(vcpu);
- else if (ctxt->error_code_valid)
- kvm_queue_exception_e(vcpu, ctxt->exception, ctxt->error_code);
+ if (ctxt->exception.vector == PF_VECTOR)
+ kvm_propagate_fault(vcpu, &ctxt->exception);
+ else if (ctxt->exception.error_code_valid)
+ kvm_queue_exception_e(vcpu, ctxt->exception.vector,
+ ctxt->exception.error_code);
else
- kvm_queue_exception(vcpu, ctxt->exception);
+ kvm_queue_exception(vcpu, ctxt->exception.vector);
}
static void init_emulate_ctxt(struct kvm_vcpu *vcpu)
@@ -4267,13 +4324,19 @@
static int handle_emulation_failure(struct kvm_vcpu *vcpu)
{
+ int r = EMULATE_DONE;
+
++vcpu->stat.insn_emulation_fail;
trace_kvm_emulate_insn_failed(vcpu);
- vcpu->run->exit_reason = KVM_EXIT_INTERNAL_ERROR;
- vcpu->run->internal.suberror = KVM_INTERNAL_ERROR_EMULATION;
- vcpu->run->internal.ndata = 0;
+ if (!is_guest_mode(vcpu)) {
+ vcpu->run->exit_reason = KVM_EXIT_INTERNAL_ERROR;
+ vcpu->run->internal.suberror = KVM_INTERNAL_ERROR_EMULATION;
+ vcpu->run->internal.ndata = 0;
+ r = EMULATE_FAIL;
+ }
kvm_queue_exception(vcpu, UD_VECTOR);
- return EMULATE_FAIL;
+
+ return r;
}
static bool reexecute_instruction(struct kvm_vcpu *vcpu, gva_t gva)
@@ -4302,10 +4365,11 @@
return false;
}
-int emulate_instruction(struct kvm_vcpu *vcpu,
- unsigned long cr2,
- u16 error_code,
- int emulation_type)
+int x86_emulate_instruction(struct kvm_vcpu *vcpu,
+ unsigned long cr2,
+ int emulation_type,
+ void *insn,
+ int insn_len)
{
int r;
struct decode_cache *c = &vcpu->arch.emulate_ctxt.decode;
@@ -4323,10 +4387,10 @@
if (!(emulation_type & EMULTYPE_NO_DECODE)) {
init_emulate_ctxt(vcpu);
vcpu->arch.emulate_ctxt.interruptibility = 0;
- vcpu->arch.emulate_ctxt.exception = -1;
+ vcpu->arch.emulate_ctxt.have_exception = false;
vcpu->arch.emulate_ctxt.perm_ok = false;
- r = x86_decode_insn(&vcpu->arch.emulate_ctxt);
+ r = x86_decode_insn(&vcpu->arch.emulate_ctxt, insn, insn_len);
if (r == X86EMUL_PROPAGATE_FAULT)
goto done;
@@ -4389,7 +4453,7 @@
}
done:
- if (vcpu->arch.emulate_ctxt.exception >= 0) {
+ if (vcpu->arch.emulate_ctxt.have_exception) {
inject_emulated_exception(vcpu);
r = EMULATE_DONE;
} else if (vcpu->arch.pio.count) {
@@ -4413,7 +4477,7 @@
return r;
}
-EXPORT_SYMBOL_GPL(emulate_instruction);
+EXPORT_SYMBOL_GPL(x86_emulate_instruction);
int kvm_fast_pio_out(struct kvm_vcpu *vcpu, int size, unsigned short port)
{
@@ -4653,7 +4717,6 @@
kvm_x86_ops = ops;
kvm_mmu_set_nonpresent_ptes(0ull, 0ull);
- kvm_mmu_set_base_ptes(PT_PRESENT_MASK);
kvm_mmu_set_mask_ptes(PT_USER_MASK, PT_ACCESSED_MASK,
PT_DIRTY_MASK, PT64_NX_MASK, 0);
@@ -5116,6 +5179,12 @@
vcpu->fpu_active = 0;
kvm_x86_ops->fpu_deactivate(vcpu);
}
+ if (kvm_check_request(KVM_REQ_APF_HALT, vcpu)) {
+ /* Page is swapped out. Do synthetic halt */
+ vcpu->arch.apf.halted = true;
+ r = 1;
+ goto out;
+ }
}
r = kvm_mmu_reload(vcpu);
@@ -5244,7 +5313,8 @@
r = 1;
while (r > 0) {
- if (vcpu->arch.mp_state == KVM_MP_STATE_RUNNABLE)
+ if (vcpu->arch.mp_state == KVM_MP_STATE_RUNNABLE &&
+ !vcpu->arch.apf.halted)
r = vcpu_enter_guest(vcpu);
else {
srcu_read_unlock(&kvm->srcu, vcpu->srcu_idx);
@@ -5257,6 +5327,7 @@
vcpu->arch.mp_state =
KVM_MP_STATE_RUNNABLE;
case KVM_MP_STATE_RUNNABLE:
+ vcpu->arch.apf.halted = false;
break;
case KVM_MP_STATE_SIPI_RECEIVED:
default:
@@ -5278,6 +5349,9 @@
vcpu->run->exit_reason = KVM_EXIT_INTR;
++vcpu->stat.request_irq_exits;
}
+
+ kvm_check_async_pf_completion(vcpu);
+
if (signal_pending(current)) {
r = -EINTR;
vcpu->run->exit_reason = KVM_EXIT_INTR;
@@ -5302,6 +5376,9 @@
int r;
sigset_t sigsaved;
+ if (!tsk_used_math(current) && init_fpu(current))
+ return -ENOMEM;
+
if (vcpu->sigset_active)
sigprocmask(SIG_SETMASK, &vcpu->sigset, &sigsaved);
@@ -5313,8 +5390,12 @@
}
/* re-sync apic's tpr */
- if (!irqchip_in_kernel(vcpu->kvm))
- kvm_set_cr8(vcpu, kvm_run->cr8);
+ if (!irqchip_in_kernel(vcpu->kvm)) {
+ if (kvm_set_cr8(vcpu, kvm_run->cr8) != 0) {
+ r = -EINVAL;
+ goto out;
+ }
+ }
if (vcpu->arch.pio.count || vcpu->mmio_needed) {
if (vcpu->mmio_needed) {
@@ -5323,7 +5404,7 @@
vcpu->mmio_needed = 0;
}
vcpu->srcu_idx = srcu_read_lock(&vcpu->kvm->srcu);
- r = emulate_instruction(vcpu, 0, 0, EMULTYPE_NO_DECODE);
+ r = emulate_instruction(vcpu, EMULTYPE_NO_DECODE);
srcu_read_unlock(&vcpu->kvm->srcu, vcpu->srcu_idx);
if (r != EMULATE_DONE) {
r = 0;
@@ -5436,7 +5517,7 @@
sregs->cr0 = kvm_read_cr0(vcpu);
sregs->cr2 = vcpu->arch.cr2;
- sregs->cr3 = vcpu->arch.cr3;
+ sregs->cr3 = kvm_read_cr3(vcpu);
sregs->cr4 = kvm_read_cr4(vcpu);
sregs->cr8 = kvm_get_cr8(vcpu);
sregs->efer = vcpu->arch.efer;
@@ -5504,8 +5585,9 @@
kvm_x86_ops->set_gdt(vcpu, &dt);
vcpu->arch.cr2 = sregs->cr2;
- mmu_reset_needed |= vcpu->arch.cr3 != sregs->cr3;
+ mmu_reset_needed |= kvm_read_cr3(vcpu) != sregs->cr3;
vcpu->arch.cr3 = sregs->cr3;
+ __set_bit(VCPU_EXREG_CR3, (ulong *)&vcpu->arch.regs_avail);
kvm_set_cr8(vcpu, sregs->cr8);
@@ -5522,7 +5604,7 @@
if (sregs->cr4 & X86_CR4_OSXSAVE)
update_cpuid(vcpu);
if (!is_long_mode(vcpu) && is_pae(vcpu)) {
- load_pdptrs(vcpu, vcpu->arch.walk_mmu, vcpu->arch.cr3);
+ load_pdptrs(vcpu, vcpu->arch.walk_mmu, kvm_read_cr3(vcpu));
mmu_reset_needed = 1;
}
@@ -5773,6 +5855,8 @@
void kvm_arch_vcpu_destroy(struct kvm_vcpu *vcpu)
{
+ vcpu->arch.apf.msr_val = 0;
+
vcpu_load(vcpu);
kvm_mmu_unload(vcpu);
vcpu_put(vcpu);
@@ -5792,6 +5876,11 @@
vcpu->arch.dr7 = DR7_FIXED_1;
kvm_make_request(KVM_REQ_EVENT, vcpu);
+ vcpu->arch.apf.msr_val = 0;
+
+ kvm_clear_async_pf_completion_queue(vcpu);
+ kvm_async_pf_hash_reset(vcpu);
+ vcpu->arch.apf.halted = false;
return kvm_x86_ops->vcpu_reset(vcpu);
}
@@ -5881,6 +5970,8 @@
if (!zalloc_cpumask_var(&vcpu->arch.wbinvd_dirty_mask, GFP_KERNEL))
goto fail_free_mce_banks;
+ kvm_async_pf_hash_reset(vcpu);
+
return 0;
fail_free_mce_banks:
kfree(vcpu->arch.mce_banks);
@@ -5906,13 +5997,8 @@
free_page((unsigned long)vcpu->arch.pio_data);
}
-struct kvm *kvm_arch_create_vm(void)
+int kvm_arch_init_vm(struct kvm *kvm)
{
- struct kvm *kvm = kzalloc(sizeof(struct kvm), GFP_KERNEL);
-
- if (!kvm)
- return ERR_PTR(-ENOMEM);
-
INIT_LIST_HEAD(&kvm->arch.active_mmu_pages);
INIT_LIST_HEAD(&kvm->arch.assigned_dev_head);
@@ -5921,7 +6007,7 @@
spin_lock_init(&kvm->arch.tsc_write_lock);
- return kvm;
+ return 0;
}
static void kvm_unload_vcpu_mmu(struct kvm_vcpu *vcpu)
@@ -5939,8 +6025,10 @@
/*
* Unpin any mmu pages first.
*/
- kvm_for_each_vcpu(i, vcpu, kvm)
+ kvm_for_each_vcpu(i, vcpu, kvm) {
+ kvm_clear_async_pf_completion_queue(vcpu);
kvm_unload_vcpu_mmu(vcpu);
+ }
kvm_for_each_vcpu(i, vcpu, kvm)
kvm_arch_vcpu_free(vcpu);
@@ -5964,13 +6052,10 @@
kfree(kvm->arch.vpic);
kfree(kvm->arch.vioapic);
kvm_free_vcpus(kvm);
- kvm_free_physmem(kvm);
if (kvm->arch.apic_access_page)
put_page(kvm->arch.apic_access_page);
if (kvm->arch.ept_identity_pagetable)
put_page(kvm->arch.ept_identity_pagetable);
- cleanup_srcu_struct(&kvm->srcu);
- kfree(kvm);
}
int kvm_arch_prepare_memory_region(struct kvm *kvm,
@@ -6051,7 +6136,9 @@
int kvm_arch_vcpu_runnable(struct kvm_vcpu *vcpu)
{
- return vcpu->arch.mp_state == KVM_MP_STATE_RUNNABLE
+ return (vcpu->arch.mp_state == KVM_MP_STATE_RUNNABLE &&
+ !vcpu->arch.apf.halted)
+ || !list_empty_careful(&vcpu->async_pf.done)
|| vcpu->arch.mp_state == KVM_MP_STATE_SIPI_RECEIVED
|| vcpu->arch.nmi_pending ||
(kvm_arch_interrupt_allowed(vcpu) &&
@@ -6110,6 +6197,147 @@
}
EXPORT_SYMBOL_GPL(kvm_set_rflags);
+void kvm_arch_async_page_ready(struct kvm_vcpu *vcpu, struct kvm_async_pf *work)
+{
+ int r;
+
+ if ((vcpu->arch.mmu.direct_map != work->arch.direct_map) ||
+ is_error_page(work->page))
+ return;
+
+ r = kvm_mmu_reload(vcpu);
+ if (unlikely(r))
+ return;
+
+ if (!vcpu->arch.mmu.direct_map &&
+ work->arch.cr3 != vcpu->arch.mmu.get_cr3(vcpu))
+ return;
+
+ vcpu->arch.mmu.page_fault(vcpu, work->gva, 0, true);
+}
+
+static inline u32 kvm_async_pf_hash_fn(gfn_t gfn)
+{
+ return hash_32(gfn & 0xffffffff, order_base_2(ASYNC_PF_PER_VCPU));
+}
+
+static inline u32 kvm_async_pf_next_probe(u32 key)
+{
+ return (key + 1) & (roundup_pow_of_two(ASYNC_PF_PER_VCPU) - 1);
+}
+
+static void kvm_add_async_pf_gfn(struct kvm_vcpu *vcpu, gfn_t gfn)
+{
+ u32 key = kvm_async_pf_hash_fn(gfn);
+
+ while (vcpu->arch.apf.gfns[key] != ~0)
+ key = kvm_async_pf_next_probe(key);
+
+ vcpu->arch.apf.gfns[key] = gfn;
+}
+
+static u32 kvm_async_pf_gfn_slot(struct kvm_vcpu *vcpu, gfn_t gfn)
+{
+ int i;
+ u32 key = kvm_async_pf_hash_fn(gfn);
+
+ for (i = 0; i < roundup_pow_of_two(ASYNC_PF_PER_VCPU) &&
+ (vcpu->arch.apf.gfns[key] != gfn &&
+ vcpu->arch.apf.gfns[key] != ~0); i++)
+ key = kvm_async_pf_next_probe(key);
+
+ return key;
+}
+
+bool kvm_find_async_pf_gfn(struct kvm_vcpu *vcpu, gfn_t gfn)
+{
+ return vcpu->arch.apf.gfns[kvm_async_pf_gfn_slot(vcpu, gfn)] == gfn;
+}
+
+static void kvm_del_async_pf_gfn(struct kvm_vcpu *vcpu, gfn_t gfn)
+{
+ u32 i, j, k;
+
+ i = j = kvm_async_pf_gfn_slot(vcpu, gfn);
+ while (true) {
+ vcpu->arch.apf.gfns[i] = ~0;
+ do {
+ j = kvm_async_pf_next_probe(j);
+ if (vcpu->arch.apf.gfns[j] == ~0)
+ return;
+ k = kvm_async_pf_hash_fn(vcpu->arch.apf.gfns[j]);
+ /*
+ * k lies cyclically in ]i,j]
+ * | i.k.j |
+ * |....j i.k.| or |.k..j i...|
+ */
+ } while ((i <= j) ? (i < k && k <= j) : (i < k || k <= j));
+ vcpu->arch.apf.gfns[i] = vcpu->arch.apf.gfns[j];
+ i = j;
+ }
+}
+
+static int apf_put_user(struct kvm_vcpu *vcpu, u32 val)
+{
+
+ return kvm_write_guest_cached(vcpu->kvm, &vcpu->arch.apf.data, &val,
+ sizeof(val));
+}
+
+void kvm_arch_async_page_not_present(struct kvm_vcpu *vcpu,
+ struct kvm_async_pf *work)
+{
+ struct x86_exception fault;
+
+ trace_kvm_async_pf_not_present(work->arch.token, work->gva);
+ kvm_add_async_pf_gfn(vcpu, work->arch.gfn);
+
+ if (!(vcpu->arch.apf.msr_val & KVM_ASYNC_PF_ENABLED) ||
+ (vcpu->arch.apf.send_user_only &&
+ kvm_x86_ops->get_cpl(vcpu) == 0))
+ kvm_make_request(KVM_REQ_APF_HALT, vcpu);
+ else if (!apf_put_user(vcpu, KVM_PV_REASON_PAGE_NOT_PRESENT)) {
+ fault.vector = PF_VECTOR;
+ fault.error_code_valid = true;
+ fault.error_code = 0;
+ fault.nested_page_fault = false;
+ fault.address = work->arch.token;
+ kvm_inject_page_fault(vcpu, &fault);
+ }
+}
+
+void kvm_arch_async_page_present(struct kvm_vcpu *vcpu,
+ struct kvm_async_pf *work)
+{
+ struct x86_exception fault;
+
+ trace_kvm_async_pf_ready(work->arch.token, work->gva);
+ if (is_error_page(work->page))
+ work->arch.token = ~0; /* broadcast wakeup */
+ else
+ kvm_del_async_pf_gfn(vcpu, work->arch.gfn);
+
+ if ((vcpu->arch.apf.msr_val & KVM_ASYNC_PF_ENABLED) &&
+ !apf_put_user(vcpu, KVM_PV_REASON_PAGE_READY)) {
+ fault.vector = PF_VECTOR;
+ fault.error_code_valid = true;
+ fault.error_code = 0;
+ fault.nested_page_fault = false;
+ fault.address = work->arch.token;
+ kvm_inject_page_fault(vcpu, &fault);
+ }
+ vcpu->arch.apf.halted = false;
+}
+
+bool kvm_arch_can_inject_async_page_present(struct kvm_vcpu *vcpu)
+{
+ if (!(vcpu->arch.apf.msr_val & KVM_ASYNC_PF_ENABLED))
+ return true;
+ else
+ return !kvm_event_needs_reinjection(vcpu) &&
+ kvm_x86_ops->interrupt_allowed(vcpu);
+}
+
EXPORT_TRACEPOINT_SYMBOL_GPL(kvm_exit);
EXPORT_TRACEPOINT_SYMBOL_GPL(kvm_inj_virq);
EXPORT_TRACEPOINT_SYMBOL_GPL(kvm_page_fault);
diff --git a/arch/x86/mm/init_32.c b/arch/x86/mm/init_32.c
index f89b5bb..c821074 100644
--- a/arch/x86/mm/init_32.c
+++ b/arch/x86/mm/init_32.c
@@ -45,6 +45,7 @@
#include <asm/bugs.h>
#include <asm/tlb.h>
#include <asm/tlbflush.h>
+#include <asm/olpc_ofw.h>
#include <asm/pgalloc.h>
#include <asm/sections.h>
#include <asm/paravirt.h>
@@ -715,6 +716,7 @@
/*
* NOTE: at this point the bootmem allocator is fully available.
*/
+ olpc_dt_build_devicetree();
sparse_init();
zone_sizes_init();
}
diff --git a/arch/x86/platform/mrst/early_printk_mrst.c b/arch/x86/platform/mrst/early_printk_mrst.c
index 65df603..25bfdbb 100644
--- a/arch/x86/platform/mrst/early_printk_mrst.c
+++ b/arch/x86/platform/mrst/early_printk_mrst.c
@@ -103,7 +103,7 @@
static unsigned long mrst_spi_paddr = MRST_REGBASE_SPI0;
static u32 *pclk_spi0;
-/* Always contains an accessable address, start with 0 */
+/* Always contains an accessible address, start with 0 */
static struct dw_spi_reg *pspi;
static struct kmsg_dumper dw_dumper;
diff --git a/arch/x86/platform/olpc/Makefile b/arch/x86/platform/olpc/Makefile
index c31b8fc..e797428 100644
--- a/arch/x86/platform/olpc/Makefile
+++ b/arch/x86/platform/olpc/Makefile
@@ -1,3 +1,4 @@
obj-$(CONFIG_OLPC) += olpc.o
obj-$(CONFIG_OLPC_XO1) += olpc-xo1.o
obj-$(CONFIG_OLPC_OPENFIRMWARE) += olpc_ofw.o
+obj-$(CONFIG_OLPC_OPENFIRMWARE_DT) += olpc_dt.o
diff --git a/arch/x86/platform/olpc/olpc_dt.c b/arch/x86/platform/olpc/olpc_dt.c
new file mode 100644
index 0000000..dab8746
--- /dev/null
+++ b/arch/x86/platform/olpc/olpc_dt.c
@@ -0,0 +1,183 @@
+/*
+ * OLPC-specific OFW device tree support code.
+ *
+ * Paul Mackerras August 1996.
+ * Copyright (C) 1996-2005 Paul Mackerras.
+ *
+ * Adapted for 64bit PowerPC by Dave Engebretsen and Peter Bergner.
+ * {engebret|bergner}@us.ibm.com
+ *
+ * Adapted for sparc by David S. Miller davem@davemloft.net
+ * Adapted for x86/OLPC by Andres Salomon <dilinger@queued.net>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+#include <linux/kernel.h>
+#include <linux/bootmem.h>
+#include <linux/of.h>
+#include <linux/of_pdt.h>
+#include <asm/olpc_ofw.h>
+
+static phandle __init olpc_dt_getsibling(phandle node)
+{
+ const void *args[] = { (void *)node };
+ void *res[] = { &node };
+
+ if ((s32)node == -1)
+ return 0;
+
+ if (olpc_ofw("peer", args, res) || (s32)node == -1)
+ return 0;
+
+ return node;
+}
+
+static phandle __init olpc_dt_getchild(phandle node)
+{
+ const void *args[] = { (void *)node };
+ void *res[] = { &node };
+
+ if ((s32)node == -1)
+ return 0;
+
+ if (olpc_ofw("child", args, res) || (s32)node == -1) {
+ pr_err("PROM: %s: fetching child failed!\n", __func__);
+ return 0;
+ }
+
+ return node;
+}
+
+static int __init olpc_dt_getproplen(phandle node, const char *prop)
+{
+ const void *args[] = { (void *)node, prop };
+ int len;
+ void *res[] = { &len };
+
+ if ((s32)node == -1)
+ return -1;
+
+ if (olpc_ofw("getproplen", args, res)) {
+ pr_err("PROM: %s: getproplen failed!\n", __func__);
+ return -1;
+ }
+
+ return len;
+}
+
+static int __init olpc_dt_getproperty(phandle node, const char *prop,
+ char *buf, int bufsize)
+{
+ int plen;
+
+ plen = olpc_dt_getproplen(node, prop);
+ if (plen > bufsize || plen < 1) {
+ return -1;
+ } else {
+ const void *args[] = { (void *)node, prop, buf, (void *)plen };
+ void *res[] = { &plen };
+
+ if (olpc_ofw("getprop", args, res)) {
+ pr_err("PROM: %s: getprop failed!\n", __func__);
+ return -1;
+ }
+ }
+
+ return plen;
+}
+
+static int __init olpc_dt_nextprop(phandle node, char *prev, char *buf)
+{
+ const void *args[] = { (void *)node, prev, buf };
+ int success;
+ void *res[] = { &success };
+
+ buf[0] = '\0';
+
+ if ((s32)node == -1)
+ return -1;
+
+ if (olpc_ofw("nextprop", args, res) || success != 1)
+ return -1;
+
+ return 0;
+}
+
+static int __init olpc_dt_pkg2path(phandle node, char *buf,
+ const int buflen, int *len)
+{
+ const void *args[] = { (void *)node, buf, (void *)buflen };
+ void *res[] = { len };
+
+ if ((s32)node == -1)
+ return -1;
+
+ if (olpc_ofw("package-to-path", args, res) || *len < 1)
+ return -1;
+
+ return 0;
+}
+
+static unsigned int prom_early_allocated __initdata;
+
+void * __init prom_early_alloc(unsigned long size)
+{
+ static u8 *mem;
+ static size_t free_mem;
+ void *res;
+
+ if (free_mem < size) {
+ const size_t chunk_size = max(PAGE_SIZE, size);
+
+ /*
+ * To mimimize the number of allocations, grab at least
+ * PAGE_SIZE of memory (that's an arbitrary choice that's
+ * fast enough on the platforms we care about while minimizing
+ * wasted bootmem) and hand off chunks of it to callers.
+ */
+ res = alloc_bootmem(chunk_size);
+ if (!res)
+ return NULL;
+ prom_early_allocated += chunk_size;
+ memset(res, 0, chunk_size);
+ free_mem = chunk_size;
+ mem = res;
+ }
+
+ /* allocate from the local cache */
+ free_mem -= size;
+ res = mem;
+ mem += size;
+ return res;
+}
+
+static struct of_pdt_ops prom_olpc_ops __initdata = {
+ .nextprop = olpc_dt_nextprop,
+ .getproplen = olpc_dt_getproplen,
+ .getproperty = olpc_dt_getproperty,
+ .getchild = olpc_dt_getchild,
+ .getsibling = olpc_dt_getsibling,
+ .pkg2path = olpc_dt_pkg2path,
+};
+
+void __init olpc_dt_build_devicetree(void)
+{
+ phandle root;
+
+ if (!olpc_ofw_is_installed())
+ return;
+
+ root = olpc_dt_getsibling(0);
+ if (!root) {
+ pr_err("PROM: unable to get root node from OFW!\n");
+ return;
+ }
+ of_pdt_build_devicetree(root, &prom_olpc_ops);
+
+ pr_info("PROM DT: Built device tree with %u bytes of memory.\n",
+ prom_early_allocated);
+}
diff --git a/arch/x86/platform/olpc/olpc_ofw.c b/arch/x86/platform/olpc/olpc_ofw.c
index 7873204..e7604f6 100644
--- a/arch/x86/platform/olpc/olpc_ofw.c
+++ b/arch/x86/platform/olpc/olpc_ofw.c
@@ -110,3 +110,8 @@
(unsigned long)olpc_ofw_cif, (-start) >> 20);
reserve_top_address(-start);
}
+
+bool __init olpc_ofw_is_installed(void)
+{
+ return olpc_ofw_cif != NULL;
+}
diff --git a/block/cfq-iosched.c b/block/cfq-iosched.c
index 4cd59b0..78ee4b1 100644
--- a/block/cfq-iosched.c
+++ b/block/cfq-iosched.c
@@ -1030,7 +1030,7 @@
/*
* Add group onto cgroup list. It might happen that bdi->dev is
- * not initiliazed yet. Initialize this new group without major
+ * not initialized yet. Initialize this new group without major
* and minor info and this info will be filled in once a new thread
* comes for IO. See code above.
*/
diff --git a/crypto/Kconfig b/crypto/Kconfig
index e4bac29..4b7cb0e 100644
--- a/crypto/Kconfig
+++ b/crypto/Kconfig
@@ -110,7 +110,6 @@
config CRYPTO_GF128MUL
tristate "GF(2^128) multiplication functions (EXPERIMENTAL)"
- depends on EXPERIMENTAL
help
Efficient table driven implementation of multiplications in the
field GF(2^128). This is needed by some cypher modes. This
@@ -539,8 +538,9 @@
config CRYPTO_AES_NI_INTEL
tristate "AES cipher algorithms (AES-NI)"
- depends on (X86 || UML_X86) && 64BIT
- select CRYPTO_AES_X86_64
+ depends on (X86 || UML_X86)
+ select CRYPTO_AES_X86_64 if 64BIT
+ select CRYPTO_AES_586 if !64BIT
select CRYPTO_CRYPTD
select CRYPTO_ALGAPI
select CRYPTO_FPU
@@ -563,9 +563,10 @@
See <http://csrc.nist.gov/encryption/aes/> for more information.
- In addition to AES cipher algorithm support, the
- acceleration for some popular block cipher mode is supported
- too, including ECB, CBC, CTR, LRW, PCBC, XTS.
+ In addition to AES cipher algorithm support, the acceleration
+ for some popular block cipher mode is supported too, including
+ ECB, CBC, LRW, PCBC, XTS. The 64 bit version has additional
+ acceleration for CTR.
config CRYPTO_ANUBIS
tristate "Anubis cipher algorithm"
@@ -841,6 +842,27 @@
ANSI X9.31 A.2.4. Note that this option must be enabled if
CRYPTO_FIPS is selected
+config CRYPTO_USER_API
+ tristate
+
+config CRYPTO_USER_API_HASH
+ tristate "User-space interface for hash algorithms"
+ depends on NET
+ select CRYPTO_HASH
+ select CRYPTO_USER_API
+ help
+ This option enables the user-spaces interface for hash
+ algorithms.
+
+config CRYPTO_USER_API_SKCIPHER
+ tristate "User-space interface for symmetric key cipher algorithms"
+ depends on NET
+ select CRYPTO_BLKCIPHER
+ select CRYPTO_USER_API
+ help
+ This option enables the user-spaces interface for symmetric
+ key cipher algorithms.
+
source "drivers/crypto/Kconfig"
endif # if CRYPTO
diff --git a/crypto/Makefile b/crypto/Makefile
index 423b7de..e9a399c 100644
--- a/crypto/Makefile
+++ b/crypto/Makefile
@@ -3,32 +3,32 @@
#
obj-$(CONFIG_CRYPTO) += crypto.o
-crypto-objs := api.o cipher.o compress.o
+crypto-y := api.o cipher.o compress.o
obj-$(CONFIG_CRYPTO_WORKQUEUE) += crypto_wq.o
obj-$(CONFIG_CRYPTO_FIPS) += fips.o
crypto_algapi-$(CONFIG_PROC_FS) += proc.o
-crypto_algapi-objs := algapi.o scatterwalk.o $(crypto_algapi-y)
+crypto_algapi-y := algapi.o scatterwalk.o $(crypto_algapi-y)
obj-$(CONFIG_CRYPTO_ALGAPI2) += crypto_algapi.o
obj-$(CONFIG_CRYPTO_AEAD2) += aead.o
-crypto_blkcipher-objs := ablkcipher.o
-crypto_blkcipher-objs += blkcipher.o
+crypto_blkcipher-y := ablkcipher.o
+crypto_blkcipher-y += blkcipher.o
obj-$(CONFIG_CRYPTO_BLKCIPHER2) += crypto_blkcipher.o
obj-$(CONFIG_CRYPTO_BLKCIPHER2) += chainiv.o
obj-$(CONFIG_CRYPTO_BLKCIPHER2) += eseqiv.o
obj-$(CONFIG_CRYPTO_SEQIV) += seqiv.o
-crypto_hash-objs += ahash.o
-crypto_hash-objs += shash.o
+crypto_hash-y += ahash.o
+crypto_hash-y += shash.o
obj-$(CONFIG_CRYPTO_HASH2) += crypto_hash.o
obj-$(CONFIG_CRYPTO_PCOMP2) += pcompress.o
-cryptomgr-objs := algboss.o testmgr.o
+cryptomgr-y := algboss.o testmgr.o
obj-$(CONFIG_CRYPTO_MANAGER2) += cryptomgr.o
obj-$(CONFIG_CRYPTO_HMAC) += hmac.o
@@ -85,6 +85,9 @@
obj-$(CONFIG_CRYPTO_ANSI_CPRNG) += ansi_cprng.o
obj-$(CONFIG_CRYPTO_TEST) += tcrypt.o
obj-$(CONFIG_CRYPTO_GHASH) += ghash-generic.o
+obj-$(CONFIG_CRYPTO_USER_API) += af_alg.o
+obj-$(CONFIG_CRYPTO_USER_API_HASH) += algif_hash.o
+obj-$(CONFIG_CRYPTO_USER_API_SKCIPHER) += algif_skcipher.o
#
# generic algorithms and the async_tx api
diff --git a/crypto/af_alg.c b/crypto/af_alg.c
new file mode 100644
index 0000000..940d70c
--- /dev/null
+++ b/crypto/af_alg.c
@@ -0,0 +1,483 @@
+/*
+ * af_alg: User-space algorithm interface
+ *
+ * This file provides the user-space API for algorithms.
+ *
+ * Copyright (c) 2010 Herbert Xu <herbert@gondor.apana.org.au>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ */
+
+#include <asm/atomic.h>
+#include <crypto/if_alg.h>
+#include <linux/crypto.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/list.h>
+#include <linux/module.h>
+#include <linux/net.h>
+#include <linux/rwsem.h>
+
+struct alg_type_list {
+ const struct af_alg_type *type;
+ struct list_head list;
+};
+
+static atomic_long_t alg_memory_allocated;
+
+static struct proto alg_proto = {
+ .name = "ALG",
+ .owner = THIS_MODULE,
+ .memory_allocated = &alg_memory_allocated,
+ .obj_size = sizeof(struct alg_sock),
+};
+
+static LIST_HEAD(alg_types);
+static DECLARE_RWSEM(alg_types_sem);
+
+static const struct af_alg_type *alg_get_type(const char *name)
+{
+ const struct af_alg_type *type = ERR_PTR(-ENOENT);
+ struct alg_type_list *node;
+
+ down_read(&alg_types_sem);
+ list_for_each_entry(node, &alg_types, list) {
+ if (strcmp(node->type->name, name))
+ continue;
+
+ if (try_module_get(node->type->owner))
+ type = node->type;
+ break;
+ }
+ up_read(&alg_types_sem);
+
+ return type;
+}
+
+int af_alg_register_type(const struct af_alg_type *type)
+{
+ struct alg_type_list *node;
+ int err = -EEXIST;
+
+ down_write(&alg_types_sem);
+ list_for_each_entry(node, &alg_types, list) {
+ if (!strcmp(node->type->name, type->name))
+ goto unlock;
+ }
+
+ node = kmalloc(sizeof(*node), GFP_KERNEL);
+ err = -ENOMEM;
+ if (!node)
+ goto unlock;
+
+ type->ops->owner = THIS_MODULE;
+ node->type = type;
+ list_add(&node->list, &alg_types);
+ err = 0;
+
+unlock:
+ up_write(&alg_types_sem);
+
+ return err;
+}
+EXPORT_SYMBOL_GPL(af_alg_register_type);
+
+int af_alg_unregister_type(const struct af_alg_type *type)
+{
+ struct alg_type_list *node;
+ int err = -ENOENT;
+
+ down_write(&alg_types_sem);
+ list_for_each_entry(node, &alg_types, list) {
+ if (strcmp(node->type->name, type->name))
+ continue;
+
+ list_del(&node->list);
+ kfree(node);
+ err = 0;
+ break;
+ }
+ up_write(&alg_types_sem);
+
+ return err;
+}
+EXPORT_SYMBOL_GPL(af_alg_unregister_type);
+
+static void alg_do_release(const struct af_alg_type *type, void *private)
+{
+ if (!type)
+ return;
+
+ type->release(private);
+ module_put(type->owner);
+}
+
+int af_alg_release(struct socket *sock)
+{
+ if (sock->sk)
+ sock_put(sock->sk);
+ return 0;
+}
+EXPORT_SYMBOL_GPL(af_alg_release);
+
+static int alg_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len)
+{
+ struct sock *sk = sock->sk;
+ struct alg_sock *ask = alg_sk(sk);
+ struct sockaddr_alg *sa = (void *)uaddr;
+ const struct af_alg_type *type;
+ void *private;
+
+ if (sock->state == SS_CONNECTED)
+ return -EINVAL;
+
+ if (addr_len != sizeof(*sa))
+ return -EINVAL;
+
+ sa->salg_type[sizeof(sa->salg_type) - 1] = 0;
+ sa->salg_name[sizeof(sa->salg_name) - 1] = 0;
+
+ type = alg_get_type(sa->salg_type);
+ if (IS_ERR(type) && PTR_ERR(type) == -ENOENT) {
+ request_module("algif-%s", sa->salg_type);
+ type = alg_get_type(sa->salg_type);
+ }
+
+ if (IS_ERR(type))
+ return PTR_ERR(type);
+
+ private = type->bind(sa->salg_name, sa->salg_feat, sa->salg_mask);
+ if (IS_ERR(private)) {
+ module_put(type->owner);
+ return PTR_ERR(private);
+ }
+
+ lock_sock(sk);
+
+ swap(ask->type, type);
+ swap(ask->private, private);
+
+ release_sock(sk);
+
+ alg_do_release(type, private);
+
+ return 0;
+}
+
+static int alg_setkey(struct sock *sk, char __user *ukey,
+ unsigned int keylen)
+{
+ struct alg_sock *ask = alg_sk(sk);
+ const struct af_alg_type *type = ask->type;
+ u8 *key;
+ int err;
+
+ key = sock_kmalloc(sk, keylen, GFP_KERNEL);
+ if (!key)
+ return -ENOMEM;
+
+ err = -EFAULT;
+ if (copy_from_user(key, ukey, keylen))
+ goto out;
+
+ err = type->setkey(ask->private, key, keylen);
+
+out:
+ sock_kfree_s(sk, key, keylen);
+
+ return err;
+}
+
+static int alg_setsockopt(struct socket *sock, int level, int optname,
+ char __user *optval, unsigned int optlen)
+{
+ struct sock *sk = sock->sk;
+ struct alg_sock *ask = alg_sk(sk);
+ const struct af_alg_type *type;
+ int err = -ENOPROTOOPT;
+
+ lock_sock(sk);
+ type = ask->type;
+
+ if (level != SOL_ALG || !type)
+ goto unlock;
+
+ switch (optname) {
+ case ALG_SET_KEY:
+ if (sock->state == SS_CONNECTED)
+ goto unlock;
+ if (!type->setkey)
+ goto unlock;
+
+ err = alg_setkey(sk, optval, optlen);
+ }
+
+unlock:
+ release_sock(sk);
+
+ return err;
+}
+
+int af_alg_accept(struct sock *sk, struct socket *newsock)
+{
+ struct alg_sock *ask = alg_sk(sk);
+ const struct af_alg_type *type;
+ struct sock *sk2;
+ int err;
+
+ lock_sock(sk);
+ type = ask->type;
+
+ err = -EINVAL;
+ if (!type)
+ goto unlock;
+
+ sk2 = sk_alloc(sock_net(sk), PF_ALG, GFP_KERNEL, &alg_proto);
+ err = -ENOMEM;
+ if (!sk2)
+ goto unlock;
+
+ sock_init_data(newsock, sk2);
+ sock_graft(sk2, newsock);
+
+ err = type->accept(ask->private, sk2);
+ if (err) {
+ sk_free(sk2);
+ goto unlock;
+ }
+
+ sk2->sk_family = PF_ALG;
+
+ sock_hold(sk);
+ alg_sk(sk2)->parent = sk;
+ alg_sk(sk2)->type = type;
+
+ newsock->ops = type->ops;
+ newsock->state = SS_CONNECTED;
+
+ err = 0;
+
+unlock:
+ release_sock(sk);
+
+ return err;
+}
+EXPORT_SYMBOL_GPL(af_alg_accept);
+
+static int alg_accept(struct socket *sock, struct socket *newsock, int flags)
+{
+ return af_alg_accept(sock->sk, newsock);
+}
+
+static const struct proto_ops alg_proto_ops = {
+ .family = PF_ALG,
+ .owner = THIS_MODULE,
+
+ .connect = sock_no_connect,
+ .socketpair = sock_no_socketpair,
+ .getname = sock_no_getname,
+ .ioctl = sock_no_ioctl,
+ .listen = sock_no_listen,
+ .shutdown = sock_no_shutdown,
+ .getsockopt = sock_no_getsockopt,
+ .mmap = sock_no_mmap,
+ .sendpage = sock_no_sendpage,
+ .sendmsg = sock_no_sendmsg,
+ .recvmsg = sock_no_recvmsg,
+ .poll = sock_no_poll,
+
+ .bind = alg_bind,
+ .release = af_alg_release,
+ .setsockopt = alg_setsockopt,
+ .accept = alg_accept,
+};
+
+static void alg_sock_destruct(struct sock *sk)
+{
+ struct alg_sock *ask = alg_sk(sk);
+
+ alg_do_release(ask->type, ask->private);
+}
+
+static int alg_create(struct net *net, struct socket *sock, int protocol,
+ int kern)
+{
+ struct sock *sk;
+ int err;
+
+ if (sock->type != SOCK_SEQPACKET)
+ return -ESOCKTNOSUPPORT;
+ if (protocol != 0)
+ return -EPROTONOSUPPORT;
+
+ err = -ENOMEM;
+ sk = sk_alloc(net, PF_ALG, GFP_KERNEL, &alg_proto);
+ if (!sk)
+ goto out;
+
+ sock->ops = &alg_proto_ops;
+ sock_init_data(sock, sk);
+
+ sk->sk_family = PF_ALG;
+ sk->sk_destruct = alg_sock_destruct;
+
+ return 0;
+out:
+ return err;
+}
+
+static const struct net_proto_family alg_family = {
+ .family = PF_ALG,
+ .create = alg_create,
+ .owner = THIS_MODULE,
+};
+
+int af_alg_make_sg(struct af_alg_sgl *sgl, void __user *addr, int len,
+ int write)
+{
+ unsigned long from = (unsigned long)addr;
+ unsigned long npages;
+ unsigned off;
+ int err;
+ int i;
+
+ err = -EFAULT;
+ if (!access_ok(write ? VERIFY_READ : VERIFY_WRITE, addr, len))
+ goto out;
+
+ off = from & ~PAGE_MASK;
+ npages = (off + len + PAGE_SIZE - 1) >> PAGE_SHIFT;
+ if (npages > ALG_MAX_PAGES)
+ npages = ALG_MAX_PAGES;
+
+ err = get_user_pages_fast(from, npages, write, sgl->pages);
+ if (err < 0)
+ goto out;
+
+ npages = err;
+ err = -EINVAL;
+ if (WARN_ON(npages == 0))
+ goto out;
+
+ err = 0;
+
+ sg_init_table(sgl->sg, npages);
+
+ for (i = 0; i < npages; i++) {
+ int plen = min_t(int, len, PAGE_SIZE - off);
+
+ sg_set_page(sgl->sg + i, sgl->pages[i], plen, off);
+
+ off = 0;
+ len -= plen;
+ err += plen;
+ }
+
+out:
+ return err;
+}
+EXPORT_SYMBOL_GPL(af_alg_make_sg);
+
+void af_alg_free_sg(struct af_alg_sgl *sgl)
+{
+ int i;
+
+ i = 0;
+ do {
+ put_page(sgl->pages[i]);
+ } while (!sg_is_last(sgl->sg + (i++)));
+}
+EXPORT_SYMBOL_GPL(af_alg_free_sg);
+
+int af_alg_cmsg_send(struct msghdr *msg, struct af_alg_control *con)
+{
+ struct cmsghdr *cmsg;
+
+ for (cmsg = CMSG_FIRSTHDR(msg); cmsg; cmsg = CMSG_NXTHDR(msg, cmsg)) {
+ if (!CMSG_OK(msg, cmsg))
+ return -EINVAL;
+ if (cmsg->cmsg_level != SOL_ALG)
+ continue;
+
+ switch(cmsg->cmsg_type) {
+ case ALG_SET_IV:
+ if (cmsg->cmsg_len < CMSG_LEN(sizeof(*con->iv)))
+ return -EINVAL;
+ con->iv = (void *)CMSG_DATA(cmsg);
+ if (cmsg->cmsg_len < CMSG_LEN(con->iv->ivlen +
+ sizeof(*con->iv)))
+ return -EINVAL;
+ break;
+
+ case ALG_SET_OP:
+ if (cmsg->cmsg_len < CMSG_LEN(sizeof(u32)))
+ return -EINVAL;
+ con->op = *(u32 *)CMSG_DATA(cmsg);
+ break;
+
+ default:
+ return -EINVAL;
+ }
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(af_alg_cmsg_send);
+
+int af_alg_wait_for_completion(int err, struct af_alg_completion *completion)
+{
+ switch (err) {
+ case -EINPROGRESS:
+ case -EBUSY:
+ wait_for_completion(&completion->completion);
+ INIT_COMPLETION(completion->completion);
+ err = completion->err;
+ break;
+ };
+
+ return err;
+}
+EXPORT_SYMBOL_GPL(af_alg_wait_for_completion);
+
+void af_alg_complete(struct crypto_async_request *req, int err)
+{
+ struct af_alg_completion *completion = req->data;
+
+ completion->err = err;
+ complete(&completion->completion);
+}
+EXPORT_SYMBOL_GPL(af_alg_complete);
+
+static int __init af_alg_init(void)
+{
+ int err = proto_register(&alg_proto, 0);
+
+ if (err)
+ goto out;
+
+ err = sock_register(&alg_family);
+ if (err != 0)
+ goto out_unregister_proto;
+
+out:
+ return err;
+
+out_unregister_proto:
+ proto_unregister(&alg_proto);
+ goto out;
+}
+
+static void __exit af_alg_exit(void)
+{
+ sock_unregister(PF_ALG);
+ proto_unregister(&alg_proto);
+}
+
+module_init(af_alg_init);
+module_exit(af_alg_exit);
+MODULE_LICENSE("GPL");
+MODULE_ALIAS_NETPROTO(AF_ALG);
diff --git a/crypto/algif_hash.c b/crypto/algif_hash.c
new file mode 100644
index 0000000..62122a1
--- /dev/null
+++ b/crypto/algif_hash.c
@@ -0,0 +1,319 @@
+/*
+ * algif_hash: User-space interface for hash algorithms
+ *
+ * This file provides the user-space API for hash algorithms.
+ *
+ * Copyright (c) 2010 Herbert Xu <herbert@gondor.apana.org.au>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ */
+
+#include <crypto/hash.h>
+#include <crypto/if_alg.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/mm.h>
+#include <linux/module.h>
+#include <linux/net.h>
+#include <net/sock.h>
+
+struct hash_ctx {
+ struct af_alg_sgl sgl;
+
+ u8 *result;
+
+ struct af_alg_completion completion;
+
+ unsigned int len;
+ bool more;
+
+ struct ahash_request req;
+};
+
+static int hash_sendmsg(struct kiocb *unused, struct socket *sock,
+ struct msghdr *msg, size_t ignored)
+{
+ int limit = ALG_MAX_PAGES * PAGE_SIZE;
+ struct sock *sk = sock->sk;
+ struct alg_sock *ask = alg_sk(sk);
+ struct hash_ctx *ctx = ask->private;
+ unsigned long iovlen;
+ struct iovec *iov;
+ long copied = 0;
+ int err;
+
+ if (limit > sk->sk_sndbuf)
+ limit = sk->sk_sndbuf;
+
+ lock_sock(sk);
+ if (!ctx->more) {
+ err = crypto_ahash_init(&ctx->req);
+ if (err)
+ goto unlock;
+ }
+
+ ctx->more = 0;
+
+ for (iov = msg->msg_iov, iovlen = msg->msg_iovlen; iovlen > 0;
+ iovlen--, iov++) {
+ unsigned long seglen = iov->iov_len;
+ char __user *from = iov->iov_base;
+
+ while (seglen) {
+ int len = min_t(unsigned long, seglen, limit);
+ int newlen;
+
+ newlen = af_alg_make_sg(&ctx->sgl, from, len, 0);
+ if (newlen < 0)
+ goto unlock;
+
+ ahash_request_set_crypt(&ctx->req, ctx->sgl.sg, NULL,
+ newlen);
+
+ err = af_alg_wait_for_completion(
+ crypto_ahash_update(&ctx->req),
+ &ctx->completion);
+
+ af_alg_free_sg(&ctx->sgl);
+
+ if (err)
+ goto unlock;
+
+ seglen -= newlen;
+ from += newlen;
+ copied += newlen;
+ }
+ }
+
+ err = 0;
+
+ ctx->more = msg->msg_flags & MSG_MORE;
+ if (!ctx->more) {
+ ahash_request_set_crypt(&ctx->req, NULL, ctx->result, 0);
+ err = af_alg_wait_for_completion(crypto_ahash_final(&ctx->req),
+ &ctx->completion);
+ }
+
+unlock:
+ release_sock(sk);
+
+ return err ?: copied;
+}
+
+static ssize_t hash_sendpage(struct socket *sock, struct page *page,
+ int offset, size_t size, int flags)
+{
+ struct sock *sk = sock->sk;
+ struct alg_sock *ask = alg_sk(sk);
+ struct hash_ctx *ctx = ask->private;
+ int err;
+
+ lock_sock(sk);
+ sg_init_table(ctx->sgl.sg, 1);
+ sg_set_page(ctx->sgl.sg, page, size, offset);
+
+ ahash_request_set_crypt(&ctx->req, ctx->sgl.sg, ctx->result, size);
+
+ if (!(flags & MSG_MORE)) {
+ if (ctx->more)
+ err = crypto_ahash_finup(&ctx->req);
+ else
+ err = crypto_ahash_digest(&ctx->req);
+ } else {
+ if (!ctx->more) {
+ err = crypto_ahash_init(&ctx->req);
+ if (err)
+ goto unlock;
+ }
+
+ err = crypto_ahash_update(&ctx->req);
+ }
+
+ err = af_alg_wait_for_completion(err, &ctx->completion);
+ if (err)
+ goto unlock;
+
+ ctx->more = flags & MSG_MORE;
+
+unlock:
+ release_sock(sk);
+
+ return err ?: size;
+}
+
+static int hash_recvmsg(struct kiocb *unused, struct socket *sock,
+ struct msghdr *msg, size_t len, int flags)
+{
+ struct sock *sk = sock->sk;
+ struct alg_sock *ask = alg_sk(sk);
+ struct hash_ctx *ctx = ask->private;
+ unsigned ds = crypto_ahash_digestsize(crypto_ahash_reqtfm(&ctx->req));
+ int err;
+
+ if (len > ds)
+ len = ds;
+ else if (len < ds)
+ msg->msg_flags |= MSG_TRUNC;
+
+ lock_sock(sk);
+ if (ctx->more) {
+ ctx->more = 0;
+ ahash_request_set_crypt(&ctx->req, NULL, ctx->result, 0);
+ err = af_alg_wait_for_completion(crypto_ahash_final(&ctx->req),
+ &ctx->completion);
+ if (err)
+ goto unlock;
+ }
+
+ err = memcpy_toiovec(msg->msg_iov, ctx->result, len);
+
+unlock:
+ release_sock(sk);
+
+ return err ?: len;
+}
+
+static int hash_accept(struct socket *sock, struct socket *newsock, int flags)
+{
+ struct sock *sk = sock->sk;
+ struct alg_sock *ask = alg_sk(sk);
+ struct hash_ctx *ctx = ask->private;
+ struct ahash_request *req = &ctx->req;
+ char state[crypto_ahash_statesize(crypto_ahash_reqtfm(req))];
+ struct sock *sk2;
+ struct alg_sock *ask2;
+ struct hash_ctx *ctx2;
+ int err;
+
+ err = crypto_ahash_export(req, state);
+ if (err)
+ return err;
+
+ err = af_alg_accept(ask->parent, newsock);
+ if (err)
+ return err;
+
+ sk2 = newsock->sk;
+ ask2 = alg_sk(sk2);
+ ctx2 = ask2->private;
+ ctx2->more = 1;
+
+ err = crypto_ahash_import(&ctx2->req, state);
+ if (err) {
+ sock_orphan(sk2);
+ sock_put(sk2);
+ }
+
+ return err;
+}
+
+static struct proto_ops algif_hash_ops = {
+ .family = PF_ALG,
+
+ .connect = sock_no_connect,
+ .socketpair = sock_no_socketpair,
+ .getname = sock_no_getname,
+ .ioctl = sock_no_ioctl,
+ .listen = sock_no_listen,
+ .shutdown = sock_no_shutdown,
+ .getsockopt = sock_no_getsockopt,
+ .mmap = sock_no_mmap,
+ .bind = sock_no_bind,
+ .setsockopt = sock_no_setsockopt,
+ .poll = sock_no_poll,
+
+ .release = af_alg_release,
+ .sendmsg = hash_sendmsg,
+ .sendpage = hash_sendpage,
+ .recvmsg = hash_recvmsg,
+ .accept = hash_accept,
+};
+
+static void *hash_bind(const char *name, u32 type, u32 mask)
+{
+ return crypto_alloc_ahash(name, type, mask);
+}
+
+static void hash_release(void *private)
+{
+ crypto_free_ahash(private);
+}
+
+static int hash_setkey(void *private, const u8 *key, unsigned int keylen)
+{
+ return crypto_ahash_setkey(private, key, keylen);
+}
+
+static void hash_sock_destruct(struct sock *sk)
+{
+ struct alg_sock *ask = alg_sk(sk);
+ struct hash_ctx *ctx = ask->private;
+
+ sock_kfree_s(sk, ctx->result,
+ crypto_ahash_digestsize(crypto_ahash_reqtfm(&ctx->req)));
+ sock_kfree_s(sk, ctx, ctx->len);
+ af_alg_release_parent(sk);
+}
+
+static int hash_accept_parent(void *private, struct sock *sk)
+{
+ struct hash_ctx *ctx;
+ struct alg_sock *ask = alg_sk(sk);
+ unsigned len = sizeof(*ctx) + crypto_ahash_reqsize(private);
+ unsigned ds = crypto_ahash_digestsize(private);
+
+ ctx = sock_kmalloc(sk, len, GFP_KERNEL);
+ if (!ctx)
+ return -ENOMEM;
+
+ ctx->result = sock_kmalloc(sk, ds, GFP_KERNEL);
+ if (!ctx->result) {
+ sock_kfree_s(sk, ctx, len);
+ return -ENOMEM;
+ }
+
+ memset(ctx->result, 0, ds);
+
+ ctx->len = len;
+ ctx->more = 0;
+ af_alg_init_completion(&ctx->completion);
+
+ ask->private = ctx;
+
+ ahash_request_set_tfm(&ctx->req, private);
+ ahash_request_set_callback(&ctx->req, CRYPTO_TFM_REQ_MAY_BACKLOG,
+ af_alg_complete, &ctx->completion);
+
+ sk->sk_destruct = hash_sock_destruct;
+
+ return 0;
+}
+
+static const struct af_alg_type algif_type_hash = {
+ .bind = hash_bind,
+ .release = hash_release,
+ .setkey = hash_setkey,
+ .accept = hash_accept_parent,
+ .ops = &algif_hash_ops,
+ .name = "hash",
+ .owner = THIS_MODULE
+};
+
+static int __init algif_hash_init(void)
+{
+ return af_alg_register_type(&algif_type_hash);
+}
+
+static void __exit algif_hash_exit(void)
+{
+ int err = af_alg_unregister_type(&algif_type_hash);
+ BUG_ON(err);
+}
+
+module_init(algif_hash_init);
+module_exit(algif_hash_exit);
+MODULE_LICENSE("GPL");
diff --git a/crypto/algif_skcipher.c b/crypto/algif_skcipher.c
new file mode 100644
index 0000000..6a6dfc0
--- /dev/null
+++ b/crypto/algif_skcipher.c
@@ -0,0 +1,632 @@
+/*
+ * algif_skcipher: User-space interface for skcipher algorithms
+ *
+ * This file provides the user-space API for symmetric key ciphers.
+ *
+ * Copyright (c) 2010 Herbert Xu <herbert@gondor.apana.org.au>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ */
+
+#include <crypto/scatterwalk.h>
+#include <crypto/skcipher.h>
+#include <crypto/if_alg.h>
+#include <linux/init.h>
+#include <linux/list.h>
+#include <linux/kernel.h>
+#include <linux/mm.h>
+#include <linux/module.h>
+#include <linux/net.h>
+#include <net/sock.h>
+
+struct skcipher_sg_list {
+ struct list_head list;
+
+ int cur;
+
+ struct scatterlist sg[0];
+};
+
+struct skcipher_ctx {
+ struct list_head tsgl;
+ struct af_alg_sgl rsgl;
+
+ void *iv;
+
+ struct af_alg_completion completion;
+
+ unsigned used;
+
+ unsigned int len;
+ bool more;
+ bool merge;
+ bool enc;
+
+ struct ablkcipher_request req;
+};
+
+#define MAX_SGL_ENTS ((PAGE_SIZE - sizeof(struct skcipher_sg_list)) / \
+ sizeof(struct scatterlist) - 1)
+
+static inline int skcipher_sndbuf(struct sock *sk)
+{
+ struct alg_sock *ask = alg_sk(sk);
+ struct skcipher_ctx *ctx = ask->private;
+
+ return max_t(int, max_t(int, sk->sk_sndbuf & PAGE_MASK, PAGE_SIZE) -
+ ctx->used, 0);
+}
+
+static inline bool skcipher_writable(struct sock *sk)
+{
+ return PAGE_SIZE <= skcipher_sndbuf(sk);
+}
+
+static int skcipher_alloc_sgl(struct sock *sk)
+{
+ struct alg_sock *ask = alg_sk(sk);
+ struct skcipher_ctx *ctx = ask->private;
+ struct skcipher_sg_list *sgl;
+ struct scatterlist *sg = NULL;
+
+ sgl = list_entry(ctx->tsgl.prev, struct skcipher_sg_list, list);
+ if (!list_empty(&ctx->tsgl))
+ sg = sgl->sg;
+
+ if (!sg || sgl->cur >= MAX_SGL_ENTS) {
+ sgl = sock_kmalloc(sk, sizeof(*sgl) +
+ sizeof(sgl->sg[0]) * (MAX_SGL_ENTS + 1),
+ GFP_KERNEL);
+ if (!sgl)
+ return -ENOMEM;
+
+ sg_init_table(sgl->sg, MAX_SGL_ENTS + 1);
+ sgl->cur = 0;
+
+ if (sg)
+ scatterwalk_sg_chain(sg, MAX_SGL_ENTS + 1, sgl->sg);
+
+ list_add_tail(&sgl->list, &ctx->tsgl);
+ }
+
+ return 0;
+}
+
+static void skcipher_pull_sgl(struct sock *sk, int used)
+{
+ struct alg_sock *ask = alg_sk(sk);
+ struct skcipher_ctx *ctx = ask->private;
+ struct skcipher_sg_list *sgl;
+ struct scatterlist *sg;
+ int i;
+
+ while (!list_empty(&ctx->tsgl)) {
+ sgl = list_first_entry(&ctx->tsgl, struct skcipher_sg_list,
+ list);
+ sg = sgl->sg;
+
+ for (i = 0; i < sgl->cur; i++) {
+ int plen = min_t(int, used, sg[i].length);
+
+ if (!sg_page(sg + i))
+ continue;
+
+ sg[i].length -= plen;
+ sg[i].offset += plen;
+
+ used -= plen;
+ ctx->used -= plen;
+
+ if (sg[i].length)
+ return;
+
+ put_page(sg_page(sg + i));
+ sg_assign_page(sg + i, NULL);
+ }
+
+ list_del(&sgl->list);
+ sock_kfree_s(sk, sgl,
+ sizeof(*sgl) + sizeof(sgl->sg[0]) *
+ (MAX_SGL_ENTS + 1));
+ }
+
+ if (!ctx->used)
+ ctx->merge = 0;
+}
+
+static void skcipher_free_sgl(struct sock *sk)
+{
+ struct alg_sock *ask = alg_sk(sk);
+ struct skcipher_ctx *ctx = ask->private;
+
+ skcipher_pull_sgl(sk, ctx->used);
+}
+
+static int skcipher_wait_for_wmem(struct sock *sk, unsigned flags)
+{
+ long timeout;
+ DEFINE_WAIT(wait);
+ int err = -ERESTARTSYS;
+
+ if (flags & MSG_DONTWAIT)
+ return -EAGAIN;
+
+ set_bit(SOCK_ASYNC_NOSPACE, &sk->sk_socket->flags);
+
+ for (;;) {
+ if (signal_pending(current))
+ break;
+ prepare_to_wait(sk_sleep(sk), &wait, TASK_INTERRUPTIBLE);
+ timeout = MAX_SCHEDULE_TIMEOUT;
+ if (sk_wait_event(sk, &timeout, skcipher_writable(sk))) {
+ err = 0;
+ break;
+ }
+ }
+ finish_wait(sk_sleep(sk), &wait);
+
+ return err;
+}
+
+static void skcipher_wmem_wakeup(struct sock *sk)
+{
+ struct socket_wq *wq;
+
+ if (!skcipher_writable(sk))
+ return;
+
+ rcu_read_lock();
+ wq = rcu_dereference(sk->sk_wq);
+ if (wq_has_sleeper(wq))
+ wake_up_interruptible_sync_poll(&wq->wait, POLLIN |
+ POLLRDNORM |
+ POLLRDBAND);
+ sk_wake_async(sk, SOCK_WAKE_WAITD, POLL_IN);
+ rcu_read_unlock();
+}
+
+static int skcipher_wait_for_data(struct sock *sk, unsigned flags)
+{
+ struct alg_sock *ask = alg_sk(sk);
+ struct skcipher_ctx *ctx = ask->private;
+ long timeout;
+ DEFINE_WAIT(wait);
+ int err = -ERESTARTSYS;
+
+ if (flags & MSG_DONTWAIT) {
+ return -EAGAIN;
+ }
+
+ set_bit(SOCK_ASYNC_WAITDATA, &sk->sk_socket->flags);
+
+ for (;;) {
+ if (signal_pending(current))
+ break;
+ prepare_to_wait(sk_sleep(sk), &wait, TASK_INTERRUPTIBLE);
+ timeout = MAX_SCHEDULE_TIMEOUT;
+ if (sk_wait_event(sk, &timeout, ctx->used)) {
+ err = 0;
+ break;
+ }
+ }
+ finish_wait(sk_sleep(sk), &wait);
+
+ clear_bit(SOCK_ASYNC_WAITDATA, &sk->sk_socket->flags);
+
+ return err;
+}
+
+static void skcipher_data_wakeup(struct sock *sk)
+{
+ struct alg_sock *ask = alg_sk(sk);
+ struct skcipher_ctx *ctx = ask->private;
+ struct socket_wq *wq;
+
+ if (!ctx->used)
+ return;
+
+ rcu_read_lock();
+ wq = rcu_dereference(sk->sk_wq);
+ if (wq_has_sleeper(wq))
+ wake_up_interruptible_sync_poll(&wq->wait, POLLOUT |
+ POLLRDNORM |
+ POLLRDBAND);
+ sk_wake_async(sk, SOCK_WAKE_SPACE, POLL_OUT);
+ rcu_read_unlock();
+}
+
+static int skcipher_sendmsg(struct kiocb *unused, struct socket *sock,
+ struct msghdr *msg, size_t size)
+{
+ struct sock *sk = sock->sk;
+ struct alg_sock *ask = alg_sk(sk);
+ struct skcipher_ctx *ctx = ask->private;
+ struct crypto_ablkcipher *tfm = crypto_ablkcipher_reqtfm(&ctx->req);
+ unsigned ivsize = crypto_ablkcipher_ivsize(tfm);
+ struct skcipher_sg_list *sgl;
+ struct af_alg_control con = {};
+ long copied = 0;
+ bool enc = 0;
+ int err;
+ int i;
+
+ if (msg->msg_controllen) {
+ err = af_alg_cmsg_send(msg, &con);
+ if (err)
+ return err;
+
+ switch (con.op) {
+ case ALG_OP_ENCRYPT:
+ enc = 1;
+ break;
+ case ALG_OP_DECRYPT:
+ enc = 0;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ if (con.iv && con.iv->ivlen != ivsize)
+ return -EINVAL;
+ }
+
+ err = -EINVAL;
+
+ lock_sock(sk);
+ if (!ctx->more && ctx->used)
+ goto unlock;
+
+ if (!ctx->used) {
+ ctx->enc = enc;
+ if (con.iv)
+ memcpy(ctx->iv, con.iv->iv, ivsize);
+ }
+
+ while (size) {
+ struct scatterlist *sg;
+ unsigned long len = size;
+ int plen;
+
+ if (ctx->merge) {
+ sgl = list_entry(ctx->tsgl.prev,
+ struct skcipher_sg_list, list);
+ sg = sgl->sg + sgl->cur - 1;
+ len = min_t(unsigned long, len,
+ PAGE_SIZE - sg->offset - sg->length);
+
+ err = memcpy_fromiovec(page_address(sg_page(sg)) +
+ sg->offset + sg->length,
+ msg->msg_iov, len);
+ if (err)
+ goto unlock;
+
+ sg->length += len;
+ ctx->merge = (sg->offset + sg->length) &
+ (PAGE_SIZE - 1);
+
+ ctx->used += len;
+ copied += len;
+ size -= len;
+ continue;
+ }
+
+ if (!skcipher_writable(sk)) {
+ err = skcipher_wait_for_wmem(sk, msg->msg_flags);
+ if (err)
+ goto unlock;
+ }
+
+ len = min_t(unsigned long, len, skcipher_sndbuf(sk));
+
+ err = skcipher_alloc_sgl(sk);
+ if (err)
+ goto unlock;
+
+ sgl = list_entry(ctx->tsgl.prev, struct skcipher_sg_list, list);
+ sg = sgl->sg;
+ do {
+ i = sgl->cur;
+ plen = min_t(int, len, PAGE_SIZE);
+
+ sg_assign_page(sg + i, alloc_page(GFP_KERNEL));
+ err = -ENOMEM;
+ if (!sg_page(sg + i))
+ goto unlock;
+
+ err = memcpy_fromiovec(page_address(sg_page(sg + i)),
+ msg->msg_iov, plen);
+ if (err) {
+ __free_page(sg_page(sg + i));
+ sg_assign_page(sg + i, NULL);
+ goto unlock;
+ }
+
+ sg[i].length = plen;
+ len -= plen;
+ ctx->used += plen;
+ copied += plen;
+ size -= plen;
+ sgl->cur++;
+ } while (len && sgl->cur < MAX_SGL_ENTS);
+
+ ctx->merge = plen & (PAGE_SIZE - 1);
+ }
+
+ err = 0;
+
+ ctx->more = msg->msg_flags & MSG_MORE;
+ if (!ctx->more && !list_empty(&ctx->tsgl))
+ sgl = list_entry(ctx->tsgl.prev, struct skcipher_sg_list, list);
+
+unlock:
+ skcipher_data_wakeup(sk);
+ release_sock(sk);
+
+ return copied ?: err;
+}
+
+static ssize_t skcipher_sendpage(struct socket *sock, struct page *page,
+ int offset, size_t size, int flags)
+{
+ struct sock *sk = sock->sk;
+ struct alg_sock *ask = alg_sk(sk);
+ struct skcipher_ctx *ctx = ask->private;
+ struct skcipher_sg_list *sgl;
+ int err = -EINVAL;
+
+ lock_sock(sk);
+ if (!ctx->more && ctx->used)
+ goto unlock;
+
+ if (!size)
+ goto done;
+
+ if (!skcipher_writable(sk)) {
+ err = skcipher_wait_for_wmem(sk, flags);
+ if (err)
+ goto unlock;
+ }
+
+ err = skcipher_alloc_sgl(sk);
+ if (err)
+ goto unlock;
+
+ ctx->merge = 0;
+ sgl = list_entry(ctx->tsgl.prev, struct skcipher_sg_list, list);
+
+ get_page(page);
+ sg_set_page(sgl->sg + sgl->cur, page, size, offset);
+ sgl->cur++;
+ ctx->used += size;
+
+done:
+ ctx->more = flags & MSG_MORE;
+ if (!ctx->more && !list_empty(&ctx->tsgl))
+ sgl = list_entry(ctx->tsgl.prev, struct skcipher_sg_list, list);
+
+unlock:
+ skcipher_data_wakeup(sk);
+ release_sock(sk);
+
+ return err ?: size;
+}
+
+static int skcipher_recvmsg(struct kiocb *unused, struct socket *sock,
+ struct msghdr *msg, size_t ignored, int flags)
+{
+ struct sock *sk = sock->sk;
+ struct alg_sock *ask = alg_sk(sk);
+ struct skcipher_ctx *ctx = ask->private;
+ unsigned bs = crypto_ablkcipher_blocksize(crypto_ablkcipher_reqtfm(
+ &ctx->req));
+ struct skcipher_sg_list *sgl;
+ struct scatterlist *sg;
+ unsigned long iovlen;
+ struct iovec *iov;
+ int err = -EAGAIN;
+ int used;
+ long copied = 0;
+
+ lock_sock(sk);
+ for (iov = msg->msg_iov, iovlen = msg->msg_iovlen; iovlen > 0;
+ iovlen--, iov++) {
+ unsigned long seglen = iov->iov_len;
+ char __user *from = iov->iov_base;
+
+ while (seglen) {
+ sgl = list_first_entry(&ctx->tsgl,
+ struct skcipher_sg_list, list);
+ sg = sgl->sg;
+
+ while (!sg->length)
+ sg++;
+
+ used = ctx->used;
+ if (!used) {
+ err = skcipher_wait_for_data(sk, flags);
+ if (err)
+ goto unlock;
+ }
+
+ used = min_t(unsigned long, used, seglen);
+
+ used = af_alg_make_sg(&ctx->rsgl, from, used, 1);
+ err = used;
+ if (err < 0)
+ goto unlock;
+
+ if (ctx->more || used < ctx->used)
+ used -= used % bs;
+
+ err = -EINVAL;
+ if (!used)
+ goto free;
+
+ ablkcipher_request_set_crypt(&ctx->req, sg,
+ ctx->rsgl.sg, used,
+ ctx->iv);
+
+ err = af_alg_wait_for_completion(
+ ctx->enc ?
+ crypto_ablkcipher_encrypt(&ctx->req) :
+ crypto_ablkcipher_decrypt(&ctx->req),
+ &ctx->completion);
+
+free:
+ af_alg_free_sg(&ctx->rsgl);
+
+ if (err)
+ goto unlock;
+
+ copied += used;
+ from += used;
+ seglen -= used;
+ skcipher_pull_sgl(sk, used);
+ }
+ }
+
+ err = 0;
+
+unlock:
+ skcipher_wmem_wakeup(sk);
+ release_sock(sk);
+
+ return copied ?: err;
+}
+
+
+static unsigned int skcipher_poll(struct file *file, struct socket *sock,
+ poll_table *wait)
+{
+ struct sock *sk = sock->sk;
+ struct alg_sock *ask = alg_sk(sk);
+ struct skcipher_ctx *ctx = ask->private;
+ unsigned int mask;
+
+ sock_poll_wait(file, sk_sleep(sk), wait);
+ mask = 0;
+
+ if (ctx->used)
+ mask |= POLLIN | POLLRDNORM;
+
+ if (skcipher_writable(sk))
+ mask |= POLLOUT | POLLWRNORM | POLLWRBAND;
+
+ return mask;
+}
+
+static struct proto_ops algif_skcipher_ops = {
+ .family = PF_ALG,
+
+ .connect = sock_no_connect,
+ .socketpair = sock_no_socketpair,
+ .getname = sock_no_getname,
+ .ioctl = sock_no_ioctl,
+ .listen = sock_no_listen,
+ .shutdown = sock_no_shutdown,
+ .getsockopt = sock_no_getsockopt,
+ .mmap = sock_no_mmap,
+ .bind = sock_no_bind,
+ .accept = sock_no_accept,
+ .setsockopt = sock_no_setsockopt,
+
+ .release = af_alg_release,
+ .sendmsg = skcipher_sendmsg,
+ .sendpage = skcipher_sendpage,
+ .recvmsg = skcipher_recvmsg,
+ .poll = skcipher_poll,
+};
+
+static void *skcipher_bind(const char *name, u32 type, u32 mask)
+{
+ return crypto_alloc_ablkcipher(name, type, mask);
+}
+
+static void skcipher_release(void *private)
+{
+ crypto_free_ablkcipher(private);
+}
+
+static int skcipher_setkey(void *private, const u8 *key, unsigned int keylen)
+{
+ return crypto_ablkcipher_setkey(private, key, keylen);
+}
+
+static void skcipher_sock_destruct(struct sock *sk)
+{
+ struct alg_sock *ask = alg_sk(sk);
+ struct skcipher_ctx *ctx = ask->private;
+ struct crypto_ablkcipher *tfm = crypto_ablkcipher_reqtfm(&ctx->req);
+
+ skcipher_free_sgl(sk);
+ sock_kfree_s(sk, ctx->iv, crypto_ablkcipher_ivsize(tfm));
+ sock_kfree_s(sk, ctx, ctx->len);
+ af_alg_release_parent(sk);
+}
+
+static int skcipher_accept_parent(void *private, struct sock *sk)
+{
+ struct skcipher_ctx *ctx;
+ struct alg_sock *ask = alg_sk(sk);
+ unsigned int len = sizeof(*ctx) + crypto_ablkcipher_reqsize(private);
+
+ ctx = sock_kmalloc(sk, len, GFP_KERNEL);
+ if (!ctx)
+ return -ENOMEM;
+
+ ctx->iv = sock_kmalloc(sk, crypto_ablkcipher_ivsize(private),
+ GFP_KERNEL);
+ if (!ctx->iv) {
+ sock_kfree_s(sk, ctx, len);
+ return -ENOMEM;
+ }
+
+ memset(ctx->iv, 0, crypto_ablkcipher_ivsize(private));
+
+ INIT_LIST_HEAD(&ctx->tsgl);
+ ctx->len = len;
+ ctx->used = 0;
+ ctx->more = 0;
+ ctx->merge = 0;
+ ctx->enc = 0;
+ af_alg_init_completion(&ctx->completion);
+
+ ask->private = ctx;
+
+ ablkcipher_request_set_tfm(&ctx->req, private);
+ ablkcipher_request_set_callback(&ctx->req, CRYPTO_TFM_REQ_MAY_BACKLOG,
+ af_alg_complete, &ctx->completion);
+
+ sk->sk_destruct = skcipher_sock_destruct;
+
+ return 0;
+}
+
+static const struct af_alg_type algif_type_skcipher = {
+ .bind = skcipher_bind,
+ .release = skcipher_release,
+ .setkey = skcipher_setkey,
+ .accept = skcipher_accept_parent,
+ .ops = &algif_skcipher_ops,
+ .name = "skcipher",
+ .owner = THIS_MODULE
+};
+
+static int __init algif_skcipher_init(void)
+{
+ return af_alg_register_type(&algif_type_skcipher);
+}
+
+static void __exit algif_skcipher_exit(void)
+{
+ int err = af_alg_unregister_type(&algif_type_skcipher);
+ BUG_ON(err);
+}
+
+module_init(algif_skcipher_init);
+module_exit(algif_skcipher_exit);
+MODULE_LICENSE("GPL");
diff --git a/crypto/authenc.c b/crypto/authenc.c
index a5a22cf..5ef7ba6 100644
--- a/crypto/authenc.c
+++ b/crypto/authenc.c
@@ -107,20 +107,6 @@
goto out;
}
-static void authenc_chain(struct scatterlist *head, struct scatterlist *sg,
- int chain)
-{
- if (chain) {
- head->length += sg->length;
- sg = scatterwalk_sg_next(sg);
- }
-
- if (sg)
- scatterwalk_sg_chain(head, 2, sg);
- else
- sg_mark_end(head);
-}
-
static void authenc_geniv_ahash_update_done(struct crypto_async_request *areq,
int err)
{
@@ -345,7 +331,7 @@
if (ivsize) {
sg_init_table(cipher, 2);
sg_set_buf(cipher, iv, ivsize);
- authenc_chain(cipher, dst, vdst == iv + ivsize);
+ scatterwalk_crypto_chain(cipher, dst, vdst == iv + ivsize, 2);
dst = cipher;
cryptlen += ivsize;
}
@@ -354,7 +340,7 @@
authenc_ahash_fn = crypto_authenc_ahash;
sg_init_table(asg, 2);
sg_set_page(asg, sg_page(assoc), assoc->length, assoc->offset);
- authenc_chain(asg, dst, 0);
+ scatterwalk_crypto_chain(asg, dst, 0, 2);
dst = asg;
cryptlen += req->assoclen;
}
@@ -499,7 +485,7 @@
if (ivsize) {
sg_init_table(cipher, 2);
sg_set_buf(cipher, iv, ivsize);
- authenc_chain(cipher, src, vsrc == iv + ivsize);
+ scatterwalk_crypto_chain(cipher, src, vsrc == iv + ivsize, 2);
src = cipher;
cryptlen += ivsize;
}
@@ -508,7 +494,7 @@
authenc_ahash_fn = crypto_authenc_ahash;
sg_init_table(asg, 2);
sg_set_page(asg, sg_page(assoc), assoc->length, assoc->offset);
- authenc_chain(asg, src, 0);
+ scatterwalk_crypto_chain(asg, src, 0, 2);
src = asg;
cryptlen += req->assoclen;
}
diff --git a/crypto/cast5.c b/crypto/cast5.c
index a1d2294..4a230dd 100644
--- a/crypto/cast5.c
+++ b/crypto/cast5.c
@@ -604,36 +604,23 @@
* Rounds 3, 6, 9, 12, and 15 use f function Type 3.
*/
+ t = l; l = r; r = t ^ F1(r, Km[0], Kr[0]);
+ t = l; l = r; r = t ^ F2(r, Km[1], Kr[1]);
+ t = l; l = r; r = t ^ F3(r, Km[2], Kr[2]);
+ t = l; l = r; r = t ^ F1(r, Km[3], Kr[3]);
+ t = l; l = r; r = t ^ F2(r, Km[4], Kr[4]);
+ t = l; l = r; r = t ^ F3(r, Km[5], Kr[5]);
+ t = l; l = r; r = t ^ F1(r, Km[6], Kr[6]);
+ t = l; l = r; r = t ^ F2(r, Km[7], Kr[7]);
+ t = l; l = r; r = t ^ F3(r, Km[8], Kr[8]);
+ t = l; l = r; r = t ^ F1(r, Km[9], Kr[9]);
+ t = l; l = r; r = t ^ F2(r, Km[10], Kr[10]);
+ t = l; l = r; r = t ^ F3(r, Km[11], Kr[11]);
if (!(c->rr)) {
- t = l; l = r; r = t ^ F1(r, Km[0], Kr[0]);
- t = l; l = r; r = t ^ F2(r, Km[1], Kr[1]);
- t = l; l = r; r = t ^ F3(r, Km[2], Kr[2]);
- t = l; l = r; r = t ^ F1(r, Km[3], Kr[3]);
- t = l; l = r; r = t ^ F2(r, Km[4], Kr[4]);
- t = l; l = r; r = t ^ F3(r, Km[5], Kr[5]);
- t = l; l = r; r = t ^ F1(r, Km[6], Kr[6]);
- t = l; l = r; r = t ^ F2(r, Km[7], Kr[7]);
- t = l; l = r; r = t ^ F3(r, Km[8], Kr[8]);
- t = l; l = r; r = t ^ F1(r, Km[9], Kr[9]);
- t = l; l = r; r = t ^ F2(r, Km[10], Kr[10]);
- t = l; l = r; r = t ^ F3(r, Km[11], Kr[11]);
t = l; l = r; r = t ^ F1(r, Km[12], Kr[12]);
t = l; l = r; r = t ^ F2(r, Km[13], Kr[13]);
t = l; l = r; r = t ^ F3(r, Km[14], Kr[14]);
t = l; l = r; r = t ^ F1(r, Km[15], Kr[15]);
- } else {
- t = l; l = r; r = t ^ F1(r, Km[0], Kr[0]);
- t = l; l = r; r = t ^ F2(r, Km[1], Kr[1]);
- t = l; l = r; r = t ^ F3(r, Km[2], Kr[2]);
- t = l; l = r; r = t ^ F1(r, Km[3], Kr[3]);
- t = l; l = r; r = t ^ F2(r, Km[4], Kr[4]);
- t = l; l = r; r = t ^ F3(r, Km[5], Kr[5]);
- t = l; l = r; r = t ^ F1(r, Km[6], Kr[6]);
- t = l; l = r; r = t ^ F2(r, Km[7], Kr[7]);
- t = l; l = r; r = t ^ F3(r, Km[8], Kr[8]);
- t = l; l = r; r = t ^ F1(r, Km[9], Kr[9]);
- t = l; l = r; r = t ^ F2(r, Km[10], Kr[10]);
- t = l; l = r; r = t ^ F3(r, Km[11], Kr[11]);
}
/* c1...c64 <-- (R16,L16). (Exchange final blocks L16, R16 and
@@ -663,32 +650,19 @@
t = l; l = r; r = t ^ F3(r, Km[14], Kr[14]);
t = l; l = r; r = t ^ F2(r, Km[13], Kr[13]);
t = l; l = r; r = t ^ F1(r, Km[12], Kr[12]);
- t = l; l = r; r = t ^ F3(r, Km[11], Kr[11]);
- t = l; l = r; r = t ^ F2(r, Km[10], Kr[10]);
- t = l; l = r; r = t ^ F1(r, Km[9], Kr[9]);
- t = l; l = r; r = t ^ F3(r, Km[8], Kr[8]);
- t = l; l = r; r = t ^ F2(r, Km[7], Kr[7]);
- t = l; l = r; r = t ^ F1(r, Km[6], Kr[6]);
- t = l; l = r; r = t ^ F3(r, Km[5], Kr[5]);
- t = l; l = r; r = t ^ F2(r, Km[4], Kr[4]);
- t = l; l = r; r = t ^ F1(r, Km[3], Kr[3]);
- t = l; l = r; r = t ^ F3(r, Km[2], Kr[2]);
- t = l; l = r; r = t ^ F2(r, Km[1], Kr[1]);
- t = l; l = r; r = t ^ F1(r, Km[0], Kr[0]);
- } else {
- t = l; l = r; r = t ^ F3(r, Km[11], Kr[11]);
- t = l; l = r; r = t ^ F2(r, Km[10], Kr[10]);
- t = l; l = r; r = t ^ F1(r, Km[9], Kr[9]);
- t = l; l = r; r = t ^ F3(r, Km[8], Kr[8]);
- t = l; l = r; r = t ^ F2(r, Km[7], Kr[7]);
- t = l; l = r; r = t ^ F1(r, Km[6], Kr[6]);
- t = l; l = r; r = t ^ F3(r, Km[5], Kr[5]);
- t = l; l = r; r = t ^ F2(r, Km[4], Kr[4]);
- t = l; l = r; r = t ^ F1(r, Km[3], Kr[3]);
- t = l; l = r; r = t ^ F3(r, Km[2], Kr[2]);
- t = l; l = r; r = t ^ F2(r, Km[1], Kr[1]);
- t = l; l = r; r = t ^ F1(r, Km[0], Kr[0]);
}
+ t = l; l = r; r = t ^ F3(r, Km[11], Kr[11]);
+ t = l; l = r; r = t ^ F2(r, Km[10], Kr[10]);
+ t = l; l = r; r = t ^ F1(r, Km[9], Kr[9]);
+ t = l; l = r; r = t ^ F3(r, Km[8], Kr[8]);
+ t = l; l = r; r = t ^ F2(r, Km[7], Kr[7]);
+ t = l; l = r; r = t ^ F1(r, Km[6], Kr[6]);
+ t = l; l = r; r = t ^ F3(r, Km[5], Kr[5]);
+ t = l; l = r; r = t ^ F2(r, Km[4], Kr[4]);
+ t = l; l = r; r = t ^ F1(r, Km[3], Kr[3]);
+ t = l; l = r; r = t ^ F3(r, Km[2], Kr[2]);
+ t = l; l = r; r = t ^ F2(r, Km[1], Kr[1]);
+ t = l; l = r; r = t ^ F1(r, Km[0], Kr[0]);
dst[0] = cpu_to_be32(r);
dst[1] = cpu_to_be32(l);
diff --git a/crypto/crypto_wq.c b/crypto/crypto_wq.c
index fdcf624..b980ee1 100644
--- a/crypto/crypto_wq.c
+++ b/crypto/crypto_wq.c
@@ -20,7 +20,8 @@
static int __init crypto_wq_init(void)
{
- kcrypto_wq = create_workqueue("crypto");
+ kcrypto_wq = alloc_workqueue("crypto",
+ WQ_MEM_RECLAIM | WQ_CPU_INTENSIVE, 1);
if (unlikely(!kcrypto_wq))
return -ENOMEM;
return 0;
diff --git a/crypto/deflate.c b/crypto/deflate.c
index 463dc85..cbc7a33 100644
--- a/crypto/deflate.c
+++ b/crypto/deflate.c
@@ -48,12 +48,11 @@
int ret = 0;
struct z_stream_s *stream = &ctx->comp_stream;
- stream->workspace = vmalloc(zlib_deflate_workspacesize());
+ stream->workspace = vzalloc(zlib_deflate_workspacesize());
if (!stream->workspace) {
ret = -ENOMEM;
goto out;
}
- memset(stream->workspace, 0, zlib_deflate_workspacesize());
ret = zlib_deflateInit2(stream, DEFLATE_DEF_LEVEL, Z_DEFLATED,
-DEFLATE_DEF_WINBITS, DEFLATE_DEF_MEMLEVEL,
Z_DEFAULT_STRATEGY);
diff --git a/crypto/eseqiv.c b/crypto/eseqiv.c
index 3ca3b66..42ce9f5 100644
--- a/crypto/eseqiv.c
+++ b/crypto/eseqiv.c
@@ -62,20 +62,6 @@
skcipher_givcrypt_complete(req, err);
}
-static void eseqiv_chain(struct scatterlist *head, struct scatterlist *sg,
- int chain)
-{
- if (chain) {
- head->length += sg->length;
- sg = scatterwalk_sg_next(sg);
- }
-
- if (sg)
- scatterwalk_sg_chain(head, 2, sg);
- else
- sg_mark_end(head);
-}
-
static int eseqiv_givencrypt(struct skcipher_givcrypt_request *req)
{
struct crypto_ablkcipher *geniv = skcipher_givcrypt_reqtfm(req);
@@ -124,13 +110,13 @@
sg_init_table(reqctx->src, 2);
sg_set_buf(reqctx->src, giv, ivsize);
- eseqiv_chain(reqctx->src, osrc, vsrc == giv + ivsize);
+ scatterwalk_crypto_chain(reqctx->src, osrc, vsrc == giv + ivsize, 2);
dst = reqctx->src;
if (osrc != odst) {
sg_init_table(reqctx->dst, 2);
sg_set_buf(reqctx->dst, giv, ivsize);
- eseqiv_chain(reqctx->dst, odst, vdst == giv + ivsize);
+ scatterwalk_crypto_chain(reqctx->dst, odst, vdst == giv + ivsize, 2);
dst = reqctx->dst;
}
diff --git a/crypto/gcm.c b/crypto/gcm.c
index 2f5fbba..1a25263 100644
--- a/crypto/gcm.c
+++ b/crypto/gcm.c
@@ -1102,21 +1102,6 @@
return crypto_aead_setauthsize(ctx->child, authsize);
}
-/* this is the same as crypto_authenc_chain */
-static void crypto_rfc4543_chain(struct scatterlist *head,
- struct scatterlist *sg, int chain)
-{
- if (chain) {
- head->length += sg->length;
- sg = scatterwalk_sg_next(sg);
- }
-
- if (sg)
- scatterwalk_sg_chain(head, 2, sg);
- else
- sg_mark_end(head);
-}
-
static struct aead_request *crypto_rfc4543_crypt(struct aead_request *req,
int enc)
{
@@ -1154,13 +1139,13 @@
sg_init_table(payload, 2);
sg_set_buf(payload, req->iv, 8);
- crypto_rfc4543_chain(payload, dst, vdst == req->iv + 8);
+ scatterwalk_crypto_chain(payload, dst, vdst == req->iv + 8, 2);
assoclen += 8 + req->cryptlen - (enc ? 0 : authsize);
sg_init_table(assoc, 2);
sg_set_page(assoc, sg_page(req->assoc), req->assoc->length,
req->assoc->offset);
- crypto_rfc4543_chain(assoc, payload, 0);
+ scatterwalk_crypto_chain(assoc, payload, 0, 2);
aead_request_set_tfm(subreq, ctx->child);
aead_request_set_callback(subreq, req->base.flags, req->base.complete,
diff --git a/crypto/pcrypt.c b/crypto/pcrypt.c
index 75586f1..29a89da 100644
--- a/crypto/pcrypt.c
+++ b/crypto/pcrypt.c
@@ -455,7 +455,8 @@
get_online_cpus();
- pcrypt->wq = create_workqueue(name);
+ pcrypt->wq = alloc_workqueue(name,
+ WQ_MEM_RECLAIM | WQ_CPU_INTENSIVE, 1);
if (!pcrypt->wq)
goto err;
diff --git a/crypto/rmd128.c b/crypto/rmd128.c
index 1ceb673..8a0f68b 100644
--- a/crypto/rmd128.c
+++ b/crypto/rmd128.c
@@ -5,7 +5,7 @@
*
* Based on the reference implementation by Antoon Bosselaers, ESAT-COSIC
*
- * Copyright (c) 2008 Adrian-Ken Rueegsegger <rueegsegger (at) swiss-it.ch>
+ * Copyright (c) 2008 Adrian-Ken Rueegsegger <ken@codelabs.ch>
*
* 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
@@ -325,4 +325,5 @@
module_exit(rmd128_mod_fini);
MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Adrian-Ken Rueegsegger <ken@codelabs.ch>");
MODULE_DESCRIPTION("RIPEMD-128 Message Digest");
diff --git a/crypto/rmd160.c b/crypto/rmd160.c
index 472261fc..525d7bb 100644
--- a/crypto/rmd160.c
+++ b/crypto/rmd160.c
@@ -5,7 +5,7 @@
*
* Based on the reference implementation by Antoon Bosselaers, ESAT-COSIC
*
- * Copyright (c) 2008 Adrian-Ken Rueegsegger <rueegsegger (at) swiss-it.ch>
+ * Copyright (c) 2008 Adrian-Ken Rueegsegger <ken@codelabs.ch>
*
* 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
@@ -369,4 +369,5 @@
module_exit(rmd160_mod_fini);
MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Adrian-Ken Rueegsegger <ken@codelabs.ch>");
MODULE_DESCRIPTION("RIPEMD-160 Message Digest");
diff --git a/crypto/rmd256.c b/crypto/rmd256.c
index 72eafa8..69293d9 100644
--- a/crypto/rmd256.c
+++ b/crypto/rmd256.c
@@ -5,7 +5,7 @@
*
* Based on the reference implementation by Antoon Bosselaers, ESAT-COSIC
*
- * Copyright (c) 2008 Adrian-Ken Rueegsegger <rueegsegger (at) swiss-it.ch>
+ * Copyright (c) 2008 Adrian-Ken Rueegsegger <ken@codelabs.ch>
*
* 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
@@ -344,4 +344,5 @@
module_exit(rmd256_mod_fini);
MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Adrian-Ken Rueegsegger <ken@codelabs.ch>");
MODULE_DESCRIPTION("RIPEMD-256 Message Digest");
diff --git a/crypto/rmd320.c b/crypto/rmd320.c
index 86becab..09f97df 100644
--- a/crypto/rmd320.c
+++ b/crypto/rmd320.c
@@ -5,7 +5,7 @@
*
* Based on the reference implementation by Antoon Bosselaers, ESAT-COSIC
*
- * Copyright (c) 2008 Adrian-Ken Rueegsegger <rueegsegger (at) swiss-it.ch>
+ * Copyright (c) 2008 Adrian-Ken Rueegsegger <ken@codelabs.ch>
*
* 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
@@ -393,4 +393,5 @@
module_exit(rmd320_mod_fini);
MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Adrian-Ken Rueegsegger <ken@codelabs.ch>");
MODULE_DESCRIPTION("RIPEMD-320 Message Digest");
diff --git a/crypto/shash.c b/crypto/shash.c
index 22fd943..76f74b9 100644
--- a/crypto/shash.c
+++ b/crypto/shash.c
@@ -310,7 +310,13 @@
static int shash_async_import(struct ahash_request *req, const void *in)
{
- return crypto_shash_import(ahash_request_ctx(req), in);
+ struct crypto_shash **ctx = crypto_ahash_ctx(crypto_ahash_reqtfm(req));
+ struct shash_desc *desc = ahash_request_ctx(req);
+
+ desc->tfm = *ctx;
+ desc->flags = req->base.flags;
+
+ return crypto_shash_import(desc, in);
}
static void crypto_exit_shash_ops_async(struct crypto_tfm *tfm)
diff --git a/crypto/tcrypt.c b/crypto/tcrypt.c
index 3ca68f9..9aac5e5 100644
--- a/crypto/tcrypt.c
+++ b/crypto/tcrypt.c
@@ -8,6 +8,13 @@
* Copyright (c) 2002 Jean-Francois Dive <jef@linuxbe.org>
* Copyright (c) 2007 Nokia Siemens Networks
*
+ * Updated RFC4106 AES-GCM testing.
+ * Authors: Aidan O'Mahony (aidan.o.mahony@intel.com)
+ * Adrian Hoban <adrian.hoban@intel.com>
+ * Gabriele Paoloni <gabriele.paoloni@intel.com>
+ * Tadeusz Struk (tadeusz.struk@intel.com)
+ * Copyright (c) 2010, Intel Corporation.
+ *
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the Free
* Software Foundation; either version 2 of the License, or (at your option)
@@ -980,6 +987,10 @@
ret += tcrypt_test("ansi_cprng");
break;
+ case 151:
+ ret += tcrypt_test("rfc4106(gcm(aes))");
+ break;
+
case 200:
test_cipher_speed("ecb(aes)", ENCRYPT, sec, NULL, 0,
speed_template_16_24_32);
diff --git a/crypto/testmgr.c b/crypto/testmgr.c
index fa8c8f7..27ea9fe 100644
--- a/crypto/testmgr.c
+++ b/crypto/testmgr.c
@@ -6,6 +6,13 @@
* Copyright (c) 2007 Nokia Siemens Networks
* Copyright (c) 2008 Herbert Xu <herbert@gondor.apana.org.au>
*
+ * Updated RFC4106 AES-GCM testing.
+ * Authors: Aidan O'Mahony (aidan.o.mahony@intel.com)
+ * Adrian Hoban <adrian.hoban@intel.com>
+ * Gabriele Paoloni <gabriele.paoloni@intel.com>
+ * Tadeusz Struk (tadeusz.struk@intel.com)
+ * Copyright (c) 2010, Intel Corporation.
+ *
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the Free
* Software Foundation; either version 2 of the License, or (at your option)
@@ -2242,6 +2249,23 @@
}
}
}, {
+ .alg = "rfc4106(gcm(aes))",
+ .test = alg_test_aead,
+ .suite = {
+ .aead = {
+ .enc = {
+ .vecs = aes_gcm_rfc4106_enc_tv_template,
+ .count = AES_GCM_4106_ENC_TEST_VECTORS
+ },
+ .dec = {
+ .vecs = aes_gcm_rfc4106_dec_tv_template,
+ .count = AES_GCM_4106_DEC_TEST_VECTORS
+ }
+ }
+ }
+ }, {
+
+
.alg = "rfc4309(ccm(aes))",
.test = alg_test_aead,
.fips_allowed = 1,
diff --git a/crypto/testmgr.h b/crypto/testmgr.h
index 74e3537..834af7f 100644
--- a/crypto/testmgr.h
+++ b/crypto/testmgr.h
@@ -6,6 +6,15 @@
* Copyright (c) 2007 Nokia Siemens Networks
* Copyright (c) 2008 Herbert Xu <herbert@gondor.apana.org.au>
*
+ * Updated RFC4106 AES-GCM testing. Some test vectors were taken from
+ * http://csrc.nist.gov/groups/ST/toolkit/BCM/documents/proposedmodes/
+ * gcm/gcm-test-vectors.tar.gz
+ * Authors: Aidan O'Mahony (aidan.o.mahony@intel.com)
+ * Adrian Hoban <adrian.hoban@intel.com>
+ * Gabriele Paoloni <gabriele.paoloni@intel.com>
+ * Tadeusz Struk (tadeusz.struk@intel.com)
+ * Copyright (c) 2010, Intel Corporation.
+ *
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the Free
* Software Foundation; either version 2 of the License, or (at your option)
@@ -2947,6 +2956,8 @@
#define AES_CTR_3686_DEC_TEST_VECTORS 6
#define AES_GCM_ENC_TEST_VECTORS 9
#define AES_GCM_DEC_TEST_VECTORS 8
+#define AES_GCM_4106_ENC_TEST_VECTORS 7
+#define AES_GCM_4106_DEC_TEST_VECTORS 7
#define AES_CCM_ENC_TEST_VECTORS 7
#define AES_CCM_DEC_TEST_VECTORS 7
#define AES_CCM_4309_ENC_TEST_VECTORS 7
@@ -5829,6 +5840,356 @@
}
};
+static struct aead_testvec aes_gcm_rfc4106_enc_tv_template[] = {
+ { /* Generated using Crypto++ */
+ .key = zeroed_string,
+ .klen = 20,
+ .iv = zeroed_string,
+ .input = zeroed_string,
+ .ilen = 16,
+ .assoc = zeroed_string,
+ .alen = 8,
+ .result = "\x03\x88\xDA\xCE\x60\xB6\xA3\x92"
+ "\xF3\x28\xC2\xB9\x71\xB2\xFE\x78"
+ "\x97\xFE\x4C\x23\x37\x42\x01\xE0"
+ "\x81\x9F\x8D\xC5\xD7\x41\xA0\x1B",
+ .rlen = 32,
+ },{
+ .key = "\xfe\xff\xe9\x92\x86\x65\x73\x1c"
+ "\x6d\x6a\x8f\x94\x67\x30\x83\x08"
+ "\x00\x00\x00\x00",
+ .klen = 20,
+ .iv = "\x00\x00\x00\x00\x00\x00\x00\x01"
+ "\x00\x00\x00\x00",
+ .input = zeroed_string,
+ .ilen = 16,
+ .assoc = zeroed_string,
+ .alen = 8,
+ .result = "\xC0\x0D\x8B\x42\x0F\x8F\x34\x18"
+ "\x88\xB1\xC5\xBC\xC5\xB6\xD6\x28"
+ "\x6A\x9D\xDF\x11\x5E\xFE\x5E\x9D"
+ "\x2F\x70\x44\x92\xF7\xF2\xE3\xEF",
+ .rlen = 32,
+
+ }, {
+ .key = "\xfe\xff\xe9\x92\x86\x65\x73\x1c"
+ "\x6d\x6a\x8f\x94\x67\x30\x83\x08"
+ "\x00\x00\x00\x00",
+ .klen = 20,
+ .iv = zeroed_string,
+ .input = "\x01\x01\x01\x01\x01\x01\x01\x01"
+ "\x01\x01\x01\x01\x01\x01\x01\x01",
+ .ilen = 16,
+ .assoc = zeroed_string,
+ .alen = 8,
+ .result = "\x4B\xB1\xB5\xE3\x25\x71\x70\xDE"
+ "\x7F\xC9\x9C\xA5\x14\x19\xF2\xAC"
+ "\x0B\x8F\x88\x69\x17\xE6\xB4\x3C"
+ "\xB1\x68\xFD\x14\x52\x64\x61\xB2",
+ .rlen = 32,
+ }, {
+ .key = "\xfe\xff\xe9\x92\x86\x65\x73\x1c"
+ "\x6d\x6a\x8f\x94\x67\x30\x83\x08"
+ "\x00\x00\x00\x00",
+ .klen = 20,
+ .iv = zeroed_string,
+ .input = "\x01\x01\x01\x01\x01\x01\x01\x01"
+ "\x01\x01\x01\x01\x01\x01\x01\x01",
+ .ilen = 16,
+ .assoc = "\x01\x01\x01\x01\x01\x01\x01\x01",
+ .alen = 8,
+ .result = "\x4B\xB1\xB5\xE3\x25\x71\x70\xDE"
+ "\x7F\xC9\x9C\xA5\x14\x19\xF2\xAC"
+ "\x90\x92\xB7\xE3\x5F\xA3\x9A\x63"
+ "\x7E\xD7\x1F\xD8\xD3\x7C\x4B\xF5",
+ .rlen = 32,
+ }, {
+ .key = "\xfe\xff\xe9\x92\x86\x65\x73\x1c"
+ "\x6d\x6a\x8f\x94\x67\x30\x83\x08"
+ "\x00\x00\x00\x00",
+ .klen = 20,
+ .iv = "\x00\x00\x00\x00\x00\x00\x00\x01"
+ "\x00\x00\x00\x00",
+ .input = "\x01\x01\x01\x01\x01\x01\x01\x01"
+ "\x01\x01\x01\x01\x01\x01\x01\x01",
+ .ilen = 16,
+ .assoc = "\x01\x01\x01\x01\x01\x01\x01\x01",
+ .alen = 8,
+ .result = "\xC1\x0C\x8A\x43\x0E\x8E\x35\x19"
+ "\x89\xB0\xC4\xBD\xC4\xB7\xD7\x29"
+ "\x64\x50\xF9\x32\x13\xFB\x74\x61"
+ "\xF4\xED\x52\xD3\xC5\x10\x55\x3C",
+ .rlen = 32,
+ }, {
+ .key = "\xfe\xff\xe9\x92\x86\x65\x73\x1c"
+ "\x6d\x6a\x8f\x94\x67\x30\x83\x08"
+ "\x00\x00\x00\x00",
+ .klen = 20,
+ .iv = "\x00\x00\x00\x00\x00\x00\x00\x01"
+ "\x00\x00\x00\x00",
+ .input = "\x01\x01\x01\x01\x01\x01\x01\x01"
+ "\x01\x01\x01\x01\x01\x01\x01\x01"
+ "\x01\x01\x01\x01\x01\x01\x01\x01"
+ "\x01\x01\x01\x01\x01\x01\x01\x01"
+ "\x01\x01\x01\x01\x01\x01\x01\x01"
+ "\x01\x01\x01\x01\x01\x01\x01\x01"
+ "\x01\x01\x01\x01\x01\x01\x01\x01"
+ "\x01\x01\x01\x01\x01\x01\x01\x01",
+ .ilen = 64,
+ .assoc = "\x01\x01\x01\x01\x01\x01\x01\x01",
+ .alen = 8,
+ .result = "\xC1\x0C\x8A\x43\x0E\x8E\x35\x19"
+ "\x89\xB0\xC4\xBD\xC4\xB7\xD7\x29"
+ "\x98\x14\xA1\x42\x37\x80\xFD\x90"
+ "\x68\x12\x01\xA8\x91\x89\xB9\x83"
+ "\x5B\x11\x77\x12\x9B\xFF\x24\x89"
+ "\x94\x5F\x18\x12\xBA\x27\x09\x39"
+ "\x99\x96\x76\x42\x15\x1C\xCD\xCB"
+ "\xDC\xD3\xDA\x65\x73\xAF\x80\xCD"
+ "\xD2\xB6\xC2\x4A\x76\xC2\x92\x85"
+ "\xBD\xCF\x62\x98\x58\x14\xE5\xBD",
+ .rlen = 80,
+ }, {
+ .key = "\x00\x01\x02\x03\x04\x05\x06\x07"
+ "\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f"
+ "\x00\x00\x00\x00",
+ .klen = 20,
+ .iv = "\x00\x00\x45\x67\x89\xab\xcd\xef"
+ "\x00\x00\x00\x00",
+ .input = "\xff\xff\xff\xff\xff\xff\xff\xff"
+ "\xff\xff\xff\xff\xff\xff\xff\xff"
+ "\xff\xff\xff\xff\xff\xff\xff\xff"
+ "\xff\xff\xff\xff\xff\xff\xff\xff"
+ "\xff\xff\xff\xff\xff\xff\xff\xff"
+ "\xff\xff\xff\xff\xff\xff\xff\xff"
+ "\xff\xff\xff\xff\xff\xff\xff\xff"
+ "\xff\xff\xff\xff\xff\xff\xff\xff"
+ "\xff\xff\xff\xff\xff\xff\xff\xff"
+ "\xff\xff\xff\xff\xff\xff\xff\xff"
+ "\xff\xff\xff\xff\xff\xff\xff\xff"
+ "\xff\xff\xff\xff\xff\xff\xff\xff"
+ "\xff\xff\xff\xff\xff\xff\xff\xff"
+ "\xff\xff\xff\xff\xff\xff\xff\xff"
+ "\xff\xff\xff\xff\xff\xff\xff\xff"
+ "\xff\xff\xff\xff\xff\xff\xff\xff"
+ "\xff\xff\xff\xff\xff\xff\xff\xff"
+ "\xff\xff\xff\xff\xff\xff\xff\xff"
+ "\xff\xff\xff\xff\xff\xff\xff\xff"
+ "\xff\xff\xff\xff\xff\xff\xff\xff"
+ "\xff\xff\xff\xff\xff\xff\xff\xff"
+ "\xff\xff\xff\xff\xff\xff\xff\xff"
+ "\xff\xff\xff\xff\xff\xff\xff\xff"
+ "\xff\xff\xff\xff\xff\xff\xff\xff",
+ .ilen = 192,
+ .assoc = "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa"
+ "\xaa\xaa\xaa\xaa",
+ .alen = 12,
+ .result = "\xC1\x76\x33\x85\xE2\x9B\x5F\xDE"
+ "\xDE\x89\x3D\x42\xE7\xC9\x69\x8A"
+ "\x44\x6D\xC3\x88\x46\x2E\xC2\x01"
+ "\x5E\xF6\x0C\x39\xF0\xC4\xA5\x82"
+ "\xCD\xE8\x31\xCC\x0A\x4C\xE4\x44"
+ "\x41\xA9\x82\x6F\x22\xA1\x23\x1A"
+ "\xA8\xE3\x16\xFD\x31\x5C\x27\x31"
+ "\xF1\x7F\x01\x63\xA3\xAF\x70\xA1"
+ "\xCF\x07\x57\x41\x67\xD0\xC4\x42"
+ "\xDB\x18\xC6\x4C\x4C\xE0\x3D\x9F"
+ "\x05\x07\xFB\x13\x7D\x4A\xCA\x5B"
+ "\xF0\xBF\x64\x7E\x05\xB1\x72\xEE"
+ "\x7C\x3B\xD4\xCD\x14\x03\xB2\x2C"
+ "\xD3\xA9\xEE\xFA\x17\xFC\x9C\xDF"
+ "\xC7\x75\x40\xFF\xAE\xAD\x1E\x59"
+ "\x2F\x30\x24\xFB\xAD\x6B\x10\xFA"
+ "\x6C\x9F\x5B\xE7\x25\xD5\xD0\x25"
+ "\xAC\x4A\x4B\xDA\xFC\x7A\x85\x1B"
+ "\x7E\x13\x06\x82\x08\x17\xA4\x35"
+ "\xEC\xC5\x8D\x63\x96\x81\x0A\x8F"
+ "\xA3\x05\x38\x95\x20\x1A\x47\x04"
+ "\x6F\x6D\xDA\x8F\xEF\xC1\x76\x35"
+ "\x6B\xC7\x4D\x0F\x94\x12\xCA\x3E"
+ "\x2E\xD5\x03\x2E\x86\x7E\xAA\x3B"
+ "\x37\x08\x1C\xCF\xBA\x5D\x71\x46"
+ "\x80\x72\xB0\x4C\x82\x0D\x60\x3C",
+ .rlen = 208,
+ }
+};
+
+static struct aead_testvec aes_gcm_rfc4106_dec_tv_template[] = {
+ { /* Generated using Crypto++ */
+ .key = zeroed_string,
+ .klen = 20,
+ .iv = zeroed_string,
+ .input = "\x03\x88\xDA\xCE\x60\xB6\xA3\x92"
+ "\xF3\x28\xC2\xB9\x71\xB2\xFE\x78"
+ "\x97\xFE\x4C\x23\x37\x42\x01\xE0"
+ "\x81\x9F\x8D\xC5\xD7\x41\xA0\x1B",
+ .ilen = 32,
+ .assoc = zeroed_string,
+ .alen = 8,
+ .result = zeroed_string,
+ .rlen = 16,
+
+ },{
+ .key = "\xfe\xff\xe9\x92\x86\x65\x73\x1c"
+ "\x6d\x6a\x8f\x94\x67\x30\x83\x08"
+ "\x00\x00\x00\x00",
+ .klen = 20,
+ .iv = "\x00\x00\x00\x00\x00\x00\x00\x01"
+ "\x00\x00\x00\x00",
+ .input = "\xC0\x0D\x8B\x42\x0F\x8F\x34\x18"
+ "\x88\xB1\xC5\xBC\xC5\xB6\xD6\x28"
+ "\x6A\x9D\xDF\x11\x5E\xFE\x5E\x9D"
+ "\x2F\x70\x44\x92\xF7\xF2\xE3\xEF",
+ .ilen = 32,
+ .assoc = zeroed_string,
+ .alen = 8,
+ .result = zeroed_string,
+ .rlen = 16,
+ }, {
+ .key = "\xfe\xff\xe9\x92\x86\x65\x73\x1c"
+ "\x6d\x6a\x8f\x94\x67\x30\x83\x08"
+ "\x00\x00\x00\x00",
+ .klen = 20,
+ .iv = zeroed_string,
+ .input = "\x4B\xB1\xB5\xE3\x25\x71\x70\xDE"
+ "\x7F\xC9\x9C\xA5\x14\x19\xF2\xAC"
+ "\x0B\x8F\x88\x69\x17\xE6\xB4\x3C"
+ "\xB1\x68\xFD\x14\x52\x64\x61\xB2",
+ .ilen = 32,
+ .assoc = zeroed_string,
+ .alen = 8,
+ .result = "\x01\x01\x01\x01\x01\x01\x01\x01"
+ "\x01\x01\x01\x01\x01\x01\x01\x01",
+ .rlen = 16,
+ }, {
+ .key = "\xfe\xff\xe9\x92\x86\x65\x73\x1c"
+ "\x6d\x6a\x8f\x94\x67\x30\x83\x08"
+ "\x00\x00\x00\x00",
+ .klen = 20,
+ .iv = zeroed_string,
+ .input = "\x4B\xB1\xB5\xE3\x25\x71\x70\xDE"
+ "\x7F\xC9\x9C\xA5\x14\x19\xF2\xAC"
+ "\x90\x92\xB7\xE3\x5F\xA3\x9A\x63"
+ "\x7E\xD7\x1F\xD8\xD3\x7C\x4B\xF5",
+ .ilen = 32,
+ .assoc = "\x01\x01\x01\x01\x01\x01\x01\x01",
+ .alen = 8,
+ .result = "\x01\x01\x01\x01\x01\x01\x01\x01"
+ "\x01\x01\x01\x01\x01\x01\x01\x01",
+ .rlen = 16,
+
+ }, {
+ .key = "\xfe\xff\xe9\x92\x86\x65\x73\x1c"
+ "\x6d\x6a\x8f\x94\x67\x30\x83\x08"
+ "\x00\x00\x00\x00",
+ .klen = 20,
+ .iv = "\x00\x00\x00\x00\x00\x00\x00\x01"
+ "\x00\x00\x00\x00",
+ .input = "\xC1\x0C\x8A\x43\x0E\x8E\x35\x19"
+ "\x89\xB0\xC4\xBD\xC4\xB7\xD7\x29"
+ "\x64\x50\xF9\x32\x13\xFB\x74\x61"
+ "\xF4\xED\x52\xD3\xC5\x10\x55\x3C",
+ .ilen = 32,
+ .assoc = "\x01\x01\x01\x01\x01\x01\x01\x01",
+ .alen = 8,
+ .result = "\x01\x01\x01\x01\x01\x01\x01\x01"
+ "\x01\x01\x01\x01\x01\x01\x01\x01",
+ .rlen = 16,
+ }, {
+ .key = "\xfe\xff\xe9\x92\x86\x65\x73\x1c"
+ "\x6d\x6a\x8f\x94\x67\x30\x83\x08"
+ "\x00\x00\x00\x00",
+ .klen = 20,
+ .iv = "\x00\x00\x00\x00\x00\x00\x00\x01"
+ "\x00\x00\x00\x00",
+ .input = "\xC1\x0C\x8A\x43\x0E\x8E\x35\x19"
+ "\x89\xB0\xC4\xBD\xC4\xB7\xD7\x29"
+ "\x98\x14\xA1\x42\x37\x80\xFD\x90"
+ "\x68\x12\x01\xA8\x91\x89\xB9\x83"
+ "\x5B\x11\x77\x12\x9B\xFF\x24\x89"
+ "\x94\x5F\x18\x12\xBA\x27\x09\x39"
+ "\x99\x96\x76\x42\x15\x1C\xCD\xCB"
+ "\xDC\xD3\xDA\x65\x73\xAF\x80\xCD"
+ "\xD2\xB6\xC2\x4A\x76\xC2\x92\x85"
+ "\xBD\xCF\x62\x98\x58\x14\xE5\xBD",
+ .ilen = 80,
+ .assoc = "\x01\x01\x01\x01\x01\x01\x01\x01",
+ .alen = 8,
+ .result = "\x01\x01\x01\x01\x01\x01\x01\x01"
+ "\x01\x01\x01\x01\x01\x01\x01\x01"
+ "\x01\x01\x01\x01\x01\x01\x01\x01"
+ "\x01\x01\x01\x01\x01\x01\x01\x01"
+ "\x01\x01\x01\x01\x01\x01\x01\x01"
+ "\x01\x01\x01\x01\x01\x01\x01\x01"
+ "\x01\x01\x01\x01\x01\x01\x01\x01"
+ "\x01\x01\x01\x01\x01\x01\x01\x01",
+ .rlen = 64,
+ }, {
+ .key = "\x00\x01\x02\x03\x04\x05\x06\x07"
+ "\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f"
+ "\x00\x00\x00\x00",
+ .klen = 20,
+ .iv = "\x00\x00\x45\x67\x89\xab\xcd\xef"
+ "\x00\x00\x00\x00",
+ .input = "\xC1\x76\x33\x85\xE2\x9B\x5F\xDE"
+ "\xDE\x89\x3D\x42\xE7\xC9\x69\x8A"
+ "\x44\x6D\xC3\x88\x46\x2E\xC2\x01"
+ "\x5E\xF6\x0C\x39\xF0\xC4\xA5\x82"
+ "\xCD\xE8\x31\xCC\x0A\x4C\xE4\x44"
+ "\x41\xA9\x82\x6F\x22\xA1\x23\x1A"
+ "\xA8\xE3\x16\xFD\x31\x5C\x27\x31"
+ "\xF1\x7F\x01\x63\xA3\xAF\x70\xA1"
+ "\xCF\x07\x57\x41\x67\xD0\xC4\x42"
+ "\xDB\x18\xC6\x4C\x4C\xE0\x3D\x9F"
+ "\x05\x07\xFB\x13\x7D\x4A\xCA\x5B"
+ "\xF0\xBF\x64\x7E\x05\xB1\x72\xEE"
+ "\x7C\x3B\xD4\xCD\x14\x03\xB2\x2C"
+ "\xD3\xA9\xEE\xFA\x17\xFC\x9C\xDF"
+ "\xC7\x75\x40\xFF\xAE\xAD\x1E\x59"
+ "\x2F\x30\x24\xFB\xAD\x6B\x10\xFA"
+ "\x6C\x9F\x5B\xE7\x25\xD5\xD0\x25"
+ "\xAC\x4A\x4B\xDA\xFC\x7A\x85\x1B"
+ "\x7E\x13\x06\x82\x08\x17\xA4\x35"
+ "\xEC\xC5\x8D\x63\x96\x81\x0A\x8F"
+ "\xA3\x05\x38\x95\x20\x1A\x47\x04"
+ "\x6F\x6D\xDA\x8F\xEF\xC1\x76\x35"
+ "\x6B\xC7\x4D\x0F\x94\x12\xCA\x3E"
+ "\x2E\xD5\x03\x2E\x86\x7E\xAA\x3B"
+ "\x37\x08\x1C\xCF\xBA\x5D\x71\x46"
+ "\x80\x72\xB0\x4C\x82\x0D\x60\x3C",
+ .ilen = 208,
+ .assoc = "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa"
+ "\xaa\xaa\xaa\xaa",
+ .alen = 12,
+ .result = "\xff\xff\xff\xff\xff\xff\xff\xff"
+ "\xff\xff\xff\xff\xff\xff\xff\xff"
+ "\xff\xff\xff\xff\xff\xff\xff\xff"
+ "\xff\xff\xff\xff\xff\xff\xff\xff"
+ "\xff\xff\xff\xff\xff\xff\xff\xff"
+ "\xff\xff\xff\xff\xff\xff\xff\xff"
+ "\xff\xff\xff\xff\xff\xff\xff\xff"
+ "\xff\xff\xff\xff\xff\xff\xff\xff"
+ "\xff\xff\xff\xff\xff\xff\xff\xff"
+ "\xff\xff\xff\xff\xff\xff\xff\xff"
+ "\xff\xff\xff\xff\xff\xff\xff\xff"
+ "\xff\xff\xff\xff\xff\xff\xff\xff"
+ "\xff\xff\xff\xff\xff\xff\xff\xff"
+ "\xff\xff\xff\xff\xff\xff\xff\xff"
+ "\xff\xff\xff\xff\xff\xff\xff\xff"
+ "\xff\xff\xff\xff\xff\xff\xff\xff"
+ "\xff\xff\xff\xff\xff\xff\xff\xff"
+ "\xff\xff\xff\xff\xff\xff\xff\xff"
+ "\xff\xff\xff\xff\xff\xff\xff\xff"
+ "\xff\xff\xff\xff\xff\xff\xff\xff"
+ "\xff\xff\xff\xff\xff\xff\xff\xff"
+ "\xff\xff\xff\xff\xff\xff\xff\xff"
+ "\xff\xff\xff\xff\xff\xff\xff\xff"
+ "\xff\xff\xff\xff\xff\xff\xff\xff",
+ .rlen = 192,
+
+ }
+};
+
static struct aead_testvec aes_ccm_enc_tv_template[] = {
{ /* From RFC 3610 */
.key = "\xc0\xc1\xc2\xc3\xc4\xc5\xc6\xc7"
diff --git a/crypto/zlib.c b/crypto/zlib.c
index c3015733..739b8fc 100644
--- a/crypto/zlib.c
+++ b/crypto/zlib.c
@@ -95,11 +95,10 @@
zlib_comp_exit(ctx);
workspacesize = zlib_deflate_workspacesize();
- stream->workspace = vmalloc(workspacesize);
+ stream->workspace = vzalloc(workspacesize);
if (!stream->workspace)
return -ENOMEM;
- memset(stream->workspace, 0, workspacesize);
ret = zlib_deflateInit2(stream,
tb[ZLIB_COMP_LEVEL]
? nla_get_u32(tb[ZLIB_COMP_LEVEL])
diff --git a/drivers/Kconfig b/drivers/Kconfig
index 3d93b3a..dd0a5b5 100644
--- a/drivers/Kconfig
+++ b/drivers/Kconfig
@@ -88,6 +88,8 @@
source "drivers/leds/Kconfig"
+source "drivers/nfc/Kconfig"
+
source "drivers/accessibility/Kconfig"
source "drivers/infiniband/Kconfig"
diff --git a/drivers/Makefile b/drivers/Makefile
index bf15ce7..ef51324 100644
--- a/drivers/Makefile
+++ b/drivers/Makefile
@@ -40,7 +40,7 @@
obj-y += serial/
obj-$(CONFIG_PARPORT) += parport/
-obj-y += base/ block/ misc/ mfd/
+obj-y += base/ block/ misc/ mfd/ nfc/
obj-$(CONFIG_NUBUS) += nubus/
obj-y += macintosh/
obj-$(CONFIG_IDE) += ide/
diff --git a/drivers/acpi/acpica/acobject.h b/drivers/acpi/acpica/acobject.h
index bdbfaf22b..962a3cc 100644
--- a/drivers/acpi/acpica/acobject.h
+++ b/drivers/acpi/acpica/acobject.h
@@ -93,7 +93,7 @@
#define AOPOBJ_AML_CONSTANT 0x01 /* Integer is an AML constant */
#define AOPOBJ_STATIC_POINTER 0x02 /* Data is part of an ACPI table, don't delete */
-#define AOPOBJ_DATA_VALID 0x04 /* Object is intialized and data is valid */
+#define AOPOBJ_DATA_VALID 0x04 /* Object is initialized and data is valid */
#define AOPOBJ_OBJECT_INITIALIZED 0x08 /* Region is initialized, _REG was run */
#define AOPOBJ_SETUP_COMPLETE 0x10 /* Region setup is complete */
#define AOPOBJ_INVALID 0x20 /* Host OS won't allow a Region address */
diff --git a/drivers/acpi/apei/einj.c b/drivers/acpi/apei/einj.c
index cf29df6..096aebf 100644
--- a/drivers/acpi/apei/einj.c
+++ b/drivers/acpi/apei/einj.c
@@ -39,7 +39,7 @@
#define EINJ_PFX "EINJ: "
#define SPIN_UNIT 100 /* 100ns */
-/* Firmware should respond within 1 miliseconds */
+/* Firmware should respond within 1 milliseconds */
#define FIRMWARE_TIMEOUT (1 * NSEC_PER_MSEC)
/*
diff --git a/drivers/acpi/apei/erst.c b/drivers/acpi/apei/erst.c
index 5850d32..cf6db6b7 100644
--- a/drivers/acpi/apei/erst.c
+++ b/drivers/acpi/apei/erst.c
@@ -53,7 +53,7 @@
sizeof(struct acpi_table_erst)))
#define SPIN_UNIT 100 /* 100ns */
-/* Firmware should respond within 1 miliseconds */
+/* Firmware should respond within 1 milliseconds */
#define FIRMWARE_TIMEOUT (1 * NSEC_PER_MSEC)
#define FIRMWARE_MAX_STALL 50 /* 50us */
diff --git a/drivers/acpi/sleep.c b/drivers/acpi/sleep.c
index febb153..c423231 100644
--- a/drivers/acpi/sleep.c
+++ b/drivers/acpi/sleep.c
@@ -319,7 +319,7 @@
}
}
-static struct platform_suspend_ops acpi_suspend_ops = {
+static const struct platform_suspend_ops acpi_suspend_ops = {
.valid = acpi_suspend_state_valid,
.begin = acpi_suspend_begin,
.prepare_late = acpi_pm_prepare,
@@ -347,7 +347,7 @@
* The following callbacks are used if the pre-ACPI 2.0 suspend ordering has
* been requested.
*/
-static struct platform_suspend_ops acpi_suspend_ops_old = {
+static const struct platform_suspend_ops acpi_suspend_ops_old = {
.valid = acpi_suspend_state_valid,
.begin = acpi_suspend_begin_old,
.prepare_late = acpi_pm_pre_suspend,
@@ -506,7 +506,7 @@
acpi_enable_all_runtime_gpes();
}
-static struct platform_hibernation_ops acpi_hibernation_ops = {
+static const struct platform_hibernation_ops acpi_hibernation_ops = {
.begin = acpi_hibernation_begin,
.end = acpi_pm_end,
.pre_snapshot = acpi_pm_prepare,
@@ -549,7 +549,7 @@
* The following callbacks are used if the pre-ACPI 2.0 suspend ordering has
* been requested.
*/
-static struct platform_hibernation_ops acpi_hibernation_ops_old = {
+static const struct platform_hibernation_ops acpi_hibernation_ops_old = {
.begin = acpi_hibernation_begin_old,
.end = acpi_pm_end,
.pre_snapshot = acpi_pm_pre_suspend,
diff --git a/drivers/acpi/video.c b/drivers/acpi/video.c
index 5cd0228..15a0fde 100644
--- a/drivers/acpi/video.c
+++ b/drivers/acpi/video.c
@@ -260,7 +260,7 @@
vd->brightness->levels[request_level]);
}
-static struct backlight_ops acpi_backlight_ops = {
+static const struct backlight_ops acpi_backlight_ops = {
.get_brightness = acpi_video_get_brightness,
.update_status = acpi_video_set_brightness,
};
diff --git a/drivers/ata/libata-core.c b/drivers/ata/libata-core.c
index 0a6a943..a31fe96 100644
--- a/drivers/ata/libata-core.c
+++ b/drivers/ata/libata-core.c
@@ -2240,7 +2240,7 @@
if (id[ATA_ID_CFA_KEY_MGMT] & 1)
ata_dev_printk(dev, KERN_WARNING,
"supports DRM functions and may "
- "not be fully accessable.\n");
+ "not be fully accessible.\n");
snprintf(revbuf, 7, "CFA");
} else {
snprintf(revbuf, 7, "ATA-%d", ata_id_major_version(id));
@@ -2248,7 +2248,7 @@
if (ata_id_has_tpm(id))
ata_dev_printk(dev, KERN_WARNING,
"supports DRM functions and may "
- "not be fully accessable.\n");
+ "not be fully accessible.\n");
}
dev->n_sectors = ata_id_n_sectors(id);
diff --git a/drivers/ata/sata_vsc.c b/drivers/ata/sata_vsc.c
index b777176..e079cf2 100644
--- a/drivers/ata/sata_vsc.c
+++ b/drivers/ata/sata_vsc.c
@@ -370,7 +370,7 @@
if (pci_resource_len(pdev, 0) == 0)
return -ENODEV;
- /* map IO regions and intialize host accordingly */
+ /* map IO regions and initialize host accordingly */
rc = pcim_iomap_regions(pdev, 1 << VSC_MMIO_BAR, DRV_NAME);
if (rc == -EBUSY)
pcim_pin_device(pdev);
diff --git a/drivers/atm/idt77252.h b/drivers/atm/idt77252.h
index 5042bb2..f53a43a 100644
--- a/drivers/atm/idt77252.h
+++ b/drivers/atm/idt77252.h
@@ -572,7 +572,7 @@
#define SAR_STAT_TSQF 0x00001000 /* Transmit Status Queue full */
#define SAR_STAT_TMROF 0x00000800 /* Timer overflow */
#define SAR_STAT_PHYI 0x00000400 /* PHY device Interrupt flag */
-#define SAR_STAT_CMDBZ 0x00000200 /* ABR SAR Comand Busy Flag */
+#define SAR_STAT_CMDBZ 0x00000200 /* ABR SAR Command Busy Flag */
#define SAR_STAT_FBQ3A 0x00000100 /* Free Buffer Queue 3 Attention */
#define SAR_STAT_FBQ2A 0x00000080 /* Free Buffer Queue 2 Attention */
#define SAR_STAT_RSQF 0x00000040 /* Receive Status Queue full */
diff --git a/drivers/atm/iphase.c b/drivers/atm/iphase.c
index 7292540..d80d51b 100644
--- a/drivers/atm/iphase.c
+++ b/drivers/atm/iphase.c
@@ -2063,7 +2063,7 @@
- UBR Table size is 4K
- UBR wait queue is 4K
since the table and wait queues are contiguous, all the bytes
- can be initialized by one memeset.
+ can be initialized by one memeset.
*/
vcsize_sel = 0;
@@ -2089,7 +2089,7 @@
- ABR Table size is 2K
- ABR wait queue is 2K
since the table and wait queues are contiguous, all the bytes
- can be intialized by one memeset.
+ can be initialized by one memeset.
*/
i = ABR_SCHED_TABLE * iadev->memSize;
writew((i >> 11) & 0xffff, iadev->seg_reg+ABR_SBPTR_BASE);
diff --git a/drivers/base/bus.c b/drivers/base/bus.c
index e243bd4..000e7b2 100644
--- a/drivers/base/bus.c
+++ b/drivers/base/bus.c
@@ -975,7 +975,7 @@
EXPORT_SYMBOL_GPL(bus_get_device_klist);
/*
- * Yes, this forcably breaks the klist abstraction temporarily. It
+ * Yes, this forcibly breaks the klist abstraction temporarily. It
* just wants to sort the klist, not change reference counts and
* take/drop locks rapidly in the process. It does all this while
* holding the lock for the list, so objects can't otherwise be
diff --git a/drivers/base/power/main.c b/drivers/base/power/main.c
index 2a52270..8340497 100644
--- a/drivers/base/power/main.c
+++ b/drivers/base/power/main.c
@@ -8,7 +8,7 @@
*
*
* The driver model core calls device_pm_add() when a device is registered.
- * This will intialize the embedded device_pm_info object in the device
+ * This will initialize the embedded device_pm_info object in the device
* and add it to the list of power-controlled devices. sysfs entries for
* controlling device power management will also be added.
*
diff --git a/drivers/block/Kconfig b/drivers/block/Kconfig
index 4b9359a..83c32cb 100644
--- a/drivers/block/Kconfig
+++ b/drivers/block/Kconfig
@@ -464,6 +464,7 @@
tristate "Xen virtual block device support"
depends on XEN
default y
+ select XEN_XENBUS_FRONTEND
help
This driver implements the front-end of the Xen virtual
block device driver. It communicates with a back-end driver
diff --git a/drivers/block/rbd.c b/drivers/block/rbd.c
index 008d4a0..e1e38b1 100644
--- a/drivers/block/rbd.c
+++ b/drivers/block/rbd.c
@@ -1790,18 +1790,29 @@
rc = rbd_bus_add_dev(rbd_dev);
if (rc)
- goto err_out_disk;
+ goto err_out_blkdev;
+
/* set up and announce blkdev mapping */
rc = rbd_init_disk(rbd_dev);
if (rc)
- goto err_out_blkdev;
+ goto err_out_bus;
return count;
+err_out_bus:
+ mutex_lock_nested(&ctl_mutex, SINGLE_DEPTH_NESTING);
+ list_del_init(&rbd_dev->node);
+ mutex_unlock(&ctl_mutex);
+
+ /* this will also clean up rest of rbd_dev stuff */
+
+ rbd_bus_del_dev(rbd_dev);
+ kfree(options);
+ kfree(mon_dev_name);
+ return rc;
+
err_out_blkdev:
unregister_blkdev(rbd_dev->major, rbd_dev->name);
-err_out_disk:
- rbd_free_disk(rbd_dev);
err_out_client:
rbd_put_client(rbd_dev);
mutex_lock_nested(&ctl_mutex, SINGLE_DEPTH_NESTING);
diff --git a/drivers/char/Kconfig b/drivers/char/Kconfig
index d4a7776..0f175a8 100644
--- a/drivers/char/Kconfig
+++ b/drivers/char/Kconfig
@@ -1047,15 +1047,6 @@
pc8736x_gpio drivers. If those drivers are built as
modules, this one will be too, named nsc_gpio
-config CS5535_GPIO
- tristate "AMD CS5535/CS5536 GPIO (Geode Companion Device)"
- depends on X86_32
- help
- Give userspace access to the GPIO pins on the AMD CS5535 and
- CS5536 Geode companion devices.
-
- If compiled as a module, it will be called cs5535_gpio.
-
config RAW_DRIVER
tristate "RAW driver (/dev/raw/rawN)"
depends on BLOCK
diff --git a/drivers/char/Makefile b/drivers/char/Makefile
index fa0b824..1e9dffb 100644
--- a/drivers/char/Makefile
+++ b/drivers/char/Makefile
@@ -82,7 +82,6 @@
obj-$(CONFIG_SCx200_GPIO) += scx200_gpio.o
obj-$(CONFIG_PC8736x_GPIO) += pc8736x_gpio.o
obj-$(CONFIG_NSC_GPIO) += nsc_gpio.o
-obj-$(CONFIG_CS5535_GPIO) += cs5535_gpio.o
obj-$(CONFIG_GPIO_TB0219) += tb0219.o
obj-$(CONFIG_TELCLOCK) += tlclk.o
diff --git a/drivers/char/agp/intel-agp.h b/drivers/char/agp/intel-agp.h
index 010e3de..c195bfe 100644
--- a/drivers/char/agp/intel-agp.h
+++ b/drivers/char/agp/intel-agp.h
@@ -94,6 +94,8 @@
#define G4x_GMCH_SIZE_VT_1_5M (0xa << 8)
#define G4x_GMCH_SIZE_VT_2M (0xc << 8)
+#define GFX_FLSH_CNTL 0x2170 /* 915+ */
+
#define I810_DRAM_CTL 0x3000
#define I810_DRAM_ROW_0 0x00000001
#define I810_DRAM_ROW_0_SDRAM 0x00000001
diff --git a/drivers/char/agp/intel-gtt.c b/drivers/char/agp/intel-gtt.c
index 356f73e..e921b69 100644
--- a/drivers/char/agp/intel-gtt.c
+++ b/drivers/char/agp/intel-gtt.c
@@ -688,14 +688,14 @@
intel_private.base.stolen_size = intel_gtt_stolen_size();
+ intel_private.base.needs_dmar = USE_PCI_DMA_API && INTEL_GTT_GEN > 2;
+
ret = intel_gtt_setup_scratch_page();
if (ret != 0) {
intel_gtt_cleanup();
return ret;
}
- intel_private.base.needs_dmar = USE_PCI_DMA_API && INTEL_GTT_GEN > 2;
-
return 0;
}
@@ -814,6 +814,12 @@
}
}
+ /* On the resume path we may be adjusting the PGTBL value, so
+ * be paranoid and flush all chipset write buffers...
+ */
+ if (INTEL_GTT_GEN >= 3)
+ writel(0, intel_private.registers+GFX_FLSH_CNTL);
+
reg = intel_private.registers+I810_PGETBL_CTL;
writel(intel_private.PGETBL_save, reg);
if (HAS_PGTBL_EN && (readl(reg) & I810_PGETBL_ENABLED) == 0) {
@@ -823,6 +829,9 @@
return false;
}
+ if (INTEL_GTT_GEN >= 3)
+ writel(0, intel_private.registers+GFX_FLSH_CNTL);
+
return true;
}
@@ -991,14 +1000,14 @@
if (mem->page_count == 0)
return 0;
+ intel_gtt_clear_range(pg_start, mem->page_count);
+
if (intel_private.base.needs_dmar) {
intel_gtt_unmap_memory(mem->sg_list, mem->num_sg);
mem->sg_list = NULL;
mem->num_sg = 0;
}
- intel_gtt_clear_range(pg_start, mem->page_count);
-
return 0;
}
diff --git a/drivers/char/hw_random/via-rng.c b/drivers/char/hw_random/via-rng.c
index 794aacb..d0387a8 100644
--- a/drivers/char/hw_random/via-rng.c
+++ b/drivers/char/hw_random/via-rng.c
@@ -24,6 +24,7 @@
* warranty of any kind, whether express or implied.
*/
+#include <crypto/padlock.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/hw_random.h>
@@ -34,7 +35,6 @@
#include <asm/i387.h>
-#define PFX KBUILD_MODNAME ": "
enum {
@@ -81,8 +81,7 @@
ts_state = irq_ts_save();
asm(".byte 0x0F,0xA7,0xC0 /* xstore %%edi (addr=%0) */"
- :"=m"(*addr), "=a"(eax_out)
- :"D"(addr), "d"(edx_in));
+ : "=m" (*addr), "=a" (eax_out), "+d" (edx_in), "+D" (addr));
irq_ts_restore(ts_state);
return eax_out;
@@ -90,8 +89,10 @@
static int via_rng_data_present(struct hwrng *rng, int wait)
{
+ char buf[16 + PADLOCK_ALIGNMENT - STACK_ALIGN] __attribute__
+ ((aligned(STACK_ALIGN)));
+ u32 *via_rng_datum = (u32 *)PTR_ALIGN(&buf[0], PADLOCK_ALIGNMENT);
u32 bytes_out;
- u32 *via_rng_datum = (u32 *)(&rng->priv);
int i;
/* We choose the recommended 1-byte-per-instruction RNG rate,
@@ -115,6 +116,7 @@
break;
udelay(10);
}
+ rng->priv = *via_rng_datum;
return bytes_out ? 1 : 0;
}
diff --git a/drivers/char/ramoops.c b/drivers/char/ramoops.c
index d3d63be..1a9f5f6 100644
--- a/drivers/char/ramoops.c
+++ b/drivers/char/ramoops.c
@@ -30,7 +30,7 @@
#define RAMOOPS_KERNMSG_HDR "===="
-#define RECORD_SIZE 4096
+#define RECORD_SIZE 4096UL
static ulong mem_address;
module_param(mem_address, ulong, 0400);
@@ -68,11 +68,16 @@
char *buf, *buf_orig;
struct timeval timestamp;
+ if (reason != KMSG_DUMP_OOPS &&
+ reason != KMSG_DUMP_PANIC &&
+ reason != KMSG_DUMP_KEXEC)
+ return;
+
/* Only dump oopses if dump_oops is set */
if (reason == KMSG_DUMP_OOPS && !dump_oops)
return;
- buf = (char *)(cxt->virt_addr + (cxt->count * RECORD_SIZE));
+ buf = cxt->virt_addr + (cxt->count * RECORD_SIZE);
buf_orig = buf;
memset(buf, '\0', RECORD_SIZE);
@@ -83,8 +88,8 @@
buf += res;
hdr_size = buf - buf_orig;
- l2_cpy = min(l2, (unsigned long)(RECORD_SIZE - hdr_size));
- l1_cpy = min(l1, (unsigned long)(RECORD_SIZE - hdr_size) - l2_cpy);
+ l2_cpy = min(l2, RECORD_SIZE - hdr_size);
+ l1_cpy = min(l1, RECORD_SIZE - hdr_size - l2_cpy);
s2_start = l2 - l2_cpy;
s1_start = l1 - l1_cpy;
diff --git a/drivers/crypto/mv_cesa.c b/drivers/crypto/mv_cesa.c
index 7d279e5..c99305a 100644
--- a/drivers/crypto/mv_cesa.c
+++ b/drivers/crypto/mv_cesa.c
@@ -857,7 +857,7 @@
printk(KERN_WARNING MV_CESA
"Base driver '%s' could not be loaded!\n",
base_hash_name);
- err = PTR_ERR(fallback_tfm);
+ err = PTR_ERR(base_hash);
goto err_bad_base;
}
}
diff --git a/drivers/crypto/n2_core.c b/drivers/crypto/n2_core.c
index 76141262..80dc094 100644
--- a/drivers/crypto/n2_core.c
+++ b/drivers/crypto/n2_core.c
@@ -1542,7 +1542,7 @@
return err;
}
-static void __exit n2_unregister_algs(void)
+static void __devexit n2_unregister_algs(void)
{
mutex_lock(&spu_lock);
if (!--algs_registered)
diff --git a/drivers/crypto/omap-aes.c b/drivers/crypto/omap-aes.c
index 799ca51..add2a1a 100644
--- a/drivers/crypto/omap-aes.c
+++ b/drivers/crypto/omap-aes.c
@@ -74,11 +74,9 @@
#define FLAGS_CBC BIT(1)
#define FLAGS_GIV BIT(2)
-#define FLAGS_NEW_KEY BIT(4)
-#define FLAGS_NEW_IV BIT(5)
-#define FLAGS_INIT BIT(6)
-#define FLAGS_FAST BIT(7)
-#define FLAGS_BUSY 8
+#define FLAGS_INIT BIT(4)
+#define FLAGS_FAST BIT(5)
+#define FLAGS_BUSY BIT(6)
struct omap_aes_ctx {
struct omap_aes_dev *dd;
@@ -98,19 +96,18 @@
struct omap_aes_dev {
struct list_head list;
unsigned long phys_base;
- void __iomem *io_base;
+ void __iomem *io_base;
struct clk *iclk;
struct omap_aes_ctx *ctx;
struct device *dev;
unsigned long flags;
+ int err;
- u32 *iv;
- u32 ctrl;
+ spinlock_t lock;
+ struct crypto_queue queue;
- spinlock_t lock;
- struct crypto_queue queue;
-
- struct tasklet_struct task;
+ struct tasklet_struct done_task;
+ struct tasklet_struct queue_task;
struct ablkcipher_request *req;
size_t total;
@@ -179,9 +176,13 @@
static int omap_aes_hw_init(struct omap_aes_dev *dd)
{
- int err = 0;
-
+ /*
+ * clocks are enabled when request starts and disabled when finished.
+ * It may be long delays between requests.
+ * Device might go to off mode to save power.
+ */
clk_enable(dd->iclk);
+
if (!(dd->flags & FLAGS_INIT)) {
/* is it necessary to reset before every operation? */
omap_aes_write_mask(dd, AES_REG_MASK, AES_REG_MASK_SOFTRESET,
@@ -193,39 +194,26 @@
__asm__ __volatile__("nop");
__asm__ __volatile__("nop");
- err = omap_aes_wait(dd, AES_REG_SYSSTATUS,
- AES_REG_SYSSTATUS_RESETDONE);
- if (!err)
- dd->flags |= FLAGS_INIT;
+ if (omap_aes_wait(dd, AES_REG_SYSSTATUS,
+ AES_REG_SYSSTATUS_RESETDONE))
+ return -ETIMEDOUT;
+
+ dd->flags |= FLAGS_INIT;
+ dd->err = 0;
}
- return err;
+ return 0;
}
-static void omap_aes_hw_cleanup(struct omap_aes_dev *dd)
-{
- clk_disable(dd->iclk);
-}
-
-static void omap_aes_write_ctrl(struct omap_aes_dev *dd)
+static int omap_aes_write_ctrl(struct omap_aes_dev *dd)
{
unsigned int key32;
- int i;
+ int i, err;
u32 val, mask;
- val = FLD_VAL(((dd->ctx->keylen >> 3) - 1), 4, 3);
- if (dd->flags & FLAGS_CBC)
- val |= AES_REG_CTRL_CBC;
- if (dd->flags & FLAGS_ENCRYPT)
- val |= AES_REG_CTRL_DIRECTION;
-
- if (dd->ctrl == val && !(dd->flags & FLAGS_NEW_IV) &&
- !(dd->ctx->flags & FLAGS_NEW_KEY))
- goto out;
-
- /* only need to write control registers for new settings */
-
- dd->ctrl = val;
+ err = omap_aes_hw_init(dd);
+ if (err)
+ return err;
val = 0;
if (dd->dma_lch_out >= 0)
@@ -237,30 +225,43 @@
omap_aes_write_mask(dd, AES_REG_MASK, val, mask);
- pr_debug("Set key\n");
key32 = dd->ctx->keylen / sizeof(u32);
- /* set a key */
+
+ /* it seems a key should always be set even if it has not changed */
for (i = 0; i < key32; i++) {
omap_aes_write(dd, AES_REG_KEY(i),
__le32_to_cpu(dd->ctx->key[i]));
}
- dd->ctx->flags &= ~FLAGS_NEW_KEY;
- if (dd->flags & FLAGS_NEW_IV) {
- pr_debug("Set IV\n");
- omap_aes_write_n(dd, AES_REG_IV(0), dd->iv, 4);
- dd->flags &= ~FLAGS_NEW_IV;
- }
+ if ((dd->flags & FLAGS_CBC) && dd->req->info)
+ omap_aes_write_n(dd, AES_REG_IV(0), dd->req->info, 4);
+
+ val = FLD_VAL(((dd->ctx->keylen >> 3) - 1), 4, 3);
+ if (dd->flags & FLAGS_CBC)
+ val |= AES_REG_CTRL_CBC;
+ if (dd->flags & FLAGS_ENCRYPT)
+ val |= AES_REG_CTRL_DIRECTION;
mask = AES_REG_CTRL_CBC | AES_REG_CTRL_DIRECTION |
AES_REG_CTRL_KEY_SIZE;
- omap_aes_write_mask(dd, AES_REG_CTRL, dd->ctrl, mask);
+ omap_aes_write_mask(dd, AES_REG_CTRL, val, mask);
-out:
- /* start DMA or disable idle mode */
- omap_aes_write_mask(dd, AES_REG_MASK, AES_REG_MASK_START,
- AES_REG_MASK_START);
+ /* IN */
+ omap_set_dma_dest_params(dd->dma_lch_in, 0, OMAP_DMA_AMODE_CONSTANT,
+ dd->phys_base + AES_REG_DATA, 0, 4);
+
+ omap_set_dma_dest_burst_mode(dd->dma_lch_in, OMAP_DMA_DATA_BURST_4);
+ omap_set_dma_src_burst_mode(dd->dma_lch_in, OMAP_DMA_DATA_BURST_4);
+
+ /* OUT */
+ omap_set_dma_src_params(dd->dma_lch_out, 0, OMAP_DMA_AMODE_CONSTANT,
+ dd->phys_base + AES_REG_DATA, 0, 4);
+
+ omap_set_dma_src_burst_mode(dd->dma_lch_out, OMAP_DMA_DATA_BURST_4);
+ omap_set_dma_dest_burst_mode(dd->dma_lch_out, OMAP_DMA_DATA_BURST_4);
+
+ return 0;
}
static struct omap_aes_dev *omap_aes_find_dev(struct omap_aes_ctx *ctx)
@@ -288,8 +289,16 @@
{
struct omap_aes_dev *dd = data;
- if (lch == dd->dma_lch_out)
- tasklet_schedule(&dd->task);
+ if (ch_status != OMAP_DMA_BLOCK_IRQ) {
+ pr_err("omap-aes DMA error status: 0x%hx\n", ch_status);
+ dd->err = -EIO;
+ dd->flags &= ~FLAGS_INIT; /* request to re-initialize */
+ } else if (lch == dd->dma_lch_in) {
+ return;
+ }
+
+ /* dma_lch_out - completed */
+ tasklet_schedule(&dd->done_task);
}
static int omap_aes_dma_init(struct omap_aes_dev *dd)
@@ -339,18 +348,6 @@
goto err_dma_out;
}
- omap_set_dma_dest_params(dd->dma_lch_in, 0, OMAP_DMA_AMODE_CONSTANT,
- dd->phys_base + AES_REG_DATA, 0, 4);
-
- omap_set_dma_dest_burst_mode(dd->dma_lch_in, OMAP_DMA_DATA_BURST_4);
- omap_set_dma_src_burst_mode(dd->dma_lch_in, OMAP_DMA_DATA_BURST_4);
-
- omap_set_dma_src_params(dd->dma_lch_out, 0, OMAP_DMA_AMODE_CONSTANT,
- dd->phys_base + AES_REG_DATA, 0, 4);
-
- omap_set_dma_src_burst_mode(dd->dma_lch_out, OMAP_DMA_DATA_BURST_4);
- omap_set_dma_dest_burst_mode(dd->dma_lch_out, OMAP_DMA_DATA_BURST_4);
-
return 0;
err_dma_out:
@@ -406,6 +403,11 @@
if (!count)
return off;
+ /*
+ * buflen and total are AES_BLOCK_SIZE size aligned,
+ * so count should be also aligned
+ */
+
sg_copy_buf(buf + off, *sg, *offset, count, out);
off += count;
@@ -461,7 +463,9 @@
omap_start_dma(dd->dma_lch_in);
omap_start_dma(dd->dma_lch_out);
- omap_aes_write_ctrl(dd);
+ /* start DMA or disable idle mode */
+ omap_aes_write_mask(dd, AES_REG_MASK, AES_REG_MASK_START,
+ AES_REG_MASK_START);
return 0;
}
@@ -488,8 +492,10 @@
count = min(dd->total, sg_dma_len(dd->in_sg));
count = min(count, sg_dma_len(dd->out_sg));
- if (count != dd->total)
+ if (count != dd->total) {
+ pr_err("request length != buffer length\n");
return -EINVAL;
+ }
pr_debug("fast\n");
@@ -525,23 +531,25 @@
dd->total -= count;
- err = omap_aes_hw_init(dd);
-
err = omap_aes_crypt_dma(tfm, addr_in, addr_out, count);
+ if (err) {
+ dma_unmap_sg(dd->dev, dd->in_sg, 1, DMA_TO_DEVICE);
+ dma_unmap_sg(dd->dev, dd->out_sg, 1, DMA_TO_DEVICE);
+ }
return err;
}
static void omap_aes_finish_req(struct omap_aes_dev *dd, int err)
{
- struct omap_aes_ctx *ctx;
+ struct ablkcipher_request *req = dd->req;
pr_debug("err: %d\n", err);
- ctx = crypto_ablkcipher_ctx(crypto_ablkcipher_reqtfm(dd->req));
+ clk_disable(dd->iclk);
+ dd->flags &= ~FLAGS_BUSY;
- if (!dd->total)
- dd->req->base.complete(&dd->req->base, err);
+ req->base.complete(&req->base, err);
}
static int omap_aes_crypt_dma_stop(struct omap_aes_dev *dd)
@@ -553,8 +561,6 @@
omap_aes_write_mask(dd, AES_REG_MASK, 0, AES_REG_MASK_START);
- omap_aes_hw_cleanup(dd);
-
omap_stop_dma(dd->dma_lch_in);
omap_stop_dma(dd->dma_lch_out);
@@ -574,40 +580,39 @@
}
}
- if (err || !dd->total)
- omap_aes_finish_req(dd, err);
-
return err;
}
-static int omap_aes_handle_req(struct omap_aes_dev *dd)
+static int omap_aes_handle_queue(struct omap_aes_dev *dd,
+ struct ablkcipher_request *req)
{
struct crypto_async_request *async_req, *backlog;
struct omap_aes_ctx *ctx;
struct omap_aes_reqctx *rctx;
- struct ablkcipher_request *req;
unsigned long flags;
-
- if (dd->total)
- goto start;
+ int err, ret = 0;
spin_lock_irqsave(&dd->lock, flags);
+ if (req)
+ ret = ablkcipher_enqueue_request(&dd->queue, req);
+ if (dd->flags & FLAGS_BUSY) {
+ spin_unlock_irqrestore(&dd->lock, flags);
+ return ret;
+ }
backlog = crypto_get_backlog(&dd->queue);
async_req = crypto_dequeue_request(&dd->queue);
- if (!async_req)
- clear_bit(FLAGS_BUSY, &dd->flags);
+ if (async_req)
+ dd->flags |= FLAGS_BUSY;
spin_unlock_irqrestore(&dd->lock, flags);
if (!async_req)
- return 0;
+ return ret;
if (backlog)
backlog->complete(backlog, -EINPROGRESS);
req = ablkcipher_request_cast(async_req);
- pr_debug("get new req\n");
-
/* assign new request to device */
dd->req = req;
dd->total = req->nbytes;
@@ -621,27 +626,22 @@
rctx->mode &= FLAGS_MODE_MASK;
dd->flags = (dd->flags & ~FLAGS_MODE_MASK) | rctx->mode;
- dd->iv = req->info;
- if ((dd->flags & FLAGS_CBC) && dd->iv)
- dd->flags |= FLAGS_NEW_IV;
- else
- dd->flags &= ~FLAGS_NEW_IV;
-
+ dd->ctx = ctx;
ctx->dd = dd;
- if (dd->ctx != ctx) {
- /* assign new context to device */
- dd->ctx = ctx;
- ctx->flags |= FLAGS_NEW_KEY;
+
+ err = omap_aes_write_ctrl(dd);
+ if (!err)
+ err = omap_aes_crypt_dma_start(dd);
+ if (err) {
+ /* aes_task will not finish it, so do it here */
+ omap_aes_finish_req(dd, err);
+ tasklet_schedule(&dd->queue_task);
}
- if (!IS_ALIGNED(req->nbytes, AES_BLOCK_SIZE))
- pr_err("request size is not exact amount of AES blocks\n");
-
-start:
- return omap_aes_crypt_dma_start(dd);
+ return ret; /* return ret, which is enqueue return value */
}
-static void omap_aes_task(unsigned long data)
+static void omap_aes_done_task(unsigned long data)
{
struct omap_aes_dev *dd = (struct omap_aes_dev *)data;
int err;
@@ -650,40 +650,50 @@
err = omap_aes_crypt_dma_stop(dd);
- err = omap_aes_handle_req(dd);
+ err = dd->err ? : err;
+
+ if (dd->total && !err) {
+ err = omap_aes_crypt_dma_start(dd);
+ if (!err)
+ return; /* DMA started. Not fininishing. */
+ }
+
+ omap_aes_finish_req(dd, err);
+ omap_aes_handle_queue(dd, NULL);
pr_debug("exit\n");
}
+static void omap_aes_queue_task(unsigned long data)
+{
+ struct omap_aes_dev *dd = (struct omap_aes_dev *)data;
+
+ omap_aes_handle_queue(dd, NULL);
+}
+
static int omap_aes_crypt(struct ablkcipher_request *req, unsigned long mode)
{
struct omap_aes_ctx *ctx = crypto_ablkcipher_ctx(
crypto_ablkcipher_reqtfm(req));
struct omap_aes_reqctx *rctx = ablkcipher_request_ctx(req);
struct omap_aes_dev *dd;
- unsigned long flags;
- int err;
pr_debug("nbytes: %d, enc: %d, cbc: %d\n", req->nbytes,
!!(mode & FLAGS_ENCRYPT),
!!(mode & FLAGS_CBC));
+ if (!IS_ALIGNED(req->nbytes, AES_BLOCK_SIZE)) {
+ pr_err("request size is not exact amount of AES blocks\n");
+ return -EINVAL;
+ }
+
dd = omap_aes_find_dev(ctx);
if (!dd)
return -ENODEV;
rctx->mode = mode;
- spin_lock_irqsave(&dd->lock, flags);
- err = ablkcipher_enqueue_request(&dd->queue, req);
- spin_unlock_irqrestore(&dd->lock, flags);
-
- if (!test_and_set_bit(FLAGS_BUSY, &dd->flags))
- omap_aes_handle_req(dd);
-
- pr_debug("exit\n");
-
- return err;
+ return omap_aes_handle_queue(dd, req);
}
/* ********************** ALG API ************************************ */
@@ -701,7 +711,6 @@
memcpy(ctx->key, key, keylen);
ctx->keylen = keylen;
- ctx->flags |= FLAGS_NEW_KEY;
return 0;
}
@@ -750,7 +759,7 @@
.cra_flags = CRYPTO_ALG_TYPE_ABLKCIPHER | CRYPTO_ALG_ASYNC,
.cra_blocksize = AES_BLOCK_SIZE,
.cra_ctxsize = sizeof(struct omap_aes_ctx),
- .cra_alignmask = 0,
+ .cra_alignmask = 0,
.cra_type = &crypto_ablkcipher_type,
.cra_module = THIS_MODULE,
.cra_init = omap_aes_cra_init,
@@ -770,7 +779,7 @@
.cra_flags = CRYPTO_ALG_TYPE_ABLKCIPHER | CRYPTO_ALG_ASYNC,
.cra_blocksize = AES_BLOCK_SIZE,
.cra_ctxsize = sizeof(struct omap_aes_ctx),
- .cra_alignmask = 0,
+ .cra_alignmask = 0,
.cra_type = &crypto_ablkcipher_type,
.cra_module = THIS_MODULE,
.cra_init = omap_aes_cra_init,
@@ -849,7 +858,8 @@
(reg & AES_REG_REV_MAJOR) >> 4, reg & AES_REG_REV_MINOR);
clk_disable(dd->iclk);
- tasklet_init(&dd->task, omap_aes_task, (unsigned long)dd);
+ tasklet_init(&dd->done_task, omap_aes_done_task, (unsigned long)dd);
+ tasklet_init(&dd->queue_task, omap_aes_queue_task, (unsigned long)dd);
err = omap_aes_dma_init(dd);
if (err)
@@ -876,7 +886,8 @@
crypto_unregister_alg(&algs[j]);
omap_aes_dma_cleanup(dd);
err_dma:
- tasklet_kill(&dd->task);
+ tasklet_kill(&dd->done_task);
+ tasklet_kill(&dd->queue_task);
iounmap(dd->io_base);
err_io:
clk_put(dd->iclk);
@@ -903,7 +914,8 @@
for (i = 0; i < ARRAY_SIZE(algs); i++)
crypto_unregister_alg(&algs[i]);
- tasklet_kill(&dd->task);
+ tasklet_kill(&dd->done_task);
+ tasklet_kill(&dd->queue_task);
omap_aes_dma_cleanup(dd);
iounmap(dd->io_base);
clk_put(dd->iclk);
diff --git a/drivers/crypto/omap-sham.c b/drivers/crypto/omap-sham.c
index a081c7c..2e71123 100644
--- a/drivers/crypto/omap-sham.c
+++ b/drivers/crypto/omap-sham.c
@@ -72,10 +72,9 @@
#define DEFAULT_TIMEOUT_INTERVAL HZ
-#define FLAGS_FIRST 0x0001
#define FLAGS_FINUP 0x0002
#define FLAGS_FINAL 0x0004
-#define FLAGS_FAST 0x0008
+#define FLAGS_SG 0x0008
#define FLAGS_SHA1 0x0010
#define FLAGS_DMA_ACTIVE 0x0020
#define FLAGS_OUTPUT_READY 0x0040
@@ -83,13 +82,17 @@
#define FLAGS_INIT 0x0100
#define FLAGS_CPU 0x0200
#define FLAGS_HMAC 0x0400
-
-/* 3rd byte */
-#define FLAGS_BUSY 16
+#define FLAGS_ERROR 0x0800
+#define FLAGS_BUSY 0x1000
#define OP_UPDATE 1
#define OP_FINAL 2
+#define OMAP_ALIGN_MASK (sizeof(u32)-1)
+#define OMAP_ALIGNED __attribute__((aligned(sizeof(u32))))
+
+#define BUFLEN PAGE_SIZE
+
struct omap_sham_dev;
struct omap_sham_reqctx {
@@ -97,8 +100,8 @@
unsigned long flags;
unsigned long op;
+ u8 digest[SHA1_DIGEST_SIZE] OMAP_ALIGNED;
size_t digcnt;
- u8 *buffer;
size_t bufcnt;
size_t buflen;
dma_addr_t dma_addr;
@@ -107,6 +110,8 @@
struct scatterlist *sg;
unsigned int offset; /* offset in current sg */
unsigned int total; /* total request */
+
+ u8 buffer[0] OMAP_ALIGNED;
};
struct omap_sham_hmac_ctx {
@@ -136,6 +141,7 @@
int irq;
struct clk *iclk;
spinlock_t lock;
+ int err;
int dma;
int dma_lch;
struct tasklet_struct done_task;
@@ -194,53 +200,68 @@
static void omap_sham_copy_hash(struct ahash_request *req, int out)
{
struct omap_sham_reqctx *ctx = ahash_request_ctx(req);
+ u32 *hash = (u32 *)ctx->digest;
+ int i;
+
+ /* MD5 is almost unused. So copy sha1 size to reduce code */
+ for (i = 0; i < SHA1_DIGEST_SIZE / sizeof(u32); i++) {
+ if (out)
+ hash[i] = omap_sham_read(ctx->dd,
+ SHA_REG_DIGEST(i));
+ else
+ omap_sham_write(ctx->dd,
+ SHA_REG_DIGEST(i), hash[i]);
+ }
+}
+
+static void omap_sham_copy_ready_hash(struct ahash_request *req)
+{
+ struct omap_sham_reqctx *ctx = ahash_request_ctx(req);
+ u32 *in = (u32 *)ctx->digest;
u32 *hash = (u32 *)req->result;
int i;
+ if (!hash)
+ return;
+
if (likely(ctx->flags & FLAGS_SHA1)) {
/* SHA1 results are in big endian */
for (i = 0; i < SHA1_DIGEST_SIZE / sizeof(u32); i++)
- if (out)
- hash[i] = be32_to_cpu(omap_sham_read(ctx->dd,
- SHA_REG_DIGEST(i)));
- else
- omap_sham_write(ctx->dd, SHA_REG_DIGEST(i),
- cpu_to_be32(hash[i]));
+ hash[i] = be32_to_cpu(in[i]);
} else {
/* MD5 results are in little endian */
for (i = 0; i < MD5_DIGEST_SIZE / sizeof(u32); i++)
- if (out)
- hash[i] = le32_to_cpu(omap_sham_read(ctx->dd,
- SHA_REG_DIGEST(i)));
- else
- omap_sham_write(ctx->dd, SHA_REG_DIGEST(i),
- cpu_to_le32(hash[i]));
+ hash[i] = le32_to_cpu(in[i]);
}
}
-static int omap_sham_write_ctrl(struct omap_sham_dev *dd, size_t length,
+static int omap_sham_hw_init(struct omap_sham_dev *dd)
+{
+ clk_enable(dd->iclk);
+
+ if (!(dd->flags & FLAGS_INIT)) {
+ omap_sham_write_mask(dd, SHA_REG_MASK,
+ SHA_REG_MASK_SOFTRESET, SHA_REG_MASK_SOFTRESET);
+
+ if (omap_sham_wait(dd, SHA_REG_SYSSTATUS,
+ SHA_REG_SYSSTATUS_RESETDONE))
+ return -ETIMEDOUT;
+
+ dd->flags |= FLAGS_INIT;
+ dd->err = 0;
+ }
+
+ return 0;
+}
+
+static void omap_sham_write_ctrl(struct omap_sham_dev *dd, size_t length,
int final, int dma)
{
struct omap_sham_reqctx *ctx = ahash_request_ctx(dd->req);
u32 val = length << 5, mask;
- if (unlikely(!ctx->digcnt)) {
-
- clk_enable(dd->iclk);
-
- if (!(dd->flags & FLAGS_INIT)) {
- omap_sham_write_mask(dd, SHA_REG_MASK,
- SHA_REG_MASK_SOFTRESET, SHA_REG_MASK_SOFTRESET);
-
- if (omap_sham_wait(dd, SHA_REG_SYSSTATUS,
- SHA_REG_SYSSTATUS_RESETDONE))
- return -ETIMEDOUT;
-
- dd->flags |= FLAGS_INIT;
- }
- } else {
+ if (likely(ctx->digcnt))
omap_sham_write(dd, SHA_REG_DIGCNT, ctx->digcnt);
- }
omap_sham_write_mask(dd, SHA_REG_MASK,
SHA_REG_MASK_IT_EN | (dma ? SHA_REG_MASK_DMA_EN : 0),
@@ -260,29 +281,26 @@
SHA_REG_CTRL_ALGO | SHA_REG_CTRL_LENGTH;
omap_sham_write_mask(dd, SHA_REG_CTRL, val, mask);
-
- return 0;
}
static int omap_sham_xmit_cpu(struct omap_sham_dev *dd, const u8 *buf,
size_t length, int final)
{
struct omap_sham_reqctx *ctx = ahash_request_ctx(dd->req);
- int err, count, len32;
+ int count, len32;
const u32 *buffer = (const u32 *)buf;
dev_dbg(dd->dev, "xmit_cpu: digcnt: %d, length: %d, final: %d\n",
ctx->digcnt, length, final);
- err = omap_sham_write_ctrl(dd, length, final, 0);
- if (err)
- return err;
+ omap_sham_write_ctrl(dd, length, final, 0);
+
+ /* should be non-zero before next lines to disable clocks later */
+ ctx->digcnt += length;
if (omap_sham_wait(dd, SHA_REG_CTRL, SHA_REG_CTRL_INPUT_READY))
return -ETIMEDOUT;
- ctx->digcnt += length;
-
if (final)
ctx->flags |= FLAGS_FINAL; /* catch last interrupt */
@@ -298,16 +316,11 @@
size_t length, int final)
{
struct omap_sham_reqctx *ctx = ahash_request_ctx(dd->req);
- int err, len32;
+ int len32;
dev_dbg(dd->dev, "xmit_dma: digcnt: %d, length: %d, final: %d\n",
ctx->digcnt, length, final);
- /* flush cache entries related to our page */
- if (dma_addr == ctx->dma_addr)
- dma_sync_single_for_device(dd->dev, dma_addr, length,
- DMA_TO_DEVICE);
-
len32 = DIV_ROUND_UP(length, sizeof(u32));
omap_set_dma_transfer_params(dd->dma_lch, OMAP_DMA_DATA_TYPE_S32, len32,
@@ -317,9 +330,7 @@
omap_set_dma_src_params(dd->dma_lch, 0, OMAP_DMA_AMODE_POST_INC,
dma_addr, 0, 0);
- err = omap_sham_write_ctrl(dd, length, final, 1);
- if (err)
- return err;
+ omap_sham_write_ctrl(dd, length, final, 1);
ctx->digcnt += length;
@@ -371,15 +382,29 @@
return 0;
}
+static int omap_sham_xmit_dma_map(struct omap_sham_dev *dd,
+ struct omap_sham_reqctx *ctx,
+ size_t length, int final)
+{
+ ctx->dma_addr = dma_map_single(dd->dev, ctx->buffer, ctx->buflen,
+ DMA_TO_DEVICE);
+ if (dma_mapping_error(dd->dev, ctx->dma_addr)) {
+ dev_err(dd->dev, "dma %u bytes error\n", ctx->buflen);
+ return -EINVAL;
+ }
+
+ ctx->flags &= ~FLAGS_SG;
+
+ /* next call does not fail... so no unmap in the case of error */
+ return omap_sham_xmit_dma(dd, ctx->dma_addr, length, final);
+}
+
static int omap_sham_update_dma_slow(struct omap_sham_dev *dd)
{
struct omap_sham_reqctx *ctx = ahash_request_ctx(dd->req);
unsigned int final;
size_t count;
- if (!ctx->total)
- return 0;
-
omap_sham_append_sg(ctx);
final = (ctx->flags & FLAGS_FINUP) && !ctx->total;
@@ -390,30 +415,68 @@
if (final || (ctx->bufcnt == ctx->buflen && ctx->total)) {
count = ctx->bufcnt;
ctx->bufcnt = 0;
- return omap_sham_xmit_dma(dd, ctx->dma_addr, count, final);
+ return omap_sham_xmit_dma_map(dd, ctx, count, final);
}
return 0;
}
-static int omap_sham_update_dma_fast(struct omap_sham_dev *dd)
+/* Start address alignment */
+#define SG_AA(sg) (IS_ALIGNED(sg->offset, sizeof(u32)))
+/* SHA1 block size alignment */
+#define SG_SA(sg) (IS_ALIGNED(sg->length, SHA1_MD5_BLOCK_SIZE))
+
+static int omap_sham_update_dma_start(struct omap_sham_dev *dd)
{
struct omap_sham_reqctx *ctx = ahash_request_ctx(dd->req);
- unsigned int length;
+ unsigned int length, final, tail;
+ struct scatterlist *sg;
- ctx->flags |= FLAGS_FAST;
+ if (!ctx->total)
+ return 0;
- length = min(ctx->total, sg_dma_len(ctx->sg));
- ctx->total = length;
+ if (ctx->bufcnt || ctx->offset)
+ return omap_sham_update_dma_slow(dd);
+
+ dev_dbg(dd->dev, "fast: digcnt: %d, bufcnt: %u, total: %u\n",
+ ctx->digcnt, ctx->bufcnt, ctx->total);
+
+ sg = ctx->sg;
+
+ if (!SG_AA(sg))
+ return omap_sham_update_dma_slow(dd);
+
+ if (!sg_is_last(sg) && !SG_SA(sg))
+ /* size is not SHA1_BLOCK_SIZE aligned */
+ return omap_sham_update_dma_slow(dd);
+
+ length = min(ctx->total, sg->length);
+
+ if (sg_is_last(sg)) {
+ if (!(ctx->flags & FLAGS_FINUP)) {
+ /* not last sg must be SHA1_MD5_BLOCK_SIZE aligned */
+ tail = length & (SHA1_MD5_BLOCK_SIZE - 1);
+ /* without finup() we need one block to close hash */
+ if (!tail)
+ tail = SHA1_MD5_BLOCK_SIZE;
+ length -= tail;
+ }
+ }
if (!dma_map_sg(dd->dev, ctx->sg, 1, DMA_TO_DEVICE)) {
dev_err(dd->dev, "dma_map_sg error\n");
return -EINVAL;
}
- ctx->total -= length;
+ ctx->flags |= FLAGS_SG;
- return omap_sham_xmit_dma(dd, sg_dma_address(ctx->sg), length, 1);
+ ctx->total -= length;
+ ctx->offset = length; /* offset where to start slow */
+
+ final = (ctx->flags & FLAGS_FINUP) && !ctx->total;
+
+ /* next call does not fail... so no unmap in the case of error */
+ return omap_sham_xmit_dma(dd, sg_dma_address(ctx->sg), length, final);
}
static int omap_sham_update_cpu(struct omap_sham_dev *dd)
@@ -433,8 +496,17 @@
struct omap_sham_reqctx *ctx = ahash_request_ctx(dd->req);
omap_stop_dma(dd->dma_lch);
- if (ctx->flags & FLAGS_FAST)
+ if (ctx->flags & FLAGS_SG) {
dma_unmap_sg(dd->dev, ctx->sg, 1, DMA_TO_DEVICE);
+ if (ctx->sg->length == ctx->offset) {
+ ctx->sg = sg_next(ctx->sg);
+ if (ctx->sg)
+ ctx->offset = 0;
+ }
+ } else {
+ dma_unmap_single(dd->dev, ctx->dma_addr, ctx->buflen,
+ DMA_TO_DEVICE);
+ }
return 0;
}
@@ -454,14 +526,7 @@
spin_unlock_irqrestore(&dd->lock, flags);
if (ctx->digcnt)
- clk_disable(dd->iclk);
-
- if (ctx->dma_addr)
- dma_unmap_single(dd->dev, ctx->dma_addr, ctx->buflen,
- DMA_TO_DEVICE);
-
- if (ctx->buffer)
- free_page((unsigned long)ctx->buffer);
+ omap_sham_copy_ready_hash(req);
dev_dbg(dd->dev, "digcnt: %d, bufcnt: %d\n", ctx->digcnt, ctx->bufcnt);
}
@@ -489,8 +554,6 @@
ctx->flags = 0;
- ctx->flags |= FLAGS_FIRST;
-
dev_dbg(dd->dev, "init: digest size: %d\n",
crypto_ahash_digestsize(tfm));
@@ -499,21 +562,7 @@
ctx->bufcnt = 0;
ctx->digcnt = 0;
-
- ctx->buflen = PAGE_SIZE;
- ctx->buffer = (void *)__get_free_page(
- (req->base.flags & CRYPTO_TFM_REQ_MAY_SLEEP) ?
- GFP_KERNEL : GFP_ATOMIC);
- if (!ctx->buffer)
- return -ENOMEM;
-
- ctx->dma_addr = dma_map_single(dd->dev, ctx->buffer, ctx->buflen,
- DMA_TO_DEVICE);
- if (dma_mapping_error(dd->dev, ctx->dma_addr)) {
- dev_err(dd->dev, "dma %u bytes error\n", ctx->buflen);
- free_page((unsigned long)ctx->buffer);
- return -EINVAL;
- }
+ ctx->buflen = BUFLEN;
if (tctx->flags & FLAGS_HMAC) {
struct omap_sham_hmac_ctx *bctx = tctx->base;
@@ -538,10 +587,8 @@
if (ctx->flags & FLAGS_CPU)
err = omap_sham_update_cpu(dd);
- else if (ctx->flags & FLAGS_FAST)
- err = omap_sham_update_dma_fast(dd);
else
- err = omap_sham_update_dma_slow(dd);
+ err = omap_sham_update_dma_start(dd);
/* wait for dma completion before can take more data */
dev_dbg(dd->dev, "update: err: %d, digcnt: %d\n", err, ctx->digcnt);
@@ -560,15 +607,12 @@
use_dma = 0;
if (use_dma)
- err = omap_sham_xmit_dma(dd, ctx->dma_addr, ctx->bufcnt, 1);
+ err = omap_sham_xmit_dma_map(dd, ctx, ctx->bufcnt, 1);
else
err = omap_sham_xmit_cpu(dd, ctx->buffer, ctx->bufcnt, 1);
ctx->bufcnt = 0;
- if (err != -EINPROGRESS)
- omap_sham_cleanup(req);
-
dev_dbg(dd->dev, "final_req: err: %d\n", err);
return err;
@@ -576,6 +620,7 @@
static int omap_sham_finish_req_hmac(struct ahash_request *req)
{
+ struct omap_sham_reqctx *ctx = ahash_request_ctx(req);
struct omap_sham_ctx *tctx = crypto_tfm_ctx(req->base.tfm);
struct omap_sham_hmac_ctx *bctx = tctx->base;
int bs = crypto_shash_blocksize(bctx->shash);
@@ -590,48 +635,56 @@
return crypto_shash_init(&desc.shash) ?:
crypto_shash_update(&desc.shash, bctx->opad, bs) ?:
- crypto_shash_finup(&desc.shash, req->result, ds, req->result);
+ crypto_shash_finup(&desc.shash, ctx->digest, ds, ctx->digest);
}
static void omap_sham_finish_req(struct ahash_request *req, int err)
{
struct omap_sham_reqctx *ctx = ahash_request_ctx(req);
+ struct omap_sham_dev *dd = ctx->dd;
if (!err) {
omap_sham_copy_hash(ctx->dd->req, 1);
if (ctx->flags & FLAGS_HMAC)
err = omap_sham_finish_req_hmac(req);
+ } else {
+ ctx->flags |= FLAGS_ERROR;
}
- if (ctx->flags & FLAGS_FINAL)
+ if ((ctx->flags & FLAGS_FINAL) || err)
omap_sham_cleanup(req);
- clear_bit(FLAGS_BUSY, &ctx->dd->flags);
+ clk_disable(dd->iclk);
+ dd->flags &= ~FLAGS_BUSY;
if (req->base.complete)
req->base.complete(&req->base, err);
}
-static int omap_sham_handle_queue(struct omap_sham_dev *dd)
+static int omap_sham_handle_queue(struct omap_sham_dev *dd,
+ struct ahash_request *req)
{
struct crypto_async_request *async_req, *backlog;
struct omap_sham_reqctx *ctx;
- struct ahash_request *req, *prev_req;
+ struct ahash_request *prev_req;
unsigned long flags;
- int err = 0;
-
- if (test_and_set_bit(FLAGS_BUSY, &dd->flags))
- return 0;
+ int err = 0, ret = 0;
spin_lock_irqsave(&dd->lock, flags);
+ if (req)
+ ret = ahash_enqueue_request(&dd->queue, req);
+ if (dd->flags & FLAGS_BUSY) {
+ spin_unlock_irqrestore(&dd->lock, flags);
+ return ret;
+ }
backlog = crypto_get_backlog(&dd->queue);
async_req = crypto_dequeue_request(&dd->queue);
- if (!async_req)
- clear_bit(FLAGS_BUSY, &dd->flags);
+ if (async_req)
+ dd->flags |= FLAGS_BUSY;
spin_unlock_irqrestore(&dd->lock, flags);
if (!async_req)
- return 0;
+ return ret;
if (backlog)
backlog->complete(backlog, -EINPROGRESS);
@@ -646,7 +699,22 @@
dev_dbg(dd->dev, "handling new req, op: %lu, nbytes: %d\n",
ctx->op, req->nbytes);
- if (req != prev_req && ctx->digcnt)
+
+ err = omap_sham_hw_init(dd);
+ if (err)
+ goto err1;
+
+ omap_set_dma_dest_params(dd->dma_lch, 0,
+ OMAP_DMA_AMODE_CONSTANT,
+ dd->phys_base + SHA_REG_DIN(0), 0, 16);
+
+ omap_set_dma_dest_burst_mode(dd->dma_lch,
+ OMAP_DMA_DATA_BURST_16);
+
+ omap_set_dma_src_burst_mode(dd->dma_lch,
+ OMAP_DMA_DATA_BURST_4);
+
+ if (ctx->digcnt)
/* request has changed - restore hash */
omap_sham_copy_hash(req, 0);
@@ -658,7 +726,7 @@
} else if (ctx->op == OP_FINAL) {
err = omap_sham_final_req(dd);
}
-
+err1:
if (err != -EINPROGRESS) {
/* done_task will not finish it, so do it here */
omap_sham_finish_req(req, err);
@@ -667,7 +735,7 @@
dev_dbg(dd->dev, "exit, err: %d\n", err);
- return err;
+ return ret;
}
static int omap_sham_enqueue(struct ahash_request *req, unsigned int op)
@@ -675,18 +743,10 @@
struct omap_sham_reqctx *ctx = ahash_request_ctx(req);
struct omap_sham_ctx *tctx = crypto_tfm_ctx(req->base.tfm);
struct omap_sham_dev *dd = tctx->dd;
- unsigned long flags;
- int err;
ctx->op = op;
- spin_lock_irqsave(&dd->lock, flags);
- err = ahash_enqueue_request(&dd->queue, req);
- spin_unlock_irqrestore(&dd->lock, flags);
-
- omap_sham_handle_queue(dd);
-
- return err;
+ return omap_sham_handle_queue(dd, req);
}
static int omap_sham_update(struct ahash_request *req)
@@ -709,21 +769,13 @@
*/
omap_sham_append_sg(ctx);
return 0;
- } else if (ctx->bufcnt + ctx->total <= 64) {
+ } else if (ctx->bufcnt + ctx->total <= SHA1_MD5_BLOCK_SIZE) {
+ /*
+ * faster to use CPU for short transfers
+ */
ctx->flags |= FLAGS_CPU;
- } else if (!ctx->bufcnt && sg_is_last(ctx->sg)) {
- /* may be can use faster functions */
- int aligned = IS_ALIGNED((u32)ctx->sg->offset,
- sizeof(u32));
-
- if (aligned && (ctx->flags & FLAGS_FIRST))
- /* digest: first and final */
- ctx->flags |= FLAGS_FAST;
-
- ctx->flags &= ~FLAGS_FIRST;
}
- } else if (ctx->bufcnt + ctx->total <= ctx->buflen) {
- /* if not finaup -> not fast */
+ } else if (ctx->bufcnt + ctx->total < ctx->buflen) {
omap_sham_append_sg(ctx);
return 0;
}
@@ -761,12 +813,14 @@
ctx->flags |= FLAGS_FINUP;
- /* OMAP HW accel works only with buffers >= 9 */
- /* HMAC is always >= 9 because of ipad */
- if ((ctx->digcnt + ctx->bufcnt) < 9)
- err = omap_sham_final_shash(req);
- else if (ctx->bufcnt)
- return omap_sham_enqueue(req, OP_FINAL);
+ if (!(ctx->flags & FLAGS_ERROR)) {
+ /* OMAP HW accel works only with buffers >= 9 */
+ /* HMAC is always >= 9 because of ipad */
+ if ((ctx->digcnt + ctx->bufcnt) < 9)
+ err = omap_sham_final_shash(req);
+ else if (ctx->bufcnt)
+ return omap_sham_enqueue(req, OP_FINAL);
+ }
omap_sham_cleanup(req);
@@ -836,6 +890,8 @@
struct omap_sham_ctx *tctx = crypto_tfm_ctx(tfm);
const char *alg_name = crypto_tfm_alg_name(tfm);
+ pr_info("enter\n");
+
/* Allocate a fallback and abort if it failed. */
tctx->fallback = crypto_alloc_shash(alg_name, 0,
CRYPTO_ALG_NEED_FALLBACK);
@@ -846,7 +902,7 @@
}
crypto_ahash_set_reqsize(__crypto_ahash_cast(tfm),
- sizeof(struct omap_sham_reqctx));
+ sizeof(struct omap_sham_reqctx) + BUFLEN);
if (alg_base) {
struct omap_sham_hmac_ctx *bctx = tctx->base;
@@ -932,7 +988,7 @@
CRYPTO_ALG_NEED_FALLBACK,
.cra_blocksize = SHA1_BLOCK_SIZE,
.cra_ctxsize = sizeof(struct omap_sham_ctx),
- .cra_alignmask = 0,
+ .cra_alignmask = OMAP_ALIGN_MASK,
.cra_module = THIS_MODULE,
.cra_init = omap_sham_cra_init,
.cra_exit = omap_sham_cra_exit,
@@ -956,7 +1012,7 @@
.cra_blocksize = SHA1_BLOCK_SIZE,
.cra_ctxsize = sizeof(struct omap_sham_ctx) +
sizeof(struct omap_sham_hmac_ctx),
- .cra_alignmask = 0,
+ .cra_alignmask = OMAP_ALIGN_MASK,
.cra_module = THIS_MODULE,
.cra_init = omap_sham_cra_sha1_init,
.cra_exit = omap_sham_cra_exit,
@@ -980,7 +1036,7 @@
.cra_blocksize = SHA1_BLOCK_SIZE,
.cra_ctxsize = sizeof(struct omap_sham_ctx) +
sizeof(struct omap_sham_hmac_ctx),
- .cra_alignmask = 0,
+ .cra_alignmask = OMAP_ALIGN_MASK,
.cra_module = THIS_MODULE,
.cra_init = omap_sham_cra_md5_init,
.cra_exit = omap_sham_cra_exit,
@@ -993,7 +1049,7 @@
struct omap_sham_dev *dd = (struct omap_sham_dev *)data;
struct ahash_request *req = dd->req;
struct omap_sham_reqctx *ctx = ahash_request_ctx(req);
- int ready = 1;
+ int ready = 0, err = 0;
if (ctx->flags & FLAGS_OUTPUT_READY) {
ctx->flags &= ~FLAGS_OUTPUT_READY;
@@ -1003,15 +1059,18 @@
if (dd->flags & FLAGS_DMA_ACTIVE) {
dd->flags &= ~FLAGS_DMA_ACTIVE;
omap_sham_update_dma_stop(dd);
- omap_sham_update_dma_slow(dd);
+ if (!dd->err)
+ err = omap_sham_update_dma_start(dd);
}
- if (ready && !(dd->flags & FLAGS_DMA_ACTIVE)) {
- dev_dbg(dd->dev, "update done\n");
+ err = dd->err ? : err;
+
+ if (err != -EINPROGRESS && (ready || err)) {
+ dev_dbg(dd->dev, "update done: err: %d\n", err);
/* finish curent request */
- omap_sham_finish_req(req, 0);
+ omap_sham_finish_req(req, err);
/* start new request */
- omap_sham_handle_queue(dd);
+ omap_sham_handle_queue(dd, NULL);
}
}
@@ -1019,7 +1078,7 @@
{
struct omap_sham_dev *dd = (struct omap_sham_dev *)data;
- omap_sham_handle_queue(dd);
+ omap_sham_handle_queue(dd, NULL);
}
static irqreturn_t omap_sham_irq(int irq, void *dev_id)
@@ -1041,6 +1100,7 @@
omap_sham_read(dd, SHA_REG_CTRL);
ctx->flags |= FLAGS_OUTPUT_READY;
+ dd->err = 0;
tasklet_schedule(&dd->done_task);
return IRQ_HANDLED;
@@ -1050,8 +1110,13 @@
{
struct omap_sham_dev *dd = data;
- if (likely(lch == dd->dma_lch))
- tasklet_schedule(&dd->done_task);
+ if (ch_status != OMAP_DMA_BLOCK_IRQ) {
+ pr_err("omap-sham DMA error status: 0x%hx\n", ch_status);
+ dd->err = -EIO;
+ dd->flags &= ~FLAGS_INIT; /* request to re-initialize */
+ }
+
+ tasklet_schedule(&dd->done_task);
}
static int omap_sham_dma_init(struct omap_sham_dev *dd)
@@ -1066,15 +1131,6 @@
dev_err(dd->dev, "Unable to request DMA channel\n");
return err;
}
- omap_set_dma_dest_params(dd->dma_lch, 0,
- OMAP_DMA_AMODE_CONSTANT,
- dd->phys_base + SHA_REG_DIN(0), 0, 16);
-
- omap_set_dma_dest_burst_mode(dd->dma_lch,
- OMAP_DMA_DATA_BURST_16);
-
- omap_set_dma_src_burst_mode(dd->dma_lch,
- OMAP_DMA_DATA_BURST_4);
return 0;
}
diff --git a/drivers/crypto/padlock-aes.c b/drivers/crypto/padlock-aes.c
index 8a515ba..db33d30 100644
--- a/drivers/crypto/padlock-aes.c
+++ b/drivers/crypto/padlock-aes.c
@@ -9,6 +9,7 @@
#include <crypto/algapi.h>
#include <crypto/aes.h>
+#include <crypto/padlock.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/types.h>
@@ -21,7 +22,6 @@
#include <asm/byteorder.h>
#include <asm/processor.h>
#include <asm/i387.h>
-#include "padlock.h"
/*
* Number of data blocks actually fetched for each xcrypt insn.
diff --git a/drivers/crypto/padlock-sha.c b/drivers/crypto/padlock-sha.c
index d3a27e0..adf075b 100644
--- a/drivers/crypto/padlock-sha.c
+++ b/drivers/crypto/padlock-sha.c
@@ -13,6 +13,7 @@
*/
#include <crypto/internal/hash.h>
+#include <crypto/padlock.h>
#include <crypto/sha.h>
#include <linux/err.h>
#include <linux/module.h>
@@ -22,13 +23,6 @@
#include <linux/kernel.h>
#include <linux/scatterlist.h>
#include <asm/i387.h>
-#include "padlock.h"
-
-#ifdef CONFIG_64BIT
-#define STACK_ALIGN 16
-#else
-#define STACK_ALIGN 4
-#endif
struct padlock_sha_desc {
struct shash_desc fallback;
diff --git a/drivers/dca/dca-core.c b/drivers/dca/dca-core.c
index b98c676..c461eda 100644
--- a/drivers/dca/dca-core.c
+++ b/drivers/dca/dca-core.c
@@ -110,8 +110,6 @@
/* at this point only one domain in the list is expected */
domain = list_first_entry(&dca_domains, struct dca_domain, node);
- if (!domain)
- return;
list_for_each_entry_safe(dca, _dca, &domain->dca_providers, node) {
list_del(&dca->node);
diff --git a/drivers/dma/intel_mid_dma.c b/drivers/dma/intel_mid_dma.c
index 3109bd9..7826638 100644
--- a/drivers/dma/intel_mid_dma.c
+++ b/drivers/dma/intel_mid_dma.c
@@ -1060,8 +1060,8 @@
* mid_setup_dma - Setup the DMA controller
* @pdev: Controller PCI device structure
*
- * Initilize the DMA controller, channels, registers with DMA engine,
- * ISR. Initilize DMA controller channels.
+ * Initialize the DMA controller, channels, registers with DMA engine,
+ * ISR. Initialize DMA controller channels.
*/
static int mid_setup_dma(struct pci_dev *pdev)
{
@@ -1217,7 +1217,7 @@
* @pdev: Controller PCI device structure
* @id: pci device id structure
*
- * Initilize the PCI device, map BARs, query driver data.
+ * Initialize the PCI device, map BARs, query driver data.
* Call setup_dma to complete contoller and chan initilzation
*/
static int __devinit intel_mid_dma_probe(struct pci_dev *pdev,
diff --git a/drivers/edac/amd8131_edac.h b/drivers/edac/amd8131_edac.h
index 60e0d1c..6f8b071 100644
--- a/drivers/edac/amd8131_edac.h
+++ b/drivers/edac/amd8131_edac.h
@@ -99,7 +99,7 @@
/*
* AMD8131 chipset has two pairs of PCIX Bridge and related IOAPIC
- * Controler, and ATCA-6101 has two AMD8131 chipsets, so there are
+ * Controller, and ATCA-6101 has two AMD8131 chipsets, so there are
* four PCIX Bridges on ATCA-6101 altogether.
*
* These PCIX Bridges share the same PCI Device ID and are all of
diff --git a/drivers/edac/cell_edac.c b/drivers/edac/cell_edac.c
index c973004..db1df59 100644
--- a/drivers/edac/cell_edac.c
+++ b/drivers/edac/cell_edac.c
@@ -47,7 +47,7 @@
offset = address & ~PAGE_MASK;
syndrome = (ar & 0x000000001fe00000ul) >> 21;
- /* TODO: Decoding of the error addresss */
+ /* TODO: Decoding of the error address */
edac_mc_handle_ce(mci, csrow->first_page + pfn, offset,
syndrome, 0, chan, "");
}
@@ -68,7 +68,7 @@
pfn = address >> PAGE_SHIFT;
offset = address & ~PAGE_MASK;
- /* TODO: Decoding of the error addresss */
+ /* TODO: Decoding of the error address */
edac_mc_handle_ue(mci, csrow->first_page + pfn, offset, 0, "");
}
diff --git a/drivers/edac/edac_core.h b/drivers/edac/edac_core.h
index ff1eb7b..3d96534 100644
--- a/drivers/edac/edac_core.h
+++ b/drivers/edac/edac_core.h
@@ -259,7 +259,7 @@
* for single channel are 64 bits, for dual channel 128
* bits.
*
- * Single-Ranked stick: A Single-ranked stick has 1 chip-select row of memmory.
+ * Single-Ranked stick: A Single-ranked stick has 1 chip-select row of memory.
* Motherboards commonly drive two chip-select pins to
* a memory stick. A single-ranked stick, will occupy
* only one of those rows. The other will be unused.
diff --git a/drivers/edac/i7core_edac.c b/drivers/edac/i7core_edac.c
index 362861c..81154ab 100644
--- a/drivers/edac/i7core_edac.c
+++ b/drivers/edac/i7core_edac.c
@@ -1,6 +1,6 @@
/* Intel i7 core/Nehalem Memory Controller kernel module
*
- * This driver supports yhe memory controllers found on the Intel
+ * This driver supports the memory controllers found on the Intel
* processor families i7core, i7core 7xx/8xx, i5core, Xeon 35xx,
* Xeon 55xx and Xeon 56xx also known as Nehalem, Nehalem-EP, Lynnfield
* and Westmere-EP.
@@ -1271,7 +1271,7 @@
int i;
/*
- * On Xeon 55xx, the Intel Quckpath Arch Generic Non-core pci buses
+ * On Xeon 55xx, the Intel Quick Path Arch Generic Non-core pci buses
* aren't announced by acpi. So, we need to use a legacy scan probing
* to detect them
*/
@@ -1864,7 +1864,7 @@
if (mce->mcgstatus & 1)
i7core_check_error(mci);
- /* Advice mcelog that the error were handled */
+ /* Advise mcelog that the errors were handled */
return 1;
}
diff --git a/drivers/edac/ppc4xx_edac.c b/drivers/edac/ppc4xx_edac.c
index 070cea4..b9f0c20 100644
--- a/drivers/edac/ppc4xx_edac.c
+++ b/drivers/edac/ppc4xx_edac.c
@@ -873,7 +873,7 @@
}
/**
- * ppc4xx_edac_init_csrows - intialize driver instance rows
+ * ppc4xx_edac_init_csrows - initialize driver instance rows
* @mci: A pointer to the EDAC memory controller instance
* associated with the ibm,sdram-4xx-ddr2 controller for which
* the csrows (i.e. banks/ranks) are being initialized.
@@ -881,7 +881,7 @@
* currently set for the controller, from which bank width
* and memory typ information is derived.
*
- * This routine intializes the virtual "chip select rows" associated
+ * This routine initializes the virtual "chip select rows" associated
* with the EDAC memory controller instance. An ibm,sdram-4xx-ddr2
* controller bank/rank is mapped to a row.
*
@@ -992,7 +992,7 @@
}
/**
- * ppc4xx_edac_mc_init - intialize driver instance
+ * ppc4xx_edac_mc_init - initialize driver instance
* @mci: A pointer to the EDAC memory controller instance being
* initialized.
* @op: A pointer to the OpenFirmware device tree node associated
diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig
index 082495b..664660e 100644
--- a/drivers/gpio/Kconfig
+++ b/drivers/gpio/Kconfig
@@ -118,7 +118,7 @@
config GPIO_VX855
tristate "VIA VX855/VX875 GPIO"
- depends on GPIOLIB
+ depends on GPIOLIB && MFD_SUPPORT && PCI
select MFD_CORE
select MFD_VX855
help
@@ -295,7 +295,7 @@
config GPIO_CS5535
tristate "AMD CS5535/CS5536 GPIO support"
- depends on PCI && !CS5535_GPIO
+ depends on PCI && X86 && !CS5535_GPIO
help
The AMD CS5535 and CS5536 southbridges support 28 GPIO pins that
can be used for quite a number of things. The CS5535/6 is found on
@@ -333,6 +333,15 @@
which is an IOH(Input/Output Hub) for x86 embedded processor.
This driver can access PCH GPIO device.
+config GPIO_ML_IOH
+ tristate "OKI SEMICONDUCTOR ML7213 IOH GPIO support"
+ depends on PCI
+ help
+ ML7213 is companion chip for Intel Atom E6xx series.
+ This driver can be used for OKI SEMICONDUCTOR ML7213 IOH(Input/Output
+ Hub) which is for IVI(In-Vehicle Infotainment) use.
+ This driver can access the IOH's GPIO device.
+
config GPIO_TIMBERDALE
bool "Support for timberdale GPIO IP"
depends on MFD_TIMBERDALE && GPIOLIB && HAS_IOMEM
@@ -342,6 +351,7 @@
config GPIO_RDC321X
tristate "RDC R-321x GPIO support"
depends on PCI && GPIOLIB
+ select MFD_SUPPORT
select MFD_CORE
select MFD_RDC321X
help
diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile
index 39bfd7a..3351cf8 100644
--- a/drivers/gpio/Makefile
+++ b/drivers/gpio/Makefile
@@ -41,3 +41,4 @@
obj-$(CONFIG_GPIO_JANZ_TTL) += janz-ttl.o
obj-$(CONFIG_GPIO_SX150X) += sx150x.o
obj-$(CONFIG_GPIO_VX855) += vx855_gpio.o
+obj-$(CONFIG_GPIO_ML_IOH) += ml_ioh_gpio.o
diff --git a/drivers/gpio/adp5588-gpio.c b/drivers/gpio/adp5588-gpio.c
index 0871f78..33fc685 100644
--- a/drivers/gpio/adp5588-gpio.c
+++ b/drivers/gpio/adp5588-gpio.c
@@ -146,9 +146,10 @@
return dev->irq_base + off;
}
-static void adp5588_irq_bus_lock(unsigned int irq)
+static void adp5588_irq_bus_lock(struct irq_data *d)
{
- struct adp5588_gpio *dev = get_irq_chip_data(irq);
+ struct adp5588_gpio *dev = irq_data_get_irq_chip_data(d);
+
mutex_lock(&dev->irq_lock);
}
@@ -160,9 +161,9 @@
* and unlocks the bus.
*/
-static void adp5588_irq_bus_sync_unlock(unsigned int irq)
+static void adp5588_irq_bus_sync_unlock(struct irq_data *d)
{
- struct adp5588_gpio *dev = get_irq_chip_data(irq);
+ struct adp5588_gpio *dev = irq_data_get_irq_chip_data(d);
int i;
for (i = 0; i <= ADP5588_BANK(ADP5588_MAXGPIO); i++)
@@ -175,31 +176,31 @@
mutex_unlock(&dev->irq_lock);
}
-static void adp5588_irq_mask(unsigned int irq)
+static void adp5588_irq_mask(struct irq_data *d)
{
- struct adp5588_gpio *dev = get_irq_chip_data(irq);
- unsigned gpio = irq - dev->irq_base;
+ struct adp5588_gpio *dev = irq_data_get_irq_chip_data(d);
+ unsigned gpio = d->irq - dev->irq_base;
dev->irq_mask[ADP5588_BANK(gpio)] &= ~ADP5588_BIT(gpio);
}
-static void adp5588_irq_unmask(unsigned int irq)
+static void adp5588_irq_unmask(struct irq_data *d)
{
- struct adp5588_gpio *dev = get_irq_chip_data(irq);
- unsigned gpio = irq - dev->irq_base;
+ struct adp5588_gpio *dev = irq_data_get_irq_chip_data(d);
+ unsigned gpio = d->irq - dev->irq_base;
dev->irq_mask[ADP5588_BANK(gpio)] |= ADP5588_BIT(gpio);
}
-static int adp5588_irq_set_type(unsigned int irq, unsigned int type)
+static int adp5588_irq_set_type(struct irq_data *d, unsigned int type)
{
- struct adp5588_gpio *dev = get_irq_chip_data(irq);
- uint16_t gpio = irq - dev->irq_base;
+ struct adp5588_gpio *dev = irq_data_get_irq_chip_data(d);
+ uint16_t gpio = d->irq - dev->irq_base;
unsigned bank, bit;
if ((type & IRQ_TYPE_EDGE_BOTH)) {
dev_err(&dev->client->dev, "irq %d: unsupported type %d\n",
- irq, type);
+ d->irq, type);
return -EINVAL;
}
@@ -222,11 +223,11 @@
static struct irq_chip adp5588_irq_chip = {
.name = "adp5588",
- .mask = adp5588_irq_mask,
- .unmask = adp5588_irq_unmask,
- .bus_lock = adp5588_irq_bus_lock,
- .bus_sync_unlock = adp5588_irq_bus_sync_unlock,
- .set_type = adp5588_irq_set_type,
+ .irq_mask = adp5588_irq_mask,
+ .irq_unmask = adp5588_irq_unmask,
+ .irq_bus_lock = adp5588_irq_bus_lock,
+ .irq_bus_sync_unlock = adp5588_irq_bus_sync_unlock,
+ .irq_set_type = adp5588_irq_set_type,
};
static int adp5588_gpio_read_intstat(struct i2c_client *client, u8 *buf)
diff --git a/drivers/gpio/cs5535-gpio.c b/drivers/gpio/cs5535-gpio.c
index d3e55a0..815d98b2 100644
--- a/drivers/gpio/cs5535-gpio.c
+++ b/drivers/gpio/cs5535-gpio.c
@@ -15,6 +15,7 @@
#include <linux/gpio.h>
#include <linux/io.h>
#include <linux/cs5535.h>
+#include <asm/msr.h>
#define DRV_NAME "cs5535-gpio"
#define GPIO_BAR 1
@@ -144,6 +145,57 @@
}
EXPORT_SYMBOL_GPL(cs5535_gpio_isset);
+int cs5535_gpio_set_irq(unsigned group, unsigned irq)
+{
+ uint32_t lo, hi;
+
+ if (group > 7 || irq > 15)
+ return -EINVAL;
+
+ rdmsr(MSR_PIC_ZSEL_HIGH, lo, hi);
+
+ lo &= ~(0xF << (group * 4));
+ lo |= (irq & 0xF) << (group * 4);
+
+ wrmsr(MSR_PIC_ZSEL_HIGH, lo, hi);
+ return 0;
+}
+EXPORT_SYMBOL_GPL(cs5535_gpio_set_irq);
+
+void cs5535_gpio_setup_event(unsigned offset, int pair, int pme)
+{
+ struct cs5535_gpio_chip *chip = &cs5535_gpio_chip;
+ uint32_t shift = (offset % 8) * 4;
+ unsigned long flags;
+ uint32_t val;
+
+ if (offset >= 24)
+ offset = GPIO_MAP_W;
+ else if (offset >= 16)
+ offset = GPIO_MAP_Z;
+ else if (offset >= 8)
+ offset = GPIO_MAP_Y;
+ else
+ offset = GPIO_MAP_X;
+
+ spin_lock_irqsave(&chip->lock, flags);
+ val = inl(chip->base + offset);
+
+ /* Clear whatever was there before */
+ val &= ~(0xF << shift);
+
+ /* Set the new value */
+ val |= ((pair & 7) << shift);
+
+ /* Set the PME bit if this is a PME event */
+ if (pme)
+ val |= (1 << (shift + 3));
+
+ outl(val, chip->base + offset);
+ spin_unlock_irqrestore(&chip->lock, flags);
+}
+EXPORT_SYMBOL_GPL(cs5535_gpio_setup_event);
+
/*
* Generic gpio_chip API support.
*/
diff --git a/drivers/gpio/langwell_gpio.c b/drivers/gpio/langwell_gpio.c
index 64db9dc..d81cc74 100644
--- a/drivers/gpio/langwell_gpio.c
+++ b/drivers/gpio/langwell_gpio.c
@@ -134,10 +134,10 @@
return lnw->irq_base + offset;
}
-static int lnw_irq_type(unsigned irq, unsigned type)
+static int lnw_irq_type(struct irq_data *d, unsigned type)
{
- struct lnw_gpio *lnw = get_irq_chip_data(irq);
- u32 gpio = irq - lnw->irq_base;
+ struct lnw_gpio *lnw = irq_data_get_irq_chip_data(d);
+ u32 gpio = d->irq - lnw->irq_base;
unsigned long flags;
u32 value;
void __iomem *grer = gpio_reg(&lnw->chip, gpio, GRER);
@@ -162,19 +162,19 @@
return 0;
}
-static void lnw_irq_unmask(unsigned irq)
+static void lnw_irq_unmask(struct irq_data *d)
{
}
-static void lnw_irq_mask(unsigned irq)
+static void lnw_irq_mask(struct irq_data *d)
{
}
static struct irq_chip lnw_irqchip = {
.name = "LNW-GPIO",
- .mask = lnw_irq_mask,
- .unmask = lnw_irq_unmask,
- .set_type = lnw_irq_type,
+ .irq_mask = lnw_irq_mask,
+ .irq_unmask = lnw_irq_unmask,
+ .irq_set_type = lnw_irq_type,
};
static DEFINE_PCI_DEVICE_TABLE(lnw_gpio_ids) = { /* pin number */
diff --git a/drivers/gpio/max732x.c b/drivers/gpio/max732x.c
index 9cad60f..9e1d01f 100644
--- a/drivers/gpio/max732x.c
+++ b/drivers/gpio/max732x.c
@@ -327,40 +327,40 @@
return chip->irq_base + off;
}
-static void max732x_irq_mask(unsigned int irq)
+static void max732x_irq_mask(struct irq_data *d)
{
- struct max732x_chip *chip = get_irq_chip_data(irq);
+ struct max732x_chip *chip = irq_data_get_irq_chip_data(d);
- chip->irq_mask_cur &= ~(1 << (irq - chip->irq_base));
+ chip->irq_mask_cur &= ~(1 << (d->irq - chip->irq_base));
}
-static void max732x_irq_unmask(unsigned int irq)
+static void max732x_irq_unmask(struct irq_data *d)
{
- struct max732x_chip *chip = get_irq_chip_data(irq);
+ struct max732x_chip *chip = irq_data_get_irq_chip_data(d);
- chip->irq_mask_cur |= 1 << (irq - chip->irq_base);
+ chip->irq_mask_cur |= 1 << (d->irq - chip->irq_base);
}
-static void max732x_irq_bus_lock(unsigned int irq)
+static void max732x_irq_bus_lock(struct irq_data *d)
{
- struct max732x_chip *chip = get_irq_chip_data(irq);
+ struct max732x_chip *chip = irq_data_get_irq_chip_data(d);
mutex_lock(&chip->irq_lock);
chip->irq_mask_cur = chip->irq_mask;
}
-static void max732x_irq_bus_sync_unlock(unsigned int irq)
+static void max732x_irq_bus_sync_unlock(struct irq_data *d)
{
- struct max732x_chip *chip = get_irq_chip_data(irq);
+ struct max732x_chip *chip = irq_data_get_irq_chip_data(d);
max732x_irq_update_mask(chip);
mutex_unlock(&chip->irq_lock);
}
-static int max732x_irq_set_type(unsigned int irq, unsigned int type)
+static int max732x_irq_set_type(struct irq_data *d, unsigned int type)
{
- struct max732x_chip *chip = get_irq_chip_data(irq);
- uint16_t off = irq - chip->irq_base;
+ struct max732x_chip *chip = irq_data_get_irq_chip_data(d);
+ uint16_t off = d->irq - chip->irq_base;
uint16_t mask = 1 << off;
if (!(mask & chip->dir_input)) {
@@ -371,7 +371,7 @@
if (!(type & IRQ_TYPE_EDGE_BOTH)) {
dev_err(&chip->client->dev, "irq %d: unsupported type %d\n",
- irq, type);
+ d->irq, type);
return -EINVAL;
}
@@ -390,11 +390,11 @@
static struct irq_chip max732x_irq_chip = {
.name = "max732x",
- .mask = max732x_irq_mask,
- .unmask = max732x_irq_unmask,
- .bus_lock = max732x_irq_bus_lock,
- .bus_sync_unlock = max732x_irq_bus_sync_unlock,
- .set_type = max732x_irq_set_type,
+ .irq_mask = max732x_irq_mask,
+ .irq_unmask = max732x_irq_unmask,
+ .irq_bus_lock = max732x_irq_bus_lock,
+ .irq_bus_sync_unlock = max732x_irq_bus_sync_unlock,
+ .irq_set_type = max732x_irq_set_type,
};
static uint8_t max732x_irq_pending(struct max732x_chip *chip)
diff --git a/drivers/gpio/ml_ioh_gpio.c b/drivers/gpio/ml_ioh_gpio.c
new file mode 100644
index 0000000..cead8e6
--- /dev/null
+++ b/drivers/gpio/ml_ioh_gpio.c
@@ -0,0 +1,352 @@
+/*
+ * Copyright (C) 2010 OKI SEMICONDUCTOR Co., 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; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
+ */
+#include <linux/kernel.h>
+#include <linux/pci.h>
+#include <linux/gpio.h>
+
+#define PCI_VENDOR_ID_ROHM 0x10DB
+
+struct ioh_reg_comn {
+ u32 ien;
+ u32 istatus;
+ u32 idisp;
+ u32 iclr;
+ u32 imask;
+ u32 imaskclr;
+ u32 po;
+ u32 pi;
+ u32 pm;
+ u32 im_0;
+ u32 im_1;
+ u32 reserved;
+};
+
+struct ioh_regs {
+ struct ioh_reg_comn regs[8];
+ u32 reserve1[16];
+ u32 ioh_sel_reg[4];
+ u32 reserve2[11];
+ u32 srst;
+};
+
+/**
+ * struct ioh_gpio_reg_data - The register store data.
+ * @po_reg: To store contents of PO register.
+ * @pm_reg: To store contents of PM register.
+ */
+struct ioh_gpio_reg_data {
+ u32 po_reg;
+ u32 pm_reg;
+};
+
+/**
+ * struct ioh_gpio - GPIO private data structure.
+ * @base: PCI base address of Memory mapped I/O register.
+ * @reg: Memory mapped IOH GPIO register list.
+ * @dev: Pointer to device structure.
+ * @gpio: Data for GPIO infrastructure.
+ * @ioh_gpio_reg: Memory mapped Register data is saved here
+ * when suspend.
+ * @ch: Indicate GPIO channel
+ */
+struct ioh_gpio {
+ void __iomem *base;
+ struct ioh_regs __iomem *reg;
+ struct device *dev;
+ struct gpio_chip gpio;
+ struct ioh_gpio_reg_data ioh_gpio_reg;
+ struct mutex lock;
+ int ch;
+};
+
+static const int num_ports[] = {6, 12, 16, 16, 15, 16, 16, 12};
+
+static void ioh_gpio_set(struct gpio_chip *gpio, unsigned nr, int val)
+{
+ u32 reg_val;
+ struct ioh_gpio *chip = container_of(gpio, struct ioh_gpio, gpio);
+
+ mutex_lock(&chip->lock);
+ reg_val = ioread32(&chip->reg->regs[chip->ch].po);
+ if (val)
+ reg_val |= (1 << nr);
+ else
+ reg_val &= ~(1 << nr);
+
+ iowrite32(reg_val, &chip->reg->regs[chip->ch].po);
+ mutex_unlock(&chip->lock);
+}
+
+static int ioh_gpio_get(struct gpio_chip *gpio, unsigned nr)
+{
+ struct ioh_gpio *chip = container_of(gpio, struct ioh_gpio, gpio);
+
+ return ioread32(&chip->reg->regs[chip->ch].pi) & (1 << nr);
+}
+
+static int ioh_gpio_direction_output(struct gpio_chip *gpio, unsigned nr,
+ int val)
+{
+ struct ioh_gpio *chip = container_of(gpio, struct ioh_gpio, gpio);
+ u32 pm;
+ u32 reg_val;
+
+ mutex_lock(&chip->lock);
+ pm = ioread32(&chip->reg->regs[chip->ch].pm) &
+ ((1 << num_ports[chip->ch]) - 1);
+ pm |= (1 << nr);
+ iowrite32(pm, &chip->reg->regs[chip->ch].pm);
+
+ reg_val = ioread32(&chip->reg->regs[chip->ch].po);
+ if (val)
+ reg_val |= (1 << nr);
+ else
+ reg_val &= ~(1 << nr);
+
+ mutex_unlock(&chip->lock);
+
+ return 0;
+}
+
+static int ioh_gpio_direction_input(struct gpio_chip *gpio, unsigned nr)
+{
+ struct ioh_gpio *chip = container_of(gpio, struct ioh_gpio, gpio);
+ u32 pm;
+
+ mutex_lock(&chip->lock);
+ pm = ioread32(&chip->reg->regs[chip->ch].pm) &
+ ((1 << num_ports[chip->ch]) - 1);
+ pm &= ~(1 << nr);
+ iowrite32(pm, &chip->reg->regs[chip->ch].pm);
+ mutex_unlock(&chip->lock);
+
+ return 0;
+}
+
+/*
+ * Save register configuration and disable interrupts.
+ */
+static void ioh_gpio_save_reg_conf(struct ioh_gpio *chip)
+{
+ chip->ioh_gpio_reg.po_reg = ioread32(&chip->reg->regs[chip->ch].po);
+ chip->ioh_gpio_reg.pm_reg = ioread32(&chip->reg->regs[chip->ch].pm);
+}
+
+/*
+ * This function restores the register configuration of the GPIO device.
+ */
+static void ioh_gpio_restore_reg_conf(struct ioh_gpio *chip)
+{
+ /* to store contents of PO register */
+ iowrite32(chip->ioh_gpio_reg.po_reg, &chip->reg->regs[chip->ch].po);
+ /* to store contents of PM register */
+ iowrite32(chip->ioh_gpio_reg.pm_reg, &chip->reg->regs[chip->ch].pm);
+}
+
+static void ioh_gpio_setup(struct ioh_gpio *chip, int num_port)
+{
+ struct gpio_chip *gpio = &chip->gpio;
+
+ gpio->label = dev_name(chip->dev);
+ gpio->owner = THIS_MODULE;
+ gpio->direction_input = ioh_gpio_direction_input;
+ gpio->get = ioh_gpio_get;
+ gpio->direction_output = ioh_gpio_direction_output;
+ gpio->set = ioh_gpio_set;
+ gpio->dbg_show = NULL;
+ gpio->base = -1;
+ gpio->ngpio = num_port;
+ gpio->can_sleep = 0;
+}
+
+static int __devinit ioh_gpio_probe(struct pci_dev *pdev,
+ const struct pci_device_id *id)
+{
+ int ret;
+ int i;
+ struct ioh_gpio *chip;
+ void __iomem *base;
+ void __iomem *chip_save;
+
+ ret = pci_enable_device(pdev);
+ if (ret) {
+ dev_err(&pdev->dev, "%s : pci_enable_device failed", __func__);
+ goto err_pci_enable;
+ }
+
+ ret = pci_request_regions(pdev, KBUILD_MODNAME);
+ if (ret) {
+ dev_err(&pdev->dev, "pci_request_regions failed-%d", ret);
+ goto err_request_regions;
+ }
+
+ base = pci_iomap(pdev, 1, 0);
+ if (base == 0) {
+ dev_err(&pdev->dev, "%s : pci_iomap failed", __func__);
+ ret = -ENOMEM;
+ goto err_iomap;
+ }
+
+ chip_save = kzalloc(sizeof(*chip) * 8, GFP_KERNEL);
+ if (chip_save == NULL) {
+ dev_err(&pdev->dev, "%s : kzalloc failed", __func__);
+ ret = -ENOMEM;
+ goto err_kzalloc;
+ }
+
+ chip = chip_save;
+ for (i = 0; i < 8; i++, chip++) {
+ chip->dev = &pdev->dev;
+ chip->base = base;
+ chip->reg = chip->base;
+ chip->ch = i;
+ mutex_init(&chip->lock);
+ ioh_gpio_setup(chip, num_ports[i]);
+ ret = gpiochip_add(&chip->gpio);
+ if (ret) {
+ dev_err(&pdev->dev, "IOH gpio: Failed to register GPIO\n");
+ goto err_gpiochip_add;
+ }
+ }
+
+ chip = chip_save;
+ pci_set_drvdata(pdev, chip);
+
+ return 0;
+
+err_gpiochip_add:
+ for (; i != 0; i--) {
+ chip--;
+ ret = gpiochip_remove(&chip->gpio);
+ if (ret)
+ dev_err(&pdev->dev, "Failed gpiochip_remove(%d)\n", i);
+ }
+ kfree(chip_save);
+
+err_kzalloc:
+ pci_iounmap(pdev, base);
+
+err_iomap:
+ pci_release_regions(pdev);
+
+err_request_regions:
+ pci_disable_device(pdev);
+
+err_pci_enable:
+
+ dev_err(&pdev->dev, "%s Failed returns %d\n", __func__, ret);
+ return ret;
+}
+
+static void __devexit ioh_gpio_remove(struct pci_dev *pdev)
+{
+ int err;
+ int i;
+ struct ioh_gpio *chip = pci_get_drvdata(pdev);
+ void __iomem *chip_save;
+
+ chip_save = chip;
+ for (i = 0; i < 8; i++, chip++) {
+ err = gpiochip_remove(&chip->gpio);
+ if (err)
+ dev_err(&pdev->dev, "Failed gpiochip_remove\n");
+ }
+
+ chip = chip_save;
+ pci_iounmap(pdev, chip->base);
+ pci_release_regions(pdev);
+ pci_disable_device(pdev);
+ kfree(chip);
+}
+
+#ifdef CONFIG_PM
+static int ioh_gpio_suspend(struct pci_dev *pdev, pm_message_t state)
+{
+ s32 ret;
+ struct ioh_gpio *chip = pci_get_drvdata(pdev);
+
+ ioh_gpio_save_reg_conf(chip);
+ ioh_gpio_restore_reg_conf(chip);
+
+ ret = pci_save_state(pdev);
+ if (ret) {
+ dev_err(&pdev->dev, "pci_save_state Failed-%d\n", ret);
+ return ret;
+ }
+ pci_disable_device(pdev);
+ pci_set_power_state(pdev, PCI_D0);
+ ret = pci_enable_wake(pdev, PCI_D0, 1);
+ if (ret)
+ dev_err(&pdev->dev, "pci_enable_wake Failed -%d\n", ret);
+
+ return 0;
+}
+
+static int ioh_gpio_resume(struct pci_dev *pdev)
+{
+ s32 ret;
+ struct ioh_gpio *chip = pci_get_drvdata(pdev);
+
+ ret = pci_enable_wake(pdev, PCI_D0, 0);
+
+ pci_set_power_state(pdev, PCI_D0);
+ ret = pci_enable_device(pdev);
+ if (ret) {
+ dev_err(&pdev->dev, "pci_enable_device Failed-%d ", ret);
+ return ret;
+ }
+ pci_restore_state(pdev);
+
+ iowrite32(0x01, &chip->reg->srst);
+ iowrite32(0x00, &chip->reg->srst);
+ ioh_gpio_restore_reg_conf(chip);
+
+ return 0;
+}
+#else
+#define ioh_gpio_suspend NULL
+#define ioh_gpio_resume NULL
+#endif
+
+static DEFINE_PCI_DEVICE_TABLE(ioh_gpio_pcidev_id) = {
+ { PCI_DEVICE(PCI_VENDOR_ID_ROHM, 0x802E) },
+ { 0, }
+};
+
+static struct pci_driver ioh_gpio_driver = {
+ .name = "ml_ioh_gpio",
+ .id_table = ioh_gpio_pcidev_id,
+ .probe = ioh_gpio_probe,
+ .remove = __devexit_p(ioh_gpio_remove),
+ .suspend = ioh_gpio_suspend,
+ .resume = ioh_gpio_resume
+};
+
+static int __init ioh_gpio_pci_init(void)
+{
+ return pci_register_driver(&ioh_gpio_driver);
+}
+module_init(ioh_gpio_pci_init);
+
+static void __exit ioh_gpio_pci_exit(void)
+{
+ pci_unregister_driver(&ioh_gpio_driver);
+}
+module_exit(ioh_gpio_pci_exit);
+
+MODULE_DESCRIPTION("OKI SEMICONDUCTOR ML-IOH series GPIO Driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/gpio/pca953x.c b/drivers/gpio/pca953x.c
index 5018666..a261972 100644
--- a/drivers/gpio/pca953x.c
+++ b/drivers/gpio/pca953x.c
@@ -228,30 +228,30 @@
return chip->irq_base + off;
}
-static void pca953x_irq_mask(unsigned int irq)
+static void pca953x_irq_mask(struct irq_data *d)
{
- struct pca953x_chip *chip = get_irq_chip_data(irq);
+ struct pca953x_chip *chip = irq_data_get_irq_chip_data(d);
- chip->irq_mask &= ~(1 << (irq - chip->irq_base));
+ chip->irq_mask &= ~(1 << (d->irq - chip->irq_base));
}
-static void pca953x_irq_unmask(unsigned int irq)
+static void pca953x_irq_unmask(struct irq_data *d)
{
- struct pca953x_chip *chip = get_irq_chip_data(irq);
+ struct pca953x_chip *chip = irq_data_get_irq_chip_data(d);
- chip->irq_mask |= 1 << (irq - chip->irq_base);
+ chip->irq_mask |= 1 << (d->irq - chip->irq_base);
}
-static void pca953x_irq_bus_lock(unsigned int irq)
+static void pca953x_irq_bus_lock(struct irq_data *d)
{
- struct pca953x_chip *chip = get_irq_chip_data(irq);
+ struct pca953x_chip *chip = irq_data_get_irq_chip_data(d);
mutex_lock(&chip->irq_lock);
}
-static void pca953x_irq_bus_sync_unlock(unsigned int irq)
+static void pca953x_irq_bus_sync_unlock(struct irq_data *d)
{
- struct pca953x_chip *chip = get_irq_chip_data(irq);
+ struct pca953x_chip *chip = irq_data_get_irq_chip_data(d);
uint16_t new_irqs;
uint16_t level;
@@ -268,15 +268,15 @@
mutex_unlock(&chip->irq_lock);
}
-static int pca953x_irq_set_type(unsigned int irq, unsigned int type)
+static int pca953x_irq_set_type(struct irq_data *d, unsigned int type)
{
- struct pca953x_chip *chip = get_irq_chip_data(irq);
- uint16_t level = irq - chip->irq_base;
+ struct pca953x_chip *chip = irq_data_get_irq_chip_data(d);
+ uint16_t level = d->irq - chip->irq_base;
uint16_t mask = 1 << level;
if (!(type & IRQ_TYPE_EDGE_BOTH)) {
dev_err(&chip->client->dev, "irq %d: unsupported type %d\n",
- irq, type);
+ d->irq, type);
return -EINVAL;
}
@@ -295,11 +295,11 @@
static struct irq_chip pca953x_irq_chip = {
.name = "pca953x",
- .mask = pca953x_irq_mask,
- .unmask = pca953x_irq_unmask,
- .bus_lock = pca953x_irq_bus_lock,
- .bus_sync_unlock = pca953x_irq_bus_sync_unlock,
- .set_type = pca953x_irq_set_type,
+ .irq_mask = pca953x_irq_mask,
+ .irq_unmask = pca953x_irq_unmask,
+ .irq_bus_lock = pca953x_irq_bus_lock,
+ .irq_bus_sync_unlock = pca953x_irq_bus_sync_unlock,
+ .irq_set_type = pca953x_irq_set_type,
};
static uint16_t pca953x_irq_pending(struct pca953x_chip *chip)
diff --git a/drivers/gpio/pl061.c b/drivers/gpio/pl061.c
index 5005990..2975d22 100644
--- a/drivers/gpio/pl061.c
+++ b/drivers/gpio/pl061.c
@@ -129,10 +129,10 @@
/*
* PL061 GPIO IRQ
*/
-static void pl061_irq_disable(unsigned irq)
+static void pl061_irq_disable(struct irq_data *d)
{
- struct pl061_gpio *chip = get_irq_chip_data(irq);
- int offset = irq - chip->irq_base;
+ struct pl061_gpio *chip = irq_data_get_irq_chip_data(d);
+ int offset = d->irq - chip->irq_base;
unsigned long flags;
u8 gpioie;
@@ -143,10 +143,10 @@
spin_unlock_irqrestore(&chip->irq_lock, flags);
}
-static void pl061_irq_enable(unsigned irq)
+static void pl061_irq_enable(struct irq_data *d)
{
- struct pl061_gpio *chip = get_irq_chip_data(irq);
- int offset = irq - chip->irq_base;
+ struct pl061_gpio *chip = irq_data_get_irq_chip_data(d);
+ int offset = d->irq - chip->irq_base;
unsigned long flags;
u8 gpioie;
@@ -157,10 +157,10 @@
spin_unlock_irqrestore(&chip->irq_lock, flags);
}
-static int pl061_irq_type(unsigned irq, unsigned trigger)
+static int pl061_irq_type(struct irq_data *d, unsigned trigger)
{
- struct pl061_gpio *chip = get_irq_chip_data(irq);
- int offset = irq - chip->irq_base;
+ struct pl061_gpio *chip = irq_data_get_irq_chip_data(d);
+ int offset = d->irq - chip->irq_base;
unsigned long flags;
u8 gpiois, gpioibe, gpioiev;
@@ -203,9 +203,9 @@
static struct irq_chip pl061_irqchip = {
.name = "GPIO",
- .enable = pl061_irq_enable,
- .disable = pl061_irq_disable,
- .set_type = pl061_irq_type,
+ .irq_enable = pl061_irq_enable,
+ .irq_disable = pl061_irq_disable,
+ .irq_set_type = pl061_irq_type,
};
static void pl061_irq_handler(unsigned irq, struct irq_desc *desc)
@@ -214,7 +214,7 @@
struct list_head *ptr;
struct pl061_gpio *chip;
- desc->chip->ack(irq);
+ desc->irq_data.chip->irq_ack(&desc->irq_data);
list_for_each(ptr, chip_list) {
unsigned long pending;
int offset;
@@ -229,7 +229,7 @@
for_each_set_bit(offset, &pending, PL061_GPIO_NR)
generic_handle_irq(pl061_to_irq(&chip->gc, offset));
}
- desc->chip->unmask(irq);
+ desc->irq_data.chip->irq_unmask(&desc->irq_data);
}
static int pl061_probe(struct amba_device *dev, struct amba_id *id)
diff --git a/drivers/gpio/stmpe-gpio.c b/drivers/gpio/stmpe-gpio.c
index 7c9e6a0..eb2901f 100644
--- a/drivers/gpio/stmpe-gpio.c
+++ b/drivers/gpio/stmpe-gpio.c
@@ -122,10 +122,10 @@
.can_sleep = 1,
};
-static int stmpe_gpio_irq_set_type(unsigned int irq, unsigned int type)
+static int stmpe_gpio_irq_set_type(struct irq_data *d, unsigned int type)
{
- struct stmpe_gpio *stmpe_gpio = get_irq_chip_data(irq);
- int offset = irq - stmpe_gpio->irq_base;
+ struct stmpe_gpio *stmpe_gpio = irq_data_get_irq_chip_data(d);
+ int offset = d->irq - stmpe_gpio->irq_base;
int regoffset = offset / 8;
int mask = 1 << (offset % 8);
@@ -145,16 +145,16 @@
return 0;
}
-static void stmpe_gpio_irq_lock(unsigned int irq)
+static void stmpe_gpio_irq_lock(struct irq_data *d)
{
- struct stmpe_gpio *stmpe_gpio = get_irq_chip_data(irq);
+ struct stmpe_gpio *stmpe_gpio = irq_data_get_irq_chip_data(d);
mutex_lock(&stmpe_gpio->irq_lock);
}
-static void stmpe_gpio_irq_sync_unlock(unsigned int irq)
+static void stmpe_gpio_irq_sync_unlock(struct irq_data *d)
{
- struct stmpe_gpio *stmpe_gpio = get_irq_chip_data(irq);
+ struct stmpe_gpio *stmpe_gpio = irq_data_get_irq_chip_data(d);
struct stmpe *stmpe = stmpe_gpio->stmpe;
int num_banks = DIV_ROUND_UP(stmpe->num_gpios, 8);
static const u8 regmap[] = {
@@ -180,20 +180,20 @@
mutex_unlock(&stmpe_gpio->irq_lock);
}
-static void stmpe_gpio_irq_mask(unsigned int irq)
+static void stmpe_gpio_irq_mask(struct irq_data *d)
{
- struct stmpe_gpio *stmpe_gpio = get_irq_chip_data(irq);
- int offset = irq - stmpe_gpio->irq_base;
+ struct stmpe_gpio *stmpe_gpio = irq_data_get_irq_chip_data(d);
+ int offset = d->irq - stmpe_gpio->irq_base;
int regoffset = offset / 8;
int mask = 1 << (offset % 8);
stmpe_gpio->regs[REG_IE][regoffset] &= ~mask;
}
-static void stmpe_gpio_irq_unmask(unsigned int irq)
+static void stmpe_gpio_irq_unmask(struct irq_data *d)
{
- struct stmpe_gpio *stmpe_gpio = get_irq_chip_data(irq);
- int offset = irq - stmpe_gpio->irq_base;
+ struct stmpe_gpio *stmpe_gpio = irq_data_get_irq_chip_data(d);
+ int offset = d->irq - stmpe_gpio->irq_base;
int regoffset = offset / 8;
int mask = 1 << (offset % 8);
@@ -202,11 +202,11 @@
static struct irq_chip stmpe_gpio_irq_chip = {
.name = "stmpe-gpio",
- .bus_lock = stmpe_gpio_irq_lock,
- .bus_sync_unlock = stmpe_gpio_irq_sync_unlock,
- .mask = stmpe_gpio_irq_mask,
- .unmask = stmpe_gpio_irq_unmask,
- .set_type = stmpe_gpio_irq_set_type,
+ .irq_bus_lock = stmpe_gpio_irq_lock,
+ .irq_bus_sync_unlock = stmpe_gpio_irq_sync_unlock,
+ .irq_mask = stmpe_gpio_irq_mask,
+ .irq_unmask = stmpe_gpio_irq_unmask,
+ .irq_set_type = stmpe_gpio_irq_set_type,
};
static irqreturn_t stmpe_gpio_irq(int irq, void *dev)
diff --git a/drivers/gpio/sx150x.c b/drivers/gpio/sx150x.c
index 823559a..e60be00 100644
--- a/drivers/gpio/sx150x.c
+++ b/drivers/gpio/sx150x.c
@@ -304,36 +304,36 @@
return chip->irq_base + offset;
}
-static void sx150x_irq_mask(unsigned int irq)
+static void sx150x_irq_mask(struct irq_data *d)
{
- struct irq_chip *ic = get_irq_chip(irq);
+ struct irq_chip *ic = irq_data_get_irq_chip(d);
struct sx150x_chip *chip;
unsigned n;
chip = container_of(ic, struct sx150x_chip, irq_chip);
- n = irq - chip->irq_base;
+ n = d->irq - chip->irq_base;
sx150x_write_cfg(chip, n, 1, chip->dev_cfg->reg_irq_mask, 1);
sx150x_write_cfg(chip, n, 2, chip->dev_cfg->reg_sense, 0);
}
-static void sx150x_irq_unmask(unsigned int irq)
+static void sx150x_irq_unmask(struct irq_data *d)
{
- struct irq_chip *ic = get_irq_chip(irq);
+ struct irq_chip *ic = irq_data_get_irq_chip(d);
struct sx150x_chip *chip;
unsigned n;
chip = container_of(ic, struct sx150x_chip, irq_chip);
- n = irq - chip->irq_base;
+ n = d->irq - chip->irq_base;
sx150x_write_cfg(chip, n, 1, chip->dev_cfg->reg_irq_mask, 0);
sx150x_write_cfg(chip, n, 2, chip->dev_cfg->reg_sense,
chip->irq_sense >> (n * 2));
}
-static int sx150x_irq_set_type(unsigned int irq, unsigned int flow_type)
+static int sx150x_irq_set_type(struct irq_data *d, unsigned int flow_type)
{
- struct irq_chip *ic = get_irq_chip(irq);
+ struct irq_chip *ic = irq_data_get_irq_chip(d);
struct sx150x_chip *chip;
unsigned n, val = 0;
@@ -341,7 +341,7 @@
return -EINVAL;
chip = container_of(ic, struct sx150x_chip, irq_chip);
- n = irq - chip->irq_base;
+ n = d->irq - chip->irq_base;
if (flow_type & IRQ_TYPE_EDGE_RISING)
val |= 0x1;
@@ -386,9 +386,9 @@
return (nhandled > 0 ? IRQ_HANDLED : IRQ_NONE);
}
-static void sx150x_irq_bus_lock(unsigned int irq)
+static void sx150x_irq_bus_lock(struct irq_data *d)
{
- struct irq_chip *ic = get_irq_chip(irq);
+ struct irq_chip *ic = irq_data_get_irq_chip(d);
struct sx150x_chip *chip;
chip = container_of(ic, struct sx150x_chip, irq_chip);
@@ -396,9 +396,9 @@
mutex_lock(&chip->lock);
}
-static void sx150x_irq_bus_sync_unlock(unsigned int irq)
+static void sx150x_irq_bus_sync_unlock(struct irq_data *d)
{
- struct irq_chip *ic = get_irq_chip(irq);
+ struct irq_chip *ic = irq_data_get_irq_chip(d);
struct sx150x_chip *chip;
unsigned n;
@@ -437,16 +437,16 @@
if (pdata->oscio_is_gpo)
++chip->gpio_chip.ngpio;
- chip->irq_chip.name = client->name;
- chip->irq_chip.mask = sx150x_irq_mask;
- chip->irq_chip.unmask = sx150x_irq_unmask;
- chip->irq_chip.set_type = sx150x_irq_set_type;
- chip->irq_chip.bus_lock = sx150x_irq_bus_lock;
- chip->irq_chip.bus_sync_unlock = sx150x_irq_bus_sync_unlock;
- chip->irq_summary = -1;
- chip->irq_base = -1;
- chip->irq_sense = 0;
- chip->irq_set_type_pending = 0;
+ chip->irq_chip.name = client->name;
+ chip->irq_chip.irq_mask = sx150x_irq_mask;
+ chip->irq_chip.irq_unmask = sx150x_irq_unmask;
+ chip->irq_chip.irq_set_type = sx150x_irq_set_type;
+ chip->irq_chip.irq_bus_lock = sx150x_irq_bus_lock;
+ chip->irq_chip.irq_bus_sync_unlock = sx150x_irq_bus_sync_unlock;
+ chip->irq_summary = -1;
+ chip->irq_base = -1;
+ chip->irq_sense = 0;
+ chip->irq_set_type_pending = 0;
}
static int sx150x_init_io(struct sx150x_chip *chip, u8 base, u16 cfg)
diff --git a/drivers/gpio/tc3589x-gpio.c b/drivers/gpio/tc3589x-gpio.c
index 180d584..27200af 100644
--- a/drivers/gpio/tc3589x-gpio.c
+++ b/drivers/gpio/tc3589x-gpio.c
@@ -110,10 +110,10 @@
.can_sleep = 1,
};
-static int tc3589x_gpio_irq_set_type(unsigned int irq, unsigned int type)
+static int tc3589x_gpio_irq_set_type(struct irq_data *d, unsigned int type)
{
- struct tc3589x_gpio *tc3589x_gpio = get_irq_chip_data(irq);
- int offset = irq - tc3589x_gpio->irq_base;
+ struct tc3589x_gpio *tc3589x_gpio = irq_data_get_irq_chip_data(d);
+ int offset = d->irq - tc3589x_gpio->irq_base;
int regoffset = offset / 8;
int mask = 1 << (offset % 8);
@@ -137,16 +137,16 @@
return 0;
}
-static void tc3589x_gpio_irq_lock(unsigned int irq)
+static void tc3589x_gpio_irq_lock(struct irq_data *d)
{
- struct tc3589x_gpio *tc3589x_gpio = get_irq_chip_data(irq);
+ struct tc3589x_gpio *tc3589x_gpio = irq_data_get_irq_chip_data(d);
mutex_lock(&tc3589x_gpio->irq_lock);
}
-static void tc3589x_gpio_irq_sync_unlock(unsigned int irq)
+static void tc3589x_gpio_irq_sync_unlock(struct irq_data *d)
{
- struct tc3589x_gpio *tc3589x_gpio = get_irq_chip_data(irq);
+ struct tc3589x_gpio *tc3589x_gpio = irq_data_get_irq_chip_data(d);
struct tc3589x *tc3589x = tc3589x_gpio->tc3589x;
static const u8 regmap[] = {
[REG_IBE] = TC3589x_GPIOIBE0,
@@ -172,20 +172,20 @@
mutex_unlock(&tc3589x_gpio->irq_lock);
}
-static void tc3589x_gpio_irq_mask(unsigned int irq)
+static void tc3589x_gpio_irq_mask(struct irq_data *d)
{
- struct tc3589x_gpio *tc3589x_gpio = get_irq_chip_data(irq);
- int offset = irq - tc3589x_gpio->irq_base;
+ struct tc3589x_gpio *tc3589x_gpio = irq_data_get_irq_chip_data(d);
+ int offset = d->irq - tc3589x_gpio->irq_base;
int regoffset = offset / 8;
int mask = 1 << (offset % 8);
tc3589x_gpio->regs[REG_IE][regoffset] &= ~mask;
}
-static void tc3589x_gpio_irq_unmask(unsigned int irq)
+static void tc3589x_gpio_irq_unmask(struct irq_data *d)
{
- struct tc3589x_gpio *tc3589x_gpio = get_irq_chip_data(irq);
- int offset = irq - tc3589x_gpio->irq_base;
+ struct tc3589x_gpio *tc3589x_gpio = irq_data_get_irq_chip_data(d);
+ int offset = d->irq - tc3589x_gpio->irq_base;
int regoffset = offset / 8;
int mask = 1 << (offset % 8);
@@ -194,11 +194,11 @@
static struct irq_chip tc3589x_gpio_irq_chip = {
.name = "tc3589x-gpio",
- .bus_lock = tc3589x_gpio_irq_lock,
- .bus_sync_unlock = tc3589x_gpio_irq_sync_unlock,
- .mask = tc3589x_gpio_irq_mask,
- .unmask = tc3589x_gpio_irq_unmask,
- .set_type = tc3589x_gpio_irq_set_type,
+ .irq_bus_lock = tc3589x_gpio_irq_lock,
+ .irq_bus_sync_unlock = tc3589x_gpio_irq_sync_unlock,
+ .irq_mask = tc3589x_gpio_irq_mask,
+ .irq_unmask = tc3589x_gpio_irq_unmask,
+ .irq_set_type = tc3589x_gpio_irq_set_type,
};
static irqreturn_t tc3589x_gpio_irq(int irq, void *dev)
diff --git a/drivers/gpio/timbgpio.c b/drivers/gpio/timbgpio.c
index 4529366..349131e 100644
--- a/drivers/gpio/timbgpio.c
+++ b/drivers/gpio/timbgpio.c
@@ -109,10 +109,10 @@
/*
* GPIO IRQ
*/
-static void timbgpio_irq_disable(unsigned irq)
+static void timbgpio_irq_disable(struct irq_data *d)
{
- struct timbgpio *tgpio = get_irq_chip_data(irq);
- int offset = irq - tgpio->irq_base;
+ struct timbgpio *tgpio = irq_data_get_irq_chip_data(d);
+ int offset = d->irq - tgpio->irq_base;
unsigned long flags;
spin_lock_irqsave(&tgpio->lock, flags);
@@ -121,10 +121,10 @@
spin_unlock_irqrestore(&tgpio->lock, flags);
}
-static void timbgpio_irq_enable(unsigned irq)
+static void timbgpio_irq_enable(struct irq_data *d)
{
- struct timbgpio *tgpio = get_irq_chip_data(irq);
- int offset = irq - tgpio->irq_base;
+ struct timbgpio *tgpio = irq_data_get_irq_chip_data(d);
+ int offset = d->irq - tgpio->irq_base;
unsigned long flags;
spin_lock_irqsave(&tgpio->lock, flags);
@@ -133,10 +133,10 @@
spin_unlock_irqrestore(&tgpio->lock, flags);
}
-static int timbgpio_irq_type(unsigned irq, unsigned trigger)
+static int timbgpio_irq_type(struct irq_data *d, unsigned trigger)
{
- struct timbgpio *tgpio = get_irq_chip_data(irq);
- int offset = irq - tgpio->irq_base;
+ struct timbgpio *tgpio = irq_data_get_irq_chip_data(d);
+ int offset = d->irq - tgpio->irq_base;
unsigned long flags;
u32 lvr, flr, bflr = 0;
u32 ver;
@@ -193,13 +193,13 @@
return ret;
}
-static void timbgpio_irq(unsigned int irq, struct irq_desc *desc)
+static void timbgpio_irq(struct irq_data *d, struct irq_desc *desc)
{
- struct timbgpio *tgpio = get_irq_data(irq);
+ struct timbgpio *tgpio = irq_data_get_irq_data(d);
unsigned long ipr;
int offset;
- desc->chip->ack(irq);
+ desc->irq_data.chip->ack(irq_get_irq_data(d));
ipr = ioread32(tgpio->membase + TGPIO_IPR);
iowrite32(ipr, tgpio->membase + TGPIO_ICR);
@@ -217,9 +217,9 @@
static struct irq_chip timbgpio_irqchip = {
.name = "GPIO",
- .enable = timbgpio_irq_enable,
- .disable = timbgpio_irq_disable,
- .set_type = timbgpio_irq_type,
+ .irq_enable = timbgpio_irq_enable,
+ .irq_disable = timbgpio_irq_disable,
+ .irq_set_type = timbgpio_irq_type,
};
static int __devinit timbgpio_probe(struct platform_device *pdev)
diff --git a/drivers/gpio/vr41xx_giu.c b/drivers/gpio/vr41xx_giu.c
index b16c9a8..cffa3bd 100644
--- a/drivers/gpio/vr41xx_giu.c
+++ b/drivers/gpio/vr41xx_giu.c
@@ -111,69 +111,69 @@
return data;
}
-static void ack_giuint_low(unsigned int irq)
+static void ack_giuint_low(struct irq_data *d)
{
- giu_write(GIUINTSTATL, 1 << GPIO_PIN_OF_IRQ(irq));
+ giu_write(GIUINTSTATL, 1 << GPIO_PIN_OF_IRQ(d->irq));
}
-static void mask_giuint_low(unsigned int irq)
+static void mask_giuint_low(struct irq_data *d)
{
- giu_clear(GIUINTENL, 1 << GPIO_PIN_OF_IRQ(irq));
+ giu_clear(GIUINTENL, 1 << GPIO_PIN_OF_IRQ(d->irq));
}
-static void mask_ack_giuint_low(unsigned int irq)
+static void mask_ack_giuint_low(struct irq_data *d)
{
unsigned int pin;
- pin = GPIO_PIN_OF_IRQ(irq);
+ pin = GPIO_PIN_OF_IRQ(d->irq);
giu_clear(GIUINTENL, 1 << pin);
giu_write(GIUINTSTATL, 1 << pin);
}
-static void unmask_giuint_low(unsigned int irq)
+static void unmask_giuint_low(struct irq_data *d)
{
- giu_set(GIUINTENL, 1 << GPIO_PIN_OF_IRQ(irq));
+ giu_set(GIUINTENL, 1 << GPIO_PIN_OF_IRQ(d->irq));
}
static struct irq_chip giuint_low_irq_chip = {
.name = "GIUINTL",
- .ack = ack_giuint_low,
- .mask = mask_giuint_low,
- .mask_ack = mask_ack_giuint_low,
- .unmask = unmask_giuint_low,
+ .irq_ack = ack_giuint_low,
+ .irq_mask = mask_giuint_low,
+ .irq_mask_ack = mask_ack_giuint_low,
+ .irq_unmask = unmask_giuint_low,
};
-static void ack_giuint_high(unsigned int irq)
+static void ack_giuint_high(struct irq_data *d)
{
giu_write(GIUINTSTATH,
- 1 << (GPIO_PIN_OF_IRQ(irq) - GIUINT_HIGH_OFFSET));
+ 1 << (GPIO_PIN_OF_IRQ(d->irq) - GIUINT_HIGH_OFFSET));
}
-static void mask_giuint_high(unsigned int irq)
+static void mask_giuint_high(struct irq_data *d)
{
- giu_clear(GIUINTENH, 1 << (GPIO_PIN_OF_IRQ(irq) - GIUINT_HIGH_OFFSET));
+ giu_clear(GIUINTENH, 1 << (GPIO_PIN_OF_IRQ(d->irq) - GIUINT_HIGH_OFFSET));
}
-static void mask_ack_giuint_high(unsigned int irq)
+static void mask_ack_giuint_high(struct irq_data *d)
{
unsigned int pin;
- pin = GPIO_PIN_OF_IRQ(irq) - GIUINT_HIGH_OFFSET;
+ pin = GPIO_PIN_OF_IRQ(d->irq) - GIUINT_HIGH_OFFSET;
giu_clear(GIUINTENH, 1 << pin);
giu_write(GIUINTSTATH, 1 << pin);
}
-static void unmask_giuint_high(unsigned int irq)
+static void unmask_giuint_high(struct irq_data *d)
{
- giu_set(GIUINTENH, 1 << (GPIO_PIN_OF_IRQ(irq) - GIUINT_HIGH_OFFSET));
+ giu_set(GIUINTENH, 1 << (GPIO_PIN_OF_IRQ(d->irq) - GIUINT_HIGH_OFFSET));
}
static struct irq_chip giuint_high_irq_chip = {
.name = "GIUINTH",
- .ack = ack_giuint_high,
- .mask = mask_giuint_high,
- .mask_ack = mask_ack_giuint_high,
- .unmask = unmask_giuint_high,
+ .irq_ack = ack_giuint_high,
+ .irq_mask = mask_giuint_high,
+ .irq_mask_ack = mask_ack_giuint_high,
+ .irq_unmask = unmask_giuint_high,
};
static int giu_get_irq(unsigned int irq)
diff --git a/drivers/gpu/drm/i915/i915_debugfs.c b/drivers/gpu/drm/i915/i915_debugfs.c
index 92f7578..19a3d58 100644
--- a/drivers/gpu/drm/i915/i915_debugfs.c
+++ b/drivers/gpu/drm/i915/i915_debugfs.c
@@ -106,10 +106,19 @@
}
}
+static const char *agp_type_str(int type)
+{
+ switch (type) {
+ case 0: return " uncached";
+ case 1: return " snooped";
+ default: return "";
+ }
+}
+
static void
describe_obj(struct seq_file *m, struct drm_i915_gem_object *obj)
{
- seq_printf(m, "%p: %s%s %8zd %04x %04x %d %d%s%s",
+ seq_printf(m, "%p: %s%s %8zd %04x %04x %d %d%s%s%s",
&obj->base,
get_pin_flag(obj),
get_tiling_flag(obj),
@@ -118,6 +127,7 @@
obj->base.write_domain,
obj->last_rendering_seqno,
obj->last_fenced_seqno,
+ agp_type_str(obj->agp_type == AGP_USER_CACHED_MEMORY),
obj->dirty ? " dirty" : "",
obj->madv == I915_MADV_DONTNEED ? " purgeable" : "");
if (obj->base.name)
@@ -276,6 +286,37 @@
return 0;
}
+static int i915_gem_gtt_info(struct seq_file *m, void* data)
+{
+ struct drm_info_node *node = (struct drm_info_node *) m->private;
+ struct drm_device *dev = node->minor->dev;
+ struct drm_i915_private *dev_priv = dev->dev_private;
+ struct drm_i915_gem_object *obj;
+ size_t total_obj_size, total_gtt_size;
+ int count, ret;
+
+ ret = mutex_lock_interruptible(&dev->struct_mutex);
+ if (ret)
+ return ret;
+
+ total_obj_size = total_gtt_size = count = 0;
+ list_for_each_entry(obj, &dev_priv->mm.gtt_list, gtt_list) {
+ seq_printf(m, " ");
+ describe_obj(m, obj);
+ seq_printf(m, "\n");
+ total_obj_size += obj->base.size;
+ total_gtt_size += obj->gtt_space->size;
+ count++;
+ }
+
+ mutex_unlock(&dev->struct_mutex);
+
+ seq_printf(m, "Total %d objects, %zu bytes, %zu GTT size\n",
+ count, total_obj_size, total_gtt_size);
+
+ return 0;
+}
+
static int i915_gem_pageflip_info(struct seq_file *m, void *data)
{
@@ -456,8 +497,14 @@
}
seq_printf(m, "Interrupts received: %d\n",
atomic_read(&dev_priv->irq_received));
- for (i = 0; i < I915_NUM_RINGS; i++)
+ for (i = 0; i < I915_NUM_RINGS; i++) {
+ if (IS_GEN6(dev)) {
+ seq_printf(m, "Graphics Interrupt mask (%s): %08x\n",
+ dev_priv->ring[i].name,
+ I915_READ_IMR(&dev_priv->ring[i]));
+ }
i915_ring_seqno_info(m, &dev_priv->ring[i]);
+ }
mutex_unlock(&dev->struct_mutex);
return 0;
@@ -656,7 +703,7 @@
seq_printf(m, "%s [%d]:\n", name, count);
while (count--) {
- seq_printf(m, " %08x %8zd %04x %04x %08x%s%s%s%s%s",
+ seq_printf(m, " %08x %8zd %04x %04x %08x%s%s%s%s%s%s",
err->gtt_offset,
err->size,
err->read_domains,
@@ -666,7 +713,8 @@
tiling_flag(err->tiling),
dirty_flag(err->dirty),
purgeable_flag(err->purgeable),
- ring_str(err->ring));
+ ring_str(err->ring),
+ agp_type_str(err->agp_type));
if (err->name)
seq_printf(m, " (name: %d)", err->name);
@@ -744,7 +792,9 @@
if (error->batchbuffer[i]) {
struct drm_i915_error_object *obj = error->batchbuffer[i];
- seq_printf(m, "--- gtt_offset = 0x%08x\n", obj->gtt_offset);
+ seq_printf(m, "%s --- gtt_offset = 0x%08x\n",
+ dev_priv->ring[i].name,
+ obj->gtt_offset);
offset = 0;
for (page = 0; page < obj->page_count; page++) {
for (elt = 0; elt < PAGE_SIZE/4; elt++) {
@@ -890,7 +940,7 @@
struct drm_device *dev = node->minor->dev;
drm_i915_private_t *dev_priv = dev->dev_private;
u32 rgvmodectl = I915_READ(MEMMODECTL);
- u32 rstdbyctl = I915_READ(MCHBAR_RENDER_STANDBY);
+ u32 rstdbyctl = I915_READ(RSTDBYCTL);
u16 crstandvid = I915_READ16(CRSTANDVID);
seq_printf(m, "HD boost: %s\n", (rgvmodectl & MEMMODE_BOOST_EN) ?
@@ -913,6 +963,30 @@
seq_printf(m, "RS2 VID: %d\n", ((crstandvid >> 8) & 0x3f));
seq_printf(m, "Render standby enabled: %s\n",
(rstdbyctl & RCX_SW_EXIT) ? "no" : "yes");
+ seq_printf(m, "Current RS state: ");
+ switch (rstdbyctl & RSX_STATUS_MASK) {
+ case RSX_STATUS_ON:
+ seq_printf(m, "on\n");
+ break;
+ case RSX_STATUS_RC1:
+ seq_printf(m, "RC1\n");
+ break;
+ case RSX_STATUS_RC1E:
+ seq_printf(m, "RC1E\n");
+ break;
+ case RSX_STATUS_RS1:
+ seq_printf(m, "RS1\n");
+ break;
+ case RSX_STATUS_RS2:
+ seq_printf(m, "RS2 (RC6)\n");
+ break;
+ case RSX_STATUS_RS3:
+ seq_printf(m, "RC3 (RC6+)\n");
+ break;
+ default:
+ seq_printf(m, "unknown\n");
+ break;
+ }
return 0;
}
@@ -1187,6 +1261,7 @@
static struct drm_info_list i915_debugfs_list[] = {
{"i915_capabilities", i915_capabilities, 0, 0},
{"i915_gem_objects", i915_gem_object_info, 0},
+ {"i915_gem_gtt", i915_gem_gtt_info, 0},
{"i915_gem_active", i915_gem_object_list_info, 0, (void *) ACTIVE_LIST},
{"i915_gem_flushing", i915_gem_object_list_info, 0, (void *) FLUSHING_LIST},
{"i915_gem_inactive", i915_gem_object_list_info, 0, (void *) INACTIVE_LIST},
diff --git a/drivers/gpu/drm/i915/i915_dma.c b/drivers/gpu/drm/i915/i915_dma.c
index 0568dbd..844f3c9 100644
--- a/drivers/gpu/drm/i915/i915_dma.c
+++ b/drivers/gpu/drm/i915/i915_dma.c
@@ -1962,13 +1962,6 @@
/* enable GEM by default */
dev_priv->has_gem = 1;
- if (dev_priv->has_gem == 0 &&
- drm_core_check_feature(dev, DRIVER_MODESET)) {
- DRM_ERROR("kernel modesetting requires GEM, disabling driver.\n");
- ret = -ENODEV;
- goto out_workqueue_free;
- }
-
dev->driver->get_vblank_counter = i915_get_vblank_counter;
dev->max_vblank_count = 0xffffff; /* only 24 bits of frame count */
if (IS_G4X(dev) || IS_GEN5(dev) || IS_GEN6(dev)) {
@@ -2055,7 +2048,6 @@
intel_teardown_gmbus(dev);
intel_teardown_mchbar(dev);
-out_workqueue_free:
destroy_workqueue(dev_priv->wq);
out_iomapfree:
io_mapping_free(dev_priv->mm.gtt_mapping);
diff --git a/drivers/gpu/drm/i915/i915_drv.c b/drivers/gpu/drm/i915/i915_drv.c
index 8724933..0de75a2 100644
--- a/drivers/gpu/drm/i915/i915_drv.c
+++ b/drivers/gpu/drm/i915/i915_drv.c
@@ -49,6 +49,9 @@
unsigned int i915_lvds_downclock = 0;
module_param_named(lvds_downclock, i915_lvds_downclock, int, 0400);
+bool i915_try_reset = true;
+module_param_named(reset, i915_try_reset, bool, 0600);
+
static struct drm_driver driver;
extern int intel_agp_enabled;
@@ -352,6 +355,9 @@
/* Resume the modeset for every activated CRTC */
drm_helper_resume_force_mode(dev);
+
+ if (dev_priv->renderctx && dev_priv->pwrctx)
+ ironlake_enable_rc6(dev);
}
intel_opregion_init(dev);
@@ -475,6 +481,9 @@
bool need_display = true;
int ret;
+ if (!i915_try_reset)
+ return 0;
+
if (!mutex_trylock(&dev->struct_mutex))
return -EBUSY;
diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h
index aac1bf3..385fc7e 100644
--- a/drivers/gpu/drm/i915/i915_drv.h
+++ b/drivers/gpu/drm/i915/i915_drv.h
@@ -172,20 +172,21 @@
int page_count;
u32 gtt_offset;
u32 *pages[0];
- } *ringbuffer, *batchbuffer[2];
+ } *ringbuffer, *batchbuffer[I915_NUM_RINGS];
struct drm_i915_error_buffer {
- size_t size;
+ u32 size;
u32 name;
u32 seqno;
u32 gtt_offset;
u32 read_domains;
u32 write_domain;
- u32 fence_reg;
+ s32 fence_reg:5;
s32 pinned:2;
u32 tiling:2;
u32 dirty:1;
u32 purgeable:1;
u32 ring:4;
+ u32 agp_type:1;
} *active_bo, *pinned_bo;
u32 active_bo_count, pinned_bo_count;
struct intel_overlay_error_state *overlay;
@@ -332,6 +333,7 @@
/* LVDS info */
int backlight_level; /* restore backlight to this value */
+ bool backlight_enabled;
struct drm_display_mode *panel_fixed_mode;
struct drm_display_mode *lfp_lvds_vbt_mode; /* if any */
struct drm_display_mode *sdvo_lvds_vbt_mode; /* if any */
@@ -794,6 +796,7 @@
*/
struct hlist_node exec_node;
unsigned long exec_handle;
+ struct drm_i915_gem_exec_object2 *exec_entry;
/**
* Current offset of the object in GTT space.
@@ -1006,12 +1009,6 @@
extern u32 gm45_get_vblank_counter(struct drm_device *dev, int crtc);
extern int i915_vblank_swap(struct drm_device *dev, void *data,
struct drm_file *file_priv);
-extern void i915_enable_irq(drm_i915_private_t *dev_priv, u32 mask);
-extern void i915_disable_irq(drm_i915_private_t *dev_priv, u32 mask);
-extern void ironlake_enable_graphics_irq(drm_i915_private_t *dev_priv,
- u32 mask);
-extern void ironlake_disable_graphics_irq(drm_i915_private_t *dev_priv,
- u32 mask);
void
i915_enable_pipestat(drm_i915_private_t *dev_priv, int pipe, u32 mask);
@@ -1091,10 +1088,10 @@
struct drm_file *file_priv);
void i915_gem_load(struct drm_device *dev);
int i915_gem_init_object(struct drm_gem_object *obj);
-void i915_gem_flush_ring(struct drm_device *dev,
- struct intel_ring_buffer *ring,
- uint32_t invalidate_domains,
- uint32_t flush_domains);
+int __must_check i915_gem_flush_ring(struct drm_device *dev,
+ struct intel_ring_buffer *ring,
+ uint32_t invalidate_domains,
+ uint32_t flush_domains);
struct drm_i915_gem_object *i915_gem_alloc_object(struct drm_device *dev,
size_t size);
void i915_gem_free_object(struct drm_gem_object *obj);
@@ -1265,6 +1262,7 @@
extern void intel_enable_fbc(struct drm_crtc *crtc, unsigned long interval);
extern bool intel_fbc_enabled(struct drm_device *dev);
extern bool ironlake_set_drps(struct drm_device *dev, u8 val);
+extern void ironlake_enable_rc6(struct drm_device *dev);
extern void gen6_set_rps(struct drm_device *dev, u8 val);
extern void intel_detect_pch (struct drm_device *dev);
extern int intel_trans_dp_port_sel (struct drm_crtc *crtc);
diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c
index c79c0b6..3dfc848 100644
--- a/drivers/gpu/drm/i915/i915_gem.c
+++ b/drivers/gpu/drm/i915/i915_gem.c
@@ -35,18 +35,18 @@
#include <linux/swap.h>
#include <linux/pci.h>
-static void i915_gem_object_flush_gpu_write_domain(struct drm_i915_gem_object *obj);
+static __must_check int i915_gem_object_flush_gpu_write_domain(struct drm_i915_gem_object *obj);
static void i915_gem_object_flush_gtt_write_domain(struct drm_i915_gem_object *obj);
static void i915_gem_object_flush_cpu_write_domain(struct drm_i915_gem_object *obj);
-static int i915_gem_object_set_to_cpu_domain(struct drm_i915_gem_object *obj,
- bool write);
-static int i915_gem_object_set_cpu_read_domain_range(struct drm_i915_gem_object *obj,
- uint64_t offset,
- uint64_t size);
+static __must_check int i915_gem_object_set_to_cpu_domain(struct drm_i915_gem_object *obj,
+ bool write);
+static __must_check int i915_gem_object_set_cpu_read_domain_range(struct drm_i915_gem_object *obj,
+ uint64_t offset,
+ uint64_t size);
static void i915_gem_object_set_to_full_cpu_read_domain(struct drm_i915_gem_object *obj);
-static int i915_gem_object_bind_to_gtt(struct drm_i915_gem_object *obj,
- unsigned alignment,
- bool map_and_fenceable);
+static __must_check int i915_gem_object_bind_to_gtt(struct drm_i915_gem_object *obj,
+ unsigned alignment,
+ bool map_and_fenceable);
static void i915_gem_clear_fence_reg(struct drm_device *dev,
struct drm_i915_fence_reg *reg);
static int i915_gem_phys_pwrite(struct drm_device *dev,
@@ -1935,6 +1935,8 @@
{
drm_i915_private_t *dev_priv;
struct drm_device *dev;
+ bool idle;
+ int i;
dev_priv = container_of(work, drm_i915_private_t,
mm.retire_work.work);
@@ -1948,11 +1950,31 @@
i915_gem_retire_requests(dev);
- if (!dev_priv->mm.suspended &&
- (!list_empty(&dev_priv->ring[RCS].request_list) ||
- !list_empty(&dev_priv->ring[VCS].request_list) ||
- !list_empty(&dev_priv->ring[BCS].request_list)))
+ /* Send a periodic flush down the ring so we don't hold onto GEM
+ * objects indefinitely.
+ */
+ idle = true;
+ for (i = 0; i < I915_NUM_RINGS; i++) {
+ struct intel_ring_buffer *ring = &dev_priv->ring[i];
+
+ if (!list_empty(&ring->gpu_write_list)) {
+ struct drm_i915_gem_request *request;
+ int ret;
+
+ ret = i915_gem_flush_ring(dev, ring, 0,
+ I915_GEM_GPU_DOMAINS);
+ request = kzalloc(sizeof(*request), GFP_KERNEL);
+ if (ret || request == NULL ||
+ i915_add_request(dev, NULL, request, ring))
+ kfree(request);
+ }
+
+ idle &= list_empty(&ring->request_list);
+ }
+
+ if (!dev_priv->mm.suspended && !idle)
queue_delayed_work(dev_priv->wq, &dev_priv->mm.retire_work, HZ);
+
mutex_unlock(&dev->struct_mutex);
}
@@ -2142,25 +2164,37 @@
return ret;
}
-void
+int
i915_gem_flush_ring(struct drm_device *dev,
struct intel_ring_buffer *ring,
uint32_t invalidate_domains,
uint32_t flush_domains)
{
- ring->flush(ring, invalidate_domains, flush_domains);
+ int ret;
+
+ ret = ring->flush(ring, invalidate_domains, flush_domains);
+ if (ret)
+ return ret;
+
i915_gem_process_flushing_list(dev, flush_domains, ring);
+ return 0;
}
static int i915_ring_idle(struct drm_device *dev,
struct intel_ring_buffer *ring)
{
+ int ret;
+
if (list_empty(&ring->gpu_write_list) && list_empty(&ring->active_list))
return 0;
- if (!list_empty(&ring->gpu_write_list))
- i915_gem_flush_ring(dev, ring,
+ if (!list_empty(&ring->gpu_write_list)) {
+ ret = i915_gem_flush_ring(dev, ring,
I915_GEM_GPU_DOMAINS, I915_GEM_GPU_DOMAINS);
+ if (ret)
+ return ret;
+ }
+
return i915_wait_request(dev,
i915_gem_next_request_seqno(dev, ring),
ring);
@@ -2370,10 +2404,13 @@
int ret;
if (obj->fenced_gpu_access) {
- if (obj->base.write_domain & I915_GEM_GPU_DOMAINS)
- i915_gem_flush_ring(obj->base.dev,
- obj->last_fenced_ring,
- 0, obj->base.write_domain);
+ if (obj->base.write_domain & I915_GEM_GPU_DOMAINS) {
+ ret = i915_gem_flush_ring(obj->base.dev,
+ obj->last_fenced_ring,
+ 0, obj->base.write_domain);
+ if (ret)
+ return ret;
+ }
obj->fenced_gpu_access = false;
}
@@ -2393,6 +2430,12 @@
obj->last_fenced_ring = NULL;
}
+ /* Ensure that all CPU reads are completed before installing a fence
+ * and all writes before removing the fence.
+ */
+ if (obj->base.read_domains & I915_GEM_DOMAIN_GTT)
+ mb();
+
return 0;
}
@@ -2523,9 +2566,12 @@
return ret;
} else if (obj->tiling_changed) {
if (obj->fenced_gpu_access) {
- if (obj->base.write_domain & I915_GEM_GPU_DOMAINS)
- i915_gem_flush_ring(obj->base.dev, obj->ring,
- 0, obj->base.write_domain);
+ if (obj->base.write_domain & I915_GEM_GPU_DOMAINS) {
+ ret = i915_gem_flush_ring(obj->base.dev, obj->ring,
+ 0, obj->base.write_domain);
+ if (ret)
+ return ret;
+ }
obj->fenced_gpu_access = false;
}
@@ -2736,10 +2782,8 @@
obj->gtt_space = NULL;
if (ret == -ENOMEM) {
- /* first try to clear up some space from the GTT */
- ret = i915_gem_evict_something(dev, size,
- alignment,
- map_and_fenceable);
+ /* first try to reclaim some memory by clearing the GTT */
+ ret = i915_gem_evict_everything(dev, false);
if (ret) {
/* now try to shrink everyone else */
if (gfpmask) {
@@ -2747,7 +2791,7 @@
goto search_free;
}
- return ret;
+ return -ENOMEM;
}
goto search_free;
@@ -2762,9 +2806,7 @@
drm_mm_put_block(obj->gtt_space);
obj->gtt_space = NULL;
- ret = i915_gem_evict_something(dev, size,
- alignment, map_and_fenceable);
- if (ret)
+ if (i915_gem_evict_everything(dev, false))
return ret;
goto search_free;
@@ -2811,17 +2853,16 @@
}
/** Flushes any GPU write domain for the object if it's dirty. */
-static void
+static int
i915_gem_object_flush_gpu_write_domain(struct drm_i915_gem_object *obj)
{
struct drm_device *dev = obj->base.dev;
if ((obj->base.write_domain & I915_GEM_GPU_DOMAINS) == 0)
- return;
+ return 0;
/* Queue the GPU write cache flushing we need. */
- i915_gem_flush_ring(dev, obj->ring, 0, obj->base.write_domain);
- BUG_ON(obj->base.write_domain);
+ return i915_gem_flush_ring(dev, obj->ring, 0, obj->base.write_domain);
}
/** Flushes the GTT write domain for the object if it's dirty. */
@@ -2833,10 +2874,16 @@
if (obj->base.write_domain != I915_GEM_DOMAIN_GTT)
return;
- /* No actual flushing is required for the GTT write domain. Writes
+ /* No actual flushing is required for the GTT write domain. Writes
* to it immediately go to main memory as far as we know, so there's
* no chipset flush. It also doesn't land in render cache.
+ *
+ * However, we do have to enforce the order so that all writes through
+ * the GTT land before any writes to the device, such as updates to
+ * the GATT itself.
*/
+ wmb();
+
i915_gem_release_mmap(obj);
old_write_domain = obj->base.write_domain;
@@ -2882,7 +2929,10 @@
if (obj->gtt_space == NULL)
return -EINVAL;
- i915_gem_object_flush_gpu_write_domain(obj);
+ ret = i915_gem_object_flush_gpu_write_domain(obj);
+ if (ret)
+ return ret;
+
if (obj->pending_gpu_write || write) {
ret = i915_gem_object_wait_rendering(obj, true);
if (ret)
@@ -2927,7 +2977,10 @@
if (obj->gtt_space == NULL)
return -EINVAL;
- i915_gem_object_flush_gpu_write_domain(obj);
+ ret = i915_gem_object_flush_gpu_write_domain(obj);
+ if (ret)
+ return ret;
+
/* Currently, we are always called from an non-interruptible context. */
if (pipelined != obj->ring) {
@@ -2952,12 +3005,17 @@
i915_gem_object_flush_gpu(struct drm_i915_gem_object *obj,
bool interruptible)
{
+ int ret;
+
if (!obj->active)
return 0;
- if (obj->base.write_domain & I915_GEM_GPU_DOMAINS)
- i915_gem_flush_ring(obj->base.dev, obj->ring,
- 0, obj->base.write_domain);
+ if (obj->base.write_domain & I915_GEM_GPU_DOMAINS) {
+ ret = i915_gem_flush_ring(obj->base.dev, obj->ring,
+ 0, obj->base.write_domain);
+ if (ret)
+ return ret;
+ }
return i915_gem_object_wait_rendering(obj, interruptible);
}
@@ -2974,7 +3032,10 @@
uint32_t old_write_domain, old_read_domains;
int ret;
- i915_gem_object_flush_gpu_write_domain(obj);
+ ret = i915_gem_object_flush_gpu_write_domain(obj);
+ if (ret)
+ return ret;
+
ret = i915_gem_object_wait_rendering(obj, true);
if (ret)
return ret;
@@ -3069,7 +3130,10 @@
if (offset == 0 && size == obj->base.size)
return i915_gem_object_set_to_cpu_domain(obj, 0);
- i915_gem_object_flush_gpu_write_domain(obj);
+ ret = i915_gem_object_flush_gpu_write_domain(obj);
+ if (ret)
+ return ret;
+
ret = i915_gem_object_wait_rendering(obj, true);
if (ret)
return ret;
@@ -3362,8 +3426,8 @@
* flush earlier is beneficial.
*/
if (obj->base.write_domain & I915_GEM_GPU_DOMAINS) {
- i915_gem_flush_ring(dev, obj->ring,
- 0, obj->base.write_domain);
+ ret = i915_gem_flush_ring(dev, obj->ring,
+ 0, obj->base.write_domain);
} else if (obj->ring->outstanding_lazy_request ==
obj->last_rendering_seqno) {
struct drm_i915_gem_request *request;
diff --git a/drivers/gpu/drm/i915/i915_gem_evict.c b/drivers/gpu/drm/i915/i915_gem_evict.c
index 78b8cf9..3d39005 100644
--- a/drivers/gpu/drm/i915/i915_gem_evict.c
+++ b/drivers/gpu/drm/i915/i915_gem_evict.c
@@ -127,9 +127,15 @@
}
/* Nothing found, clean up and bail out! */
- list_for_each_entry(obj, &unwind_list, exec_list) {
+ while (!list_empty(&unwind_list)) {
+ obj = list_first_entry(&unwind_list,
+ struct drm_i915_gem_object,
+ exec_list);
+
ret = drm_mm_scan_remove_block(obj->gtt_space);
BUG_ON(ret);
+
+ list_del_init(&obj->exec_list);
drm_gem_object_unreference(&obj->base);
}
@@ -162,6 +168,7 @@
exec_list);
if (ret == 0)
ret = i915_gem_object_unbind(obj);
+
list_del_init(&obj->exec_list);
drm_gem_object_unreference(&obj->base);
}
diff --git a/drivers/gpu/drm/i915/i915_gem_execbuffer.c b/drivers/gpu/drm/i915/i915_gem_execbuffer.c
index 61129e6..e698343 100644
--- a/drivers/gpu/drm/i915/i915_gem_execbuffer.c
+++ b/drivers/gpu/drm/i915/i915_gem_execbuffer.c
@@ -268,7 +268,6 @@
static int
i915_gem_execbuffer_relocate_entry(struct drm_i915_gem_object *obj,
struct eb_objects *eb,
- struct drm_i915_gem_exec_object2 *entry,
struct drm_i915_gem_relocation_entry *reloc)
{
struct drm_device *dev = obj->base.dev;
@@ -411,10 +410,10 @@
static int
i915_gem_execbuffer_relocate_object(struct drm_i915_gem_object *obj,
- struct eb_objects *eb,
- struct drm_i915_gem_exec_object2 *entry)
+ struct eb_objects *eb)
{
struct drm_i915_gem_relocation_entry __user *user_relocs;
+ struct drm_i915_gem_exec_object2 *entry = obj->exec_entry;
int i, ret;
user_relocs = (void __user *)(uintptr_t)entry->relocs_ptr;
@@ -426,7 +425,7 @@
sizeof(reloc)))
return -EFAULT;
- ret = i915_gem_execbuffer_relocate_entry(obj, eb, entry, &reloc);
+ ret = i915_gem_execbuffer_relocate_entry(obj, eb, &reloc);
if (ret)
return ret;
@@ -442,13 +441,13 @@
static int
i915_gem_execbuffer_relocate_object_slow(struct drm_i915_gem_object *obj,
struct eb_objects *eb,
- struct drm_i915_gem_exec_object2 *entry,
struct drm_i915_gem_relocation_entry *relocs)
{
+ const struct drm_i915_gem_exec_object2 *entry = obj->exec_entry;
int i, ret;
for (i = 0; i < entry->relocation_count; i++) {
- ret = i915_gem_execbuffer_relocate_entry(obj, eb, entry, &relocs[i]);
+ ret = i915_gem_execbuffer_relocate_entry(obj, eb, &relocs[i]);
if (ret)
return ret;
}
@@ -459,8 +458,7 @@
static int
i915_gem_execbuffer_relocate(struct drm_device *dev,
struct eb_objects *eb,
- struct list_head *objects,
- struct drm_i915_gem_exec_object2 *exec)
+ struct list_head *objects)
{
struct drm_i915_gem_object *obj;
int ret;
@@ -468,7 +466,7 @@
list_for_each_entry(obj, objects, exec_list) {
obj->base.pending_read_domains = 0;
obj->base.pending_write_domain = 0;
- ret = i915_gem_execbuffer_relocate_object(obj, eb, exec++);
+ ret = i915_gem_execbuffer_relocate_object(obj, eb);
if (ret)
return ret;
}
@@ -479,13 +477,36 @@
static int
i915_gem_execbuffer_reserve(struct intel_ring_buffer *ring,
struct drm_file *file,
- struct list_head *objects,
- struct drm_i915_gem_exec_object2 *exec)
+ struct list_head *objects)
{
struct drm_i915_gem_object *obj;
- struct drm_i915_gem_exec_object2 *entry;
int ret, retry;
bool has_fenced_gpu_access = INTEL_INFO(ring->dev)->gen < 4;
+ struct list_head ordered_objects;
+
+ INIT_LIST_HEAD(&ordered_objects);
+ while (!list_empty(objects)) {
+ struct drm_i915_gem_exec_object2 *entry;
+ bool need_fence, need_mappable;
+
+ obj = list_first_entry(objects,
+ struct drm_i915_gem_object,
+ exec_list);
+ entry = obj->exec_entry;
+
+ need_fence =
+ has_fenced_gpu_access &&
+ entry->flags & EXEC_OBJECT_NEEDS_FENCE &&
+ obj->tiling_mode != I915_TILING_NONE;
+ need_mappable =
+ entry->relocation_count ? true : need_fence;
+
+ if (need_mappable)
+ list_move(&obj->exec_list, &ordered_objects);
+ else
+ list_move_tail(&obj->exec_list, &ordered_objects);
+ }
+ list_splice(&ordered_objects, objects);
/* Attempt to pin all of the buffers into the GTT.
* This is done in 3 phases:
@@ -504,14 +525,11 @@
ret = 0;
/* Unbind any ill-fitting objects or pin. */
- entry = exec;
list_for_each_entry(obj, objects, exec_list) {
+ struct drm_i915_gem_exec_object2 *entry = obj->exec_entry;
bool need_fence, need_mappable;
-
- if (!obj->gtt_space) {
- entry++;
+ if (!obj->gtt_space)
continue;
- }
need_fence =
has_fenced_gpu_access &&
@@ -534,8 +552,8 @@
}
/* Bind fresh objects */
- entry = exec;
list_for_each_entry(obj, objects, exec_list) {
+ struct drm_i915_gem_exec_object2 *entry = obj->exec_entry;
bool need_fence;
need_fence =
@@ -570,7 +588,6 @@
}
entry->offset = obj->gtt_offset;
- entry++;
}
/* Decrement pin count for bound objects */
@@ -622,7 +639,7 @@
int i, total, ret;
/* We may process another execbuffer during the unlock... */
- while (list_empty(objects)) {
+ while (!list_empty(objects)) {
obj = list_first_entry(objects,
struct drm_i915_gem_object,
exec_list);
@@ -665,7 +682,6 @@
}
/* reacquire the objects */
- INIT_LIST_HEAD(objects);
eb_reset(eb);
for (i = 0; i < count; i++) {
struct drm_i915_gem_object *obj;
@@ -681,10 +697,11 @@
list_add_tail(&obj->exec_list, objects);
obj->exec_handle = exec[i].handle;
+ obj->exec_entry = &exec[i];
eb_add_object(eb, obj);
}
- ret = i915_gem_execbuffer_reserve(ring, file, objects, exec);
+ ret = i915_gem_execbuffer_reserve(ring, file, objects);
if (ret)
goto err;
@@ -693,7 +710,6 @@
obj->base.pending_read_domains = 0;
obj->base.pending_write_domain = 0;
ret = i915_gem_execbuffer_relocate_object_slow(obj, eb,
- exec,
reloc + total);
if (ret)
goto err;
@@ -713,25 +729,34 @@
return ret;
}
-static void
+static int
i915_gem_execbuffer_flush(struct drm_device *dev,
uint32_t invalidate_domains,
uint32_t flush_domains,
uint32_t flush_rings)
{
drm_i915_private_t *dev_priv = dev->dev_private;
- int i;
+ int i, ret;
if (flush_domains & I915_GEM_DOMAIN_CPU)
intel_gtt_chipset_flush();
+ if (flush_domains & I915_GEM_DOMAIN_GTT)
+ wmb();
+
if ((flush_domains | invalidate_domains) & I915_GEM_GPU_DOMAINS) {
for (i = 0; i < I915_NUM_RINGS; i++)
- if (flush_rings & (1 << i))
- i915_gem_flush_ring(dev, &dev_priv->ring[i],
- invalidate_domains,
- flush_domains);
+ if (flush_rings & (1 << i)) {
+ ret = i915_gem_flush_ring(dev,
+ &dev_priv->ring[i],
+ invalidate_domains,
+ flush_domains);
+ if (ret)
+ return ret;
+ }
}
+
+ return 0;
}
static int
@@ -795,10 +820,12 @@
cd.invalidate_domains,
cd.flush_domains);
#endif
- i915_gem_execbuffer_flush(ring->dev,
- cd.invalidate_domains,
- cd.flush_domains,
- cd.flush_rings);
+ ret = i915_gem_execbuffer_flush(ring->dev,
+ cd.invalidate_domains,
+ cd.flush_domains,
+ cd.flush_rings);
+ if (ret)
+ return ret;
}
list_for_each_entry(obj, objects, exec_list) {
@@ -921,7 +948,7 @@
struct intel_ring_buffer *ring)
{
struct drm_i915_gem_request *request;
- u32 flush_domains;
+ u32 invalidate;
/*
* Ensure that the commands in the batch buffer are
@@ -929,11 +956,13 @@
*
* The sampler always gets flushed on i965 (sigh).
*/
- flush_domains = 0;
+ invalidate = I915_GEM_DOMAIN_COMMAND;
if (INTEL_INFO(dev)->gen >= 4)
- flush_domains |= I915_GEM_DOMAIN_SAMPLER;
-
- ring->flush(ring, I915_GEM_DOMAIN_COMMAND, flush_domains);
+ invalidate |= I915_GEM_DOMAIN_SAMPLER;
+ if (ring->flush(ring, invalidate, 0)) {
+ i915_gem_next_request_seqno(dev, ring);
+ return;
+ }
/* Add a breadcrumb for the completion of the batch buffer */
request = kzalloc(sizeof(*request), GFP_KERNEL);
@@ -1098,16 +1127,22 @@
list_add_tail(&obj->exec_list, &objects);
obj->exec_handle = exec[i].handle;
+ obj->exec_entry = &exec[i];
eb_add_object(eb, obj);
}
+ /* take note of the batch buffer before we might reorder the lists */
+ batch_obj = list_entry(objects.prev,
+ struct drm_i915_gem_object,
+ exec_list);
+
/* Move the objects en-masse into the GTT, evicting if necessary. */
- ret = i915_gem_execbuffer_reserve(ring, file, &objects, exec);
+ ret = i915_gem_execbuffer_reserve(ring, file, &objects);
if (ret)
goto err;
/* The objects are in their final locations, apply the relocations. */
- ret = i915_gem_execbuffer_relocate(dev, eb, &objects, exec);
+ ret = i915_gem_execbuffer_relocate(dev, eb, &objects);
if (ret) {
if (ret == -EFAULT) {
ret = i915_gem_execbuffer_relocate_slow(dev, file, ring,
@@ -1121,9 +1156,6 @@
}
/* Set the pending read domains for the batch buffer to COMMAND */
- batch_obj = list_entry(objects.prev,
- struct drm_i915_gem_object,
- exec_list);
if (batch_obj->base.pending_write_domain) {
DRM_ERROR("Attempting to use self-modifying batch buffer\n");
ret = -EINVAL;
@@ -1340,4 +1372,3 @@
drm_free_large(exec2_list);
return ret;
}
-
diff --git a/drivers/gpu/drm/i915/i915_gem_gtt.c b/drivers/gpu/drm/i915/i915_gem_gtt.c
index 86673e7..70433ae 100644
--- a/drivers/gpu/drm/i915/i915_gem_gtt.c
+++ b/drivers/gpu/drm/i915/i915_gem_gtt.c
@@ -85,15 +85,11 @@
void i915_gem_gtt_unbind_object(struct drm_i915_gem_object *obj)
{
- struct drm_device *dev = obj->base.dev;
- struct drm_i915_private *dev_priv = dev->dev_private;
-
- if (dev_priv->mm.gtt->needs_dmar) {
- intel_gtt_unmap_memory(obj->sg_list, obj->num_sg);
- obj->sg_list = NULL;
- obj->num_sg = 0;
- }
-
intel_gtt_clear_range(obj->gtt_space->start >> PAGE_SHIFT,
obj->base.size >> PAGE_SHIFT);
+
+ if (obj->sg_list) {
+ intel_gtt_unmap_memory(obj->sg_list, obj->num_sg);
+ obj->sg_list = NULL;
+ }
}
diff --git a/drivers/gpu/drm/i915/i915_irq.c b/drivers/gpu/drm/i915/i915_irq.c
index 0dadc02..e418e8b 100644
--- a/drivers/gpu/drm/i915/i915_irq.c
+++ b/drivers/gpu/drm/i915/i915_irq.c
@@ -64,26 +64,6 @@
#define DRM_I915_VBLANK_PIPE_ALL (DRM_I915_VBLANK_PIPE_A | \
DRM_I915_VBLANK_PIPE_B)
-void
-ironlake_enable_graphics_irq(drm_i915_private_t *dev_priv, u32 mask)
-{
- if ((dev_priv->gt_irq_mask & mask) != 0) {
- dev_priv->gt_irq_mask &= ~mask;
- I915_WRITE(GTIMR, dev_priv->gt_irq_mask);
- POSTING_READ(GTIMR);
- }
-}
-
-void
-ironlake_disable_graphics_irq(drm_i915_private_t *dev_priv, u32 mask)
-{
- if ((dev_priv->gt_irq_mask & mask) != mask) {
- dev_priv->gt_irq_mask |= mask;
- I915_WRITE(GTIMR, dev_priv->gt_irq_mask);
- POSTING_READ(GTIMR);
- }
-}
-
/* For display hotplug interrupt */
static void
ironlake_enable_display_irq(drm_i915_private_t *dev_priv, u32 mask)
@@ -105,26 +85,6 @@
}
}
-void
-i915_enable_irq(drm_i915_private_t *dev_priv, u32 mask)
-{
- if ((dev_priv->irq_mask & mask) != 0) {
- dev_priv->irq_mask &= ~mask;
- I915_WRITE(IMR, dev_priv->irq_mask);
- POSTING_READ(IMR);
- }
-}
-
-void
-i915_disable_irq(drm_i915_private_t *dev_priv, u32 mask)
-{
- if ((dev_priv->irq_mask & mask) != mask) {
- dev_priv->irq_mask |= mask;
- I915_WRITE(IMR, dev_priv->irq_mask);
- POSTING_READ(IMR);
- }
-}
-
static inline u32
i915_pipestat(int pipe)
{
@@ -389,9 +349,12 @@
{
struct drm_i915_private *dev_priv = dev->dev_private;
u32 seqno = ring->get_seqno(ring);
- ring->irq_seqno = seqno;
+
trace_i915_gem_request_complete(dev, seqno);
+
+ ring->irq_seqno = seqno;
wake_up_all(&ring->irq_queue);
+
dev_priv->hangcheck_count = 0;
mod_timer(&dev_priv->hangcheck_timer,
jiffies + msecs_to_jiffies(DRM_I915_HANGCHECK_PERIOD));
@@ -435,6 +398,50 @@
I915_WRITE(GEN6_PMIIR, pm_iir);
}
+static void pch_irq_handler(struct drm_device *dev)
+{
+ drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private;
+ u32 pch_iir;
+
+ pch_iir = I915_READ(SDEIIR);
+
+ if (pch_iir & SDE_AUDIO_POWER_MASK)
+ DRM_DEBUG_DRIVER("PCH audio power change on port %d\n",
+ (pch_iir & SDE_AUDIO_POWER_MASK) >>
+ SDE_AUDIO_POWER_SHIFT);
+
+ if (pch_iir & SDE_GMBUS)
+ DRM_DEBUG_DRIVER("PCH GMBUS interrupt\n");
+
+ if (pch_iir & SDE_AUDIO_HDCP_MASK)
+ DRM_DEBUG_DRIVER("PCH HDCP audio interrupt\n");
+
+ if (pch_iir & SDE_AUDIO_TRANS_MASK)
+ DRM_DEBUG_DRIVER("PCH transcoder audio interrupt\n");
+
+ if (pch_iir & SDE_POISON)
+ DRM_ERROR("PCH poison interrupt\n");
+
+ if (pch_iir & SDE_FDI_MASK) {
+ u32 fdia, fdib;
+
+ fdia = I915_READ(FDI_RXA_IIR);
+ fdib = I915_READ(FDI_RXB_IIR);
+ DRM_DEBUG_DRIVER("PCH FDI RX interrupt; FDI RXA IIR: 0x%08x, FDI RXB IIR: 0x%08x\n", fdia, fdib);
+ }
+
+ if (pch_iir & (SDE_TRANSB_CRC_DONE | SDE_TRANSA_CRC_DONE))
+ DRM_DEBUG_DRIVER("PCH transcoder CRC done interrupt\n");
+
+ if (pch_iir & (SDE_TRANSB_CRC_ERR | SDE_TRANSA_CRC_ERR))
+ DRM_DEBUG_DRIVER("PCH transcoder CRC error interrupt\n");
+
+ if (pch_iir & SDE_TRANSB_FIFO_UNDER)
+ DRM_DEBUG_DRIVER("PCH transcoder B underrun interrupt\n");
+ if (pch_iir & SDE_TRANSA_FIFO_UNDER)
+ DRM_DEBUG_DRIVER("PCH transcoder A underrun interrupt\n");
+}
+
static irqreturn_t ironlake_irq_handler(struct drm_device *dev)
{
drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private;
@@ -502,8 +509,11 @@
drm_handle_vblank(dev, 1);
/* check event from PCH */
- if ((de_iir & DE_PCH_EVENT) && (pch_iir & hotplug_mask))
- queue_work(dev_priv->wq, &dev_priv->hotplug_work);
+ if (de_iir & DE_PCH_EVENT) {
+ if (pch_iir & hotplug_mask)
+ queue_work(dev_priv->wq, &dev_priv->hotplug_work);
+ pch_irq_handler(dev);
+ }
if (de_iir & DE_PCU_EVENT) {
I915_WRITE16(MEMINTRSTS, I915_READ(MEMINTRSTS));
@@ -556,10 +566,9 @@
#ifdef CONFIG_DEBUG_FS
static struct drm_i915_error_object *
-i915_error_object_create(struct drm_device *dev,
+i915_error_object_create(struct drm_i915_private *dev_priv,
struct drm_i915_gem_object *src)
{
- drm_i915_private_t *dev_priv = dev->dev_private;
struct drm_i915_error_object *dst;
int page, page_count;
u32 reloc_offset;
@@ -632,52 +641,6 @@
kfree(error);
}
-static u32
-i915_get_bbaddr(struct drm_device *dev, u32 *ring)
-{
- u32 cmd;
-
- if (IS_I830(dev) || IS_845G(dev))
- cmd = MI_BATCH_BUFFER;
- else if (INTEL_INFO(dev)->gen >= 4)
- cmd = (MI_BATCH_BUFFER_START | (2 << 6) |
- MI_BATCH_NON_SECURE_I965);
- else
- cmd = (MI_BATCH_BUFFER_START | (2 << 6));
-
- return ring[0] == cmd ? ring[1] : 0;
-}
-
-static u32
-i915_ringbuffer_last_batch(struct drm_device *dev,
- struct intel_ring_buffer *ring)
-{
- struct drm_i915_private *dev_priv = dev->dev_private;
- u32 head, bbaddr;
- u32 *val;
-
- /* Locate the current position in the ringbuffer and walk back
- * to find the most recently dispatched batch buffer.
- */
- head = I915_READ_HEAD(ring) & HEAD_ADDR;
-
- val = (u32 *)(ring->virtual_start + head);
- while (--val >= (u32 *)ring->virtual_start) {
- bbaddr = i915_get_bbaddr(dev, val);
- if (bbaddr)
- return bbaddr;
- }
-
- val = (u32 *)(ring->virtual_start + ring->size);
- while (--val >= (u32 *)ring->virtual_start) {
- bbaddr = i915_get_bbaddr(dev, val);
- if (bbaddr)
- return bbaddr;
- }
-
- return 0;
-}
-
static u32 capture_bo_list(struct drm_i915_error_buffer *err,
int count,
struct list_head *head)
@@ -702,6 +665,7 @@
err->dirty = obj->dirty;
err->purgeable = obj->madv != I915_MADV_WILLNEED;
err->ring = obj->ring ? obj->ring->id : 0;
+ err->agp_type = obj->agp_type == AGP_USER_CACHED_MEMORY;
if (++i == count)
break;
@@ -741,6 +705,36 @@
}
}
+static struct drm_i915_error_object *
+i915_error_first_batchbuffer(struct drm_i915_private *dev_priv,
+ struct intel_ring_buffer *ring)
+{
+ struct drm_i915_gem_object *obj;
+ u32 seqno;
+
+ if (!ring->get_seqno)
+ return NULL;
+
+ seqno = ring->get_seqno(ring);
+ list_for_each_entry(obj, &dev_priv->mm.active_list, mm_list) {
+ if (obj->ring != ring)
+ continue;
+
+ if (!i915_seqno_passed(obj->last_rendering_seqno, seqno))
+ continue;
+
+ if ((obj->base.read_domains & I915_GEM_DOMAIN_COMMAND) == 0)
+ continue;
+
+ /* We need to copy these to an anonymous buffer as the simplest
+ * method to avoid being overwritten by userspace.
+ */
+ return i915_error_object_create(dev_priv, obj);
+ }
+
+ return NULL;
+}
+
/**
* i915_capture_error_state - capture an error record for later analysis
* @dev: drm device
@@ -755,10 +749,8 @@
struct drm_i915_private *dev_priv = dev->dev_private;
struct drm_i915_gem_object *obj;
struct drm_i915_error_state *error;
- struct drm_i915_gem_object *batchbuffer[2];
unsigned long flags;
- u32 bbaddr;
- int count;
+ int i;
spin_lock_irqsave(&dev_priv->error_lock, flags);
error = dev_priv->first_error;
@@ -817,83 +809,30 @@
}
i915_gem_record_fences(dev, error);
- bbaddr = i915_ringbuffer_last_batch(dev, &dev_priv->ring[RCS]);
-
- /* Grab the current batchbuffer, most likely to have crashed. */
- batchbuffer[0] = NULL;
- batchbuffer[1] = NULL;
- count = 0;
- list_for_each_entry(obj, &dev_priv->mm.active_list, mm_list) {
- if (batchbuffer[0] == NULL &&
- bbaddr >= obj->gtt_offset &&
- bbaddr < obj->gtt_offset + obj->base.size)
- batchbuffer[0] = obj;
-
- if (batchbuffer[1] == NULL &&
- error->acthd >= obj->gtt_offset &&
- error->acthd < obj->gtt_offset + obj->base.size)
- batchbuffer[1] = obj;
-
- count++;
- }
- /* Scan the other lists for completeness for those bizarre errors. */
- if (batchbuffer[0] == NULL || batchbuffer[1] == NULL) {
- list_for_each_entry(obj, &dev_priv->mm.flushing_list, mm_list) {
- if (batchbuffer[0] == NULL &&
- bbaddr >= obj->gtt_offset &&
- bbaddr < obj->gtt_offset + obj->base.size)
- batchbuffer[0] = obj;
-
- if (batchbuffer[1] == NULL &&
- error->acthd >= obj->gtt_offset &&
- error->acthd < obj->gtt_offset + obj->base.size)
- batchbuffer[1] = obj;
-
- if (batchbuffer[0] && batchbuffer[1])
- break;
- }
- }
- if (batchbuffer[0] == NULL || batchbuffer[1] == NULL) {
- list_for_each_entry(obj, &dev_priv->mm.inactive_list, mm_list) {
- if (batchbuffer[0] == NULL &&
- bbaddr >= obj->gtt_offset &&
- bbaddr < obj->gtt_offset + obj->base.size)
- batchbuffer[0] = obj;
-
- if (batchbuffer[1] == NULL &&
- error->acthd >= obj->gtt_offset &&
- error->acthd < obj->gtt_offset + obj->base.size)
- batchbuffer[1] = obj;
-
- if (batchbuffer[0] && batchbuffer[1])
- break;
- }
- }
-
- /* We need to copy these to an anonymous buffer as the simplest
- * method to avoid being overwritten by userspace.
- */
- error->batchbuffer[0] = i915_error_object_create(dev, batchbuffer[0]);
- if (batchbuffer[1] != batchbuffer[0])
- error->batchbuffer[1] = i915_error_object_create(dev, batchbuffer[1]);
- else
- error->batchbuffer[1] = NULL;
+ /* Record the active batchbuffers */
+ for (i = 0; i < I915_NUM_RINGS; i++)
+ error->batchbuffer[i] =
+ i915_error_first_batchbuffer(dev_priv,
+ &dev_priv->ring[i]);
/* Record the ringbuffer */
- error->ringbuffer = i915_error_object_create(dev,
+ error->ringbuffer = i915_error_object_create(dev_priv,
dev_priv->ring[RCS].obj);
/* Record buffers on the active and pinned lists. */
error->active_bo = NULL;
error->pinned_bo = NULL;
- error->active_bo_count = count;
+ i = 0;
+ list_for_each_entry(obj, &dev_priv->mm.active_list, mm_list)
+ i++;
+ error->active_bo_count = i;
list_for_each_entry(obj, &dev_priv->mm.pinned_list, mm_list)
- count++;
- error->pinned_bo_count = count - error->active_bo_count;
+ i++;
+ error->pinned_bo_count = i - error->active_bo_count;
- if (count) {
- error->active_bo = kmalloc(sizeof(*error->active_bo)*count,
+ if (i) {
+ error->active_bo = kmalloc(sizeof(*error->active_bo)*i,
GFP_ATOMIC);
if (error->active_bo)
error->pinned_bo =
@@ -1673,11 +1612,6 @@
I915_WRITE(GTIIR, I915_READ(GTIIR));
I915_WRITE(GTIMR, dev_priv->gt_irq_mask);
- if (IS_GEN6(dev)) {
- I915_WRITE(GEN6_RENDER_IMR, ~GEN6_RENDER_USER_INTERRUPT);
- I915_WRITE(GEN6_BSD_IMR, ~GEN6_BSD_USER_INTERRUPT);
- I915_WRITE(GEN6_BLITTER_IMR, ~GEN6_BLITTER_USER_INTERRUPT);
- }
if (IS_GEN6(dev))
render_irqs =
@@ -1698,6 +1632,9 @@
} else {
hotplug_mask = SDE_CRT_HOTPLUG | SDE_PORTB_HOTPLUG |
SDE_PORTC_HOTPLUG | SDE_PORTD_HOTPLUG;
+ hotplug_mask |= SDE_AUX_MASK | SDE_FDI_MASK | SDE_TRANS_MASK;
+ I915_WRITE(FDI_RXA_IMR, 0);
+ I915_WRITE(FDI_RXB_IMR, 0);
}
dev_priv->pch_irq_mask = ~hotplug_mask;
diff --git a/drivers/gpu/drm/i915/i915_reg.h b/drivers/gpu/drm/i915/i915_reg.h
index 8f948a6..40a407f 100644
--- a/drivers/gpu/drm/i915/i915_reg.h
+++ b/drivers/gpu/drm/i915/i915_reg.h
@@ -145,6 +145,8 @@
#define MI_END_SCENE (1 << 4) /* flush binner and incr scene count */
#define MI_INVALIDATE_ISP (1 << 5) /* invalidate indirect state pointers */
#define MI_BATCH_BUFFER_END MI_INSTR(0x0a, 0)
+#define MI_SUSPEND_FLUSH MI_INSTR(0x0b, 0)
+#define MI_SUSPEND_FLUSH_EN (1<<0)
#define MI_REPORT_HEAD MI_INSTR(0x07, 0)
#define MI_OVERLAY_FLIP MI_INSTR(0x11,0)
#define MI_OVERLAY_CONTINUE (0x0<<21)
@@ -159,6 +161,7 @@
#define MI_MM_SPACE_PHYSICAL (0<<8)
#define MI_SAVE_EXT_STATE_EN (1<<3)
#define MI_RESTORE_EXT_STATE_EN (1<<2)
+#define MI_FORCE_RESTORE (1<<1)
#define MI_RESTORE_INHIBIT (1<<0)
#define MI_STORE_DWORD_IMM MI_INSTR(0x20, 1)
#define MI_MEM_VIRTUAL (1 << 22) /* 965+ only */
@@ -288,6 +291,7 @@
#define RING_HWS_PGA_GEN6(base) ((base)+0x2080)
#define RING_ACTHD(base) ((base)+0x74)
#define RING_NOPID(base) ((base)+0x94)
+#define RING_IMR(base) ((base)+0xa8)
#define TAIL_ADDR 0x001FFFF8
#define HEAD_WRAP_COUNT 0xFFE00000
#define HEAD_WRAP_ONE 0x00200000
@@ -1130,9 +1134,50 @@
#define RCBMINAVG 0x111a0
#define RCUPEI 0x111b0
#define RCDNEI 0x111b4
-#define MCHBAR_RENDER_STANDBY 0x111b8
-#define RCX_SW_EXIT (1<<23)
-#define RSX_STATUS_MASK 0x00700000
+#define RSTDBYCTL 0x111b8
+#define RS1EN (1<<31)
+#define RS2EN (1<<30)
+#define RS3EN (1<<29)
+#define D3RS3EN (1<<28) /* Display D3 imlies RS3 */
+#define SWPROMORSX (1<<27) /* RSx promotion timers ignored */
+#define RCWAKERW (1<<26) /* Resetwarn from PCH causes wakeup */
+#define DPRSLPVREN (1<<25) /* Fast voltage ramp enable */
+#define GFXTGHYST (1<<24) /* Hysteresis to allow trunk gating */
+#define RCX_SW_EXIT (1<<23) /* Leave RSx and prevent re-entry */
+#define RSX_STATUS_MASK (7<<20)
+#define RSX_STATUS_ON (0<<20)
+#define RSX_STATUS_RC1 (1<<20)
+#define RSX_STATUS_RC1E (2<<20)
+#define RSX_STATUS_RS1 (3<<20)
+#define RSX_STATUS_RS2 (4<<20) /* aka rc6 */
+#define RSX_STATUS_RSVD (5<<20) /* deep rc6 unsupported on ilk */
+#define RSX_STATUS_RS3 (6<<20) /* rs3 unsupported on ilk */
+#define RSX_STATUS_RSVD2 (7<<20)
+#define UWRCRSXE (1<<19) /* wake counter limit prevents rsx */
+#define RSCRP (1<<18) /* rs requests control on rs1/2 reqs */
+#define JRSC (1<<17) /* rsx coupled to cpu c-state */
+#define RS2INC0 (1<<16) /* allow rs2 in cpu c0 */
+#define RS1CONTSAV_MASK (3<<14)
+#define RS1CONTSAV_NO_RS1 (0<<14) /* rs1 doesn't save/restore context */
+#define RS1CONTSAV_RSVD (1<<14)
+#define RS1CONTSAV_SAVE_RS1 (2<<14) /* rs1 saves context */
+#define RS1CONTSAV_FULL_RS1 (3<<14) /* rs1 saves and restores context */
+#define NORMSLEXLAT_MASK (3<<12)
+#define SLOW_RS123 (0<<12)
+#define SLOW_RS23 (1<<12)
+#define SLOW_RS3 (2<<12)
+#define NORMAL_RS123 (3<<12)
+#define RCMODE_TIMEOUT (1<<11) /* 0 is eval interval method */
+#define IMPROMOEN (1<<10) /* promo is immediate or delayed until next idle interval (only for timeout method above) */
+#define RCENTSYNC (1<<9) /* rs coupled to cpu c-state (3/6/7) */
+#define STATELOCK (1<<7) /* locked to rs_cstate if 0 */
+#define RS_CSTATE_MASK (3<<4)
+#define RS_CSTATE_C367_RS1 (0<<4)
+#define RS_CSTATE_C36_RS1_C7_RS2 (1<<4)
+#define RS_CSTATE_RSVD (2<<4)
+#define RS_CSTATE_C367_RS2 (3<<4)
+#define REDSAVES (1<<3) /* no context save if was idle during rs0 */
+#define REDRESTORES (1<<2) /* no restore if was idle during rs0 */
#define VIDCTL 0x111c0
#define VIDSTS 0x111c8
#define VIDSTART 0x111cc /* 8 bits */
@@ -2345,8 +2390,13 @@
/* Memory latency timer register */
#define MLTR_ILK 0x11222
+#define MLTR_WM1_SHIFT 0
+#define MLTR_WM2_SHIFT 8
/* the unit of memory self-refresh latency time is 0.5us */
#define ILK_SRLT_MASK 0x3f
+#define ILK_LATENCY(shift) (I915_READ(MLTR_ILK) >> (shift) & ILK_SRLT_MASK)
+#define ILK_READ_WM1_LATENCY() ILK_LATENCY(MLTR_WM1_SHIFT)
+#define ILK_READ_WM2_LATENCY() ILK_LATENCY(MLTR_WM2_SHIFT)
/* define the fifo size on Ironlake */
#define ILK_DISPLAY_FIFO 128
@@ -2728,12 +2778,41 @@
/* PCH */
/* south display engine interrupt */
+#define SDE_AUDIO_POWER_D (1 << 27)
+#define SDE_AUDIO_POWER_C (1 << 26)
+#define SDE_AUDIO_POWER_B (1 << 25)
+#define SDE_AUDIO_POWER_SHIFT (25)
+#define SDE_AUDIO_POWER_MASK (7 << SDE_AUDIO_POWER_SHIFT)
+#define SDE_GMBUS (1 << 24)
+#define SDE_AUDIO_HDCP_TRANSB (1 << 23)
+#define SDE_AUDIO_HDCP_TRANSA (1 << 22)
+#define SDE_AUDIO_HDCP_MASK (3 << 22)
+#define SDE_AUDIO_TRANSB (1 << 21)
+#define SDE_AUDIO_TRANSA (1 << 20)
+#define SDE_AUDIO_TRANS_MASK (3 << 20)
+#define SDE_POISON (1 << 19)
+/* 18 reserved */
+#define SDE_FDI_RXB (1 << 17)
+#define SDE_FDI_RXA (1 << 16)
+#define SDE_FDI_MASK (3 << 16)
+#define SDE_AUXD (1 << 15)
+#define SDE_AUXC (1 << 14)
+#define SDE_AUXB (1 << 13)
+#define SDE_AUX_MASK (7 << 13)
+/* 12 reserved */
#define SDE_CRT_HOTPLUG (1 << 11)
#define SDE_PORTD_HOTPLUG (1 << 10)
#define SDE_PORTC_HOTPLUG (1 << 9)
#define SDE_PORTB_HOTPLUG (1 << 8)
#define SDE_SDVOB_HOTPLUG (1 << 6)
#define SDE_HOTPLUG_MASK (0xf << 8)
+#define SDE_TRANSB_CRC_DONE (1 << 5)
+#define SDE_TRANSB_CRC_ERR (1 << 4)
+#define SDE_TRANSB_FIFO_UNDER (1 << 3)
+#define SDE_TRANSA_CRC_DONE (1 << 2)
+#define SDE_TRANSA_CRC_ERR (1 << 1)
+#define SDE_TRANSA_FIFO_UNDER (1 << 0)
+#define SDE_TRANS_MASK (0x3f)
/* CPT */
#define SDE_CRT_HOTPLUG_CPT (1 << 19)
#define SDE_PORTD_HOTPLUG_CPT (1 << 23)
@@ -3174,10 +3253,11 @@
#define EDP_LINK_TRAIN_600MV_3_5DB_SNB_A (0x01<<22)
#define EDP_LINK_TRAIN_800MV_0DB_SNB_A (0x0<<22)
/* SNB B-stepping */
-#define EDP_LINK_TRAIN_400MV_0DB_SNB_B (0x0<<22)
-#define EDP_LINK_TRAIN_400MV_6DB_SNB_B (0x3a<<22)
-#define EDP_LINK_TRAIN_600MV_3_5DB_SNB_B (0x39<<22)
-#define EDP_LINK_TRAIN_800MV_0DB_SNB_B (0x38<<22)
+#define EDP_LINK_TRAIN_400_600MV_0DB_SNB_B (0x0<<22)
+#define EDP_LINK_TRAIN_400MV_3_5DB_SNB_B (0x1<<22)
+#define EDP_LINK_TRAIN_400_600MV_6DB_SNB_B (0x3a<<22)
+#define EDP_LINK_TRAIN_600_800MV_3_5DB_SNB_B (0x39<<22)
+#define EDP_LINK_TRAIN_800_1200MV_0DB_SNB_B (0x38<<22)
#define EDP_LINK_TRAIN_VOL_EMP_MASK_SNB (0x3f<<22)
#define FORCEWAKE 0xA18C
@@ -3239,6 +3319,7 @@
#define GEN6_PCODE_MAILBOX 0x138124
#define GEN6_PCODE_READY (1<<31)
+#define GEN6_READ_OC_PARAMS 0xc
#define GEN6_PCODE_WRITE_MIN_FREQ_TABLE 0x9
#define GEN6_PCODE_DATA 0x138128
diff --git a/drivers/gpu/drm/i915/i915_suspend.c b/drivers/gpu/drm/i915/i915_suspend.c
index 4107724..0521ecf2 100644
--- a/drivers/gpu/drm/i915/i915_suspend.c
+++ b/drivers/gpu/drm/i915/i915_suspend.c
@@ -740,7 +740,7 @@
I915_WRITE(PCH_PP_OFF_DELAYS, dev_priv->savePP_OFF_DELAYS);
I915_WRITE(PCH_PP_DIVISOR, dev_priv->savePP_DIVISOR);
I915_WRITE(PCH_PP_CONTROL, dev_priv->savePP_CONTROL);
- I915_WRITE(MCHBAR_RENDER_STANDBY,
+ I915_WRITE(RSTDBYCTL,
dev_priv->saveMCHBAR_RENDER_STANDBY);
} else {
I915_WRITE(PFIT_PGM_RATIOS, dev_priv->savePFIT_PGM_RATIOS);
@@ -811,7 +811,7 @@
dev_priv->saveFDI_RXA_IMR = I915_READ(FDI_RXA_IMR);
dev_priv->saveFDI_RXB_IMR = I915_READ(FDI_RXB_IMR);
dev_priv->saveMCHBAR_RENDER_STANDBY =
- I915_READ(MCHBAR_RENDER_STANDBY);
+ I915_READ(RSTDBYCTL);
} else {
dev_priv->saveIER = I915_READ(IER);
dev_priv->saveIMR = I915_READ(IMR);
@@ -822,10 +822,6 @@
if (IS_GEN6(dev))
gen6_disable_rps(dev);
- /* XXX disabling the clock gating breaks suspend on gm45
- intel_disable_clock_gating(dev);
- */
-
/* Cache mode state */
dev_priv->saveCACHE_MODE_0 = I915_READ(CACHE_MODE_0);
diff --git a/drivers/gpu/drm/i915/intel_crt.c b/drivers/gpu/drm/i915/intel_crt.c
index 8df5743..17035b8 100644
--- a/drivers/gpu/drm/i915/intel_crt.c
+++ b/drivers/gpu/drm/i915/intel_crt.c
@@ -30,6 +30,7 @@
#include "drm.h"
#include "drm_crtc.h"
#include "drm_crtc_helper.h"
+#include "drm_edid.h"
#include "intel_drv.h"
#include "i915_drm.h"
#include "i915_drv.h"
@@ -287,8 +288,9 @@
return i2c_transfer(&dev_priv->gmbus[ddc_bus].adapter, msgs, 1) == 1;
}
-static bool intel_crt_detect_ddc(struct intel_crt *crt)
+static bool intel_crt_detect_ddc(struct drm_connector *connector)
{
+ struct intel_crt *crt = intel_attached_crt(connector);
struct drm_i915_private *dev_priv = crt->base.base.dev->dev_private;
/* CRT should always be at 0, but check anyway */
@@ -301,8 +303,26 @@
}
if (intel_ddc_probe(&crt->base, dev_priv->crt_ddc_pin)) {
- DRM_DEBUG_KMS("CRT detected via DDC:0x50 [EDID]\n");
- return true;
+ struct edid *edid;
+ bool is_digital = false;
+
+ edid = drm_get_edid(connector,
+ &dev_priv->gmbus[dev_priv->crt_ddc_pin].adapter);
+ /*
+ * This may be a DVI-I connector with a shared DDC
+ * link between analog and digital outputs, so we
+ * have to check the EDID input spec of the attached device.
+ */
+ if (edid != NULL) {
+ is_digital = edid->input & DRM_EDID_INPUT_DIGITAL;
+ connector->display_info.raw_edid = NULL;
+ kfree(edid);
+ }
+
+ if (!is_digital) {
+ DRM_DEBUG_KMS("CRT detected via DDC:0x50 [EDID]\n");
+ return true;
+ }
}
return false;
@@ -458,7 +478,7 @@
}
}
- if (intel_crt_detect_ddc(crt))
+ if (intel_crt_detect_ddc(connector))
return connector_status_connected;
if (!force)
@@ -472,7 +492,7 @@
crtc = intel_get_load_detect_pipe(&crt->base, connector,
NULL, &dpms_mode);
if (crtc) {
- if (intel_crt_detect_ddc(crt))
+ if (intel_crt_detect_ddc(connector))
status = connector_status_connected;
else
status = intel_crt_load_detect(crtc, crt);
diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c
index 0abe79f..25d9688 100644
--- a/drivers/gpu/drm/i915/intel_display.c
+++ b/drivers/gpu/drm/i915/intel_display.c
@@ -3418,15 +3418,16 @@
static bool ironlake_compute_wm0(struct drm_device *dev,
int pipe,
const struct intel_watermark_params *display,
- int display_latency,
+ int display_latency_ns,
const struct intel_watermark_params *cursor,
- int cursor_latency,
+ int cursor_latency_ns,
int *plane_wm,
int *cursor_wm)
{
struct drm_crtc *crtc;
- int htotal, hdisplay, clock, pixel_size = 0;
- int line_time_us, line_count, entries;
+ int htotal, hdisplay, clock, pixel_size;
+ int line_time_us, line_count;
+ int entries, tlb_miss;
crtc = intel_get_crtc_for_pipe(dev, pipe);
if (crtc->fb == NULL || !crtc->enabled)
@@ -3438,7 +3439,10 @@
pixel_size = crtc->fb->bits_per_pixel / 8;
/* Use the small buffer method to calculate plane watermark */
- entries = ((clock * pixel_size / 1000) * display_latency * 100) / 1000;
+ entries = ((clock * pixel_size / 1000) * display_latency_ns) / 1000;
+ tlb_miss = display->fifo_size*display->cacheline_size - hdisplay * 8;
+ if (tlb_miss > 0)
+ entries += tlb_miss;
entries = DIV_ROUND_UP(entries, display->cacheline_size);
*plane_wm = entries + display->guard_size;
if (*plane_wm > (int)display->max_wm)
@@ -3446,8 +3450,11 @@
/* Use the large buffer method to calculate cursor watermark */
line_time_us = ((htotal * 1000) / clock);
- line_count = (cursor_latency * 100 / line_time_us + 1000) / 1000;
+ line_count = (cursor_latency_ns / line_time_us + 1000) / 1000;
entries = line_count * 64 * pixel_size;
+ tlb_miss = cursor->fifo_size*cursor->cacheline_size - hdisplay * 8;
+ if (tlb_miss > 0)
+ entries += tlb_miss;
entries = DIV_ROUND_UP(entries, cursor->cacheline_size);
*cursor_wm = entries + cursor->guard_size;
if (*cursor_wm > (int)cursor->max_wm)
@@ -3456,14 +3463,109 @@
return true;
}
+/*
+ * Check the wm result.
+ *
+ * If any calculated watermark values is larger than the maximum value that
+ * can be programmed into the associated watermark register, that watermark
+ * must be disabled.
+ */
+static bool ironlake_check_srwm(struct drm_device *dev, int level,
+ int fbc_wm, int display_wm, int cursor_wm,
+ const struct intel_watermark_params *display,
+ const struct intel_watermark_params *cursor)
+{
+ struct drm_i915_private *dev_priv = dev->dev_private;
+
+ DRM_DEBUG_KMS("watermark %d: display plane %d, fbc lines %d,"
+ " cursor %d\n", level, display_wm, fbc_wm, cursor_wm);
+
+ if (fbc_wm > SNB_FBC_MAX_SRWM) {
+ DRM_DEBUG_KMS("fbc watermark(%d) is too large(%d), disabling wm%d+\n",
+ fbc_wm, SNB_FBC_MAX_SRWM, level);
+
+ /* fbc has it's own way to disable FBC WM */
+ I915_WRITE(DISP_ARB_CTL,
+ I915_READ(DISP_ARB_CTL) | DISP_FBC_WM_DIS);
+ return false;
+ }
+
+ if (display_wm > display->max_wm) {
+ DRM_DEBUG_KMS("display watermark(%d) is too large(%d), disabling wm%d+\n",
+ display_wm, SNB_DISPLAY_MAX_SRWM, level);
+ return false;
+ }
+
+ if (cursor_wm > cursor->max_wm) {
+ DRM_DEBUG_KMS("cursor watermark(%d) is too large(%d), disabling wm%d+\n",
+ cursor_wm, SNB_CURSOR_MAX_SRWM, level);
+ return false;
+ }
+
+ if (!(fbc_wm || display_wm || cursor_wm)) {
+ DRM_DEBUG_KMS("latency %d is 0, disabling wm%d+\n", level, level);
+ return false;
+ }
+
+ return true;
+}
+
+/*
+ * Compute watermark values of WM[1-3],
+ */
+static bool ironlake_compute_srwm(struct drm_device *dev, int level,
+ int hdisplay, int htotal,
+ int pixel_size, int clock, int latency_ns,
+ const struct intel_watermark_params *display,
+ const struct intel_watermark_params *cursor,
+ int *fbc_wm, int *display_wm, int *cursor_wm)
+{
+
+ unsigned long line_time_us;
+ int line_count, line_size;
+ int small, large;
+ int entries;
+
+ if (!latency_ns) {
+ *fbc_wm = *display_wm = *cursor_wm = 0;
+ return false;
+ }
+
+ line_time_us = (htotal * 1000) / clock;
+ line_count = (latency_ns / line_time_us + 1000) / 1000;
+ line_size = hdisplay * pixel_size;
+
+ /* Use the minimum of the small and large buffer method for primary */
+ small = ((clock * pixel_size / 1000) * latency_ns) / 1000;
+ large = line_count * line_size;
+
+ entries = DIV_ROUND_UP(min(small, large), display->cacheline_size);
+ *display_wm = entries + display->guard_size;
+
+ /*
+ * Spec says:
+ * FBC WM = ((Final Primary WM * 64) / number of bytes per line) + 2
+ */
+ *fbc_wm = DIV_ROUND_UP(*display_wm * 64, line_size) + 2;
+
+ /* calculate the self-refresh watermark for display cursor */
+ entries = line_count * pixel_size * 64;
+ entries = DIV_ROUND_UP(entries, cursor->cacheline_size);
+ *cursor_wm = entries + cursor->guard_size;
+
+ return ironlake_check_srwm(dev, level,
+ *fbc_wm, *display_wm, *cursor_wm,
+ display, cursor);
+}
+
static void ironlake_update_wm(struct drm_device *dev,
int planea_clock, int planeb_clock,
- int sr_hdisplay, int sr_htotal,
+ int hdisplay, int htotal,
int pixel_size)
{
struct drm_i915_private *dev_priv = dev->dev_private;
- int plane_wm, cursor_wm, enabled;
- int tmp;
+ int fbc_wm, plane_wm, cursor_wm, enabled;
+ int clock;
enabled = 0;
if (ironlake_compute_wm0(dev, 0,
@@ -3498,152 +3600,49 @@
* Calculate and update the self-refresh watermark only when one
* display plane is used.
*/
- tmp = 0;
- if (enabled == 1) {
- unsigned long line_time_us;
- int small, large, plane_fbc;
- int sr_clock, entries;
- int line_count, line_size;
- /* Read the self-refresh latency. The unit is 0.5us */
- int ilk_sr_latency = I915_READ(MLTR_ILK) & ILK_SRLT_MASK;
+ I915_WRITE(WM3_LP_ILK, 0);
+ I915_WRITE(WM2_LP_ILK, 0);
+ I915_WRITE(WM1_LP_ILK, 0);
- sr_clock = planea_clock ? planea_clock : planeb_clock;
- line_time_us = (sr_htotal * 1000) / sr_clock;
+ if (enabled != 1)
+ return;
- /* Use ns/us then divide to preserve precision */
- line_count = ((ilk_sr_latency * 500) / line_time_us + 1000)
- / 1000;
- line_size = sr_hdisplay * pixel_size;
+ clock = planea_clock ? planea_clock : planeb_clock;
- /* Use the minimum of the small and large buffer method for primary */
- small = ((sr_clock * pixel_size / 1000) * (ilk_sr_latency * 500)) / 1000;
- large = line_count * line_size;
+ /* WM1 */
+ if (!ironlake_compute_srwm(dev, 1, hdisplay, htotal, pixel_size,
+ clock, ILK_READ_WM1_LATENCY() * 500,
+ &ironlake_display_srwm_info,
+ &ironlake_cursor_srwm_info,
+ &fbc_wm, &plane_wm, &cursor_wm))
+ return;
- entries = DIV_ROUND_UP(min(small, large),
- ironlake_display_srwm_info.cacheline_size);
+ I915_WRITE(WM1_LP_ILK,
+ WM1_LP_SR_EN |
+ (ILK_READ_WM1_LATENCY() << WM1_LP_LATENCY_SHIFT) |
+ (fbc_wm << WM1_LP_FBC_SHIFT) |
+ (plane_wm << WM1_LP_SR_SHIFT) |
+ cursor_wm);
- plane_fbc = entries * 64;
- plane_fbc = DIV_ROUND_UP(plane_fbc, line_size);
+ /* WM2 */
+ if (!ironlake_compute_srwm(dev, 2, hdisplay, htotal, pixel_size,
+ clock, ILK_READ_WM2_LATENCY() * 500,
+ &ironlake_display_srwm_info,
+ &ironlake_cursor_srwm_info,
+ &fbc_wm, &plane_wm, &cursor_wm))
+ return;
- plane_wm = entries + ironlake_display_srwm_info.guard_size;
- if (plane_wm > (int)ironlake_display_srwm_info.max_wm)
- plane_wm = ironlake_display_srwm_info.max_wm;
-
- /* calculate the self-refresh watermark for display cursor */
- entries = line_count * pixel_size * 64;
- entries = DIV_ROUND_UP(entries,
- ironlake_cursor_srwm_info.cacheline_size);
-
- cursor_wm = entries + ironlake_cursor_srwm_info.guard_size;
- if (cursor_wm > (int)ironlake_cursor_srwm_info.max_wm)
- cursor_wm = ironlake_cursor_srwm_info.max_wm;
-
- /* configure watermark and enable self-refresh */
- tmp = (WM1_LP_SR_EN |
- (ilk_sr_latency << WM1_LP_LATENCY_SHIFT) |
- (plane_fbc << WM1_LP_FBC_SHIFT) |
- (plane_wm << WM1_LP_SR_SHIFT) |
- cursor_wm);
- DRM_DEBUG_KMS("self-refresh watermark: display plane %d, fbc lines %d,"
- " cursor %d\n", plane_wm, plane_fbc, cursor_wm);
- }
- I915_WRITE(WM1_LP_ILK, tmp);
- /* XXX setup WM2 and WM3 */
-}
-
-/*
- * Check the wm result.
- *
- * If any calculated watermark values is larger than the maximum value that
- * can be programmed into the associated watermark register, that watermark
- * must be disabled.
- *
- * Also return true if all of those watermark values is 0, which is set by
- * sandybridge_compute_srwm, to indicate the latency is ZERO.
- */
-static bool sandybridge_check_srwm(struct drm_device *dev, int level,
- int fbc_wm, int display_wm, int cursor_wm)
-{
- struct drm_i915_private *dev_priv = dev->dev_private;
-
- DRM_DEBUG_KMS("watermark %d: display plane %d, fbc lines %d,"
- " cursor %d\n", level, display_wm, fbc_wm, cursor_wm);
-
- if (fbc_wm > SNB_FBC_MAX_SRWM) {
- DRM_DEBUG_KMS("fbc watermark(%d) is too large(%d), disabling wm%d+\n",
- fbc_wm, SNB_FBC_MAX_SRWM, level);
-
- /* fbc has it's own way to disable FBC WM */
- I915_WRITE(DISP_ARB_CTL,
- I915_READ(DISP_ARB_CTL) | DISP_FBC_WM_DIS);
- return false;
- }
-
- if (display_wm > SNB_DISPLAY_MAX_SRWM) {
- DRM_DEBUG_KMS("display watermark(%d) is too large(%d), disabling wm%d+\n",
- display_wm, SNB_DISPLAY_MAX_SRWM, level);
- return false;
- }
-
- if (cursor_wm > SNB_CURSOR_MAX_SRWM) {
- DRM_DEBUG_KMS("cursor watermark(%d) is too large(%d), disabling wm%d+\n",
- cursor_wm, SNB_CURSOR_MAX_SRWM, level);
- return false;
- }
-
- if (!(fbc_wm || display_wm || cursor_wm)) {
- DRM_DEBUG_KMS("latency %d is 0, disabling wm%d+\n", level, level);
- return false;
- }
-
- return true;
-}
-
-/*
- * Compute watermark values of WM[1-3],
- */
-static bool sandybridge_compute_srwm(struct drm_device *dev, int level,
- int hdisplay, int htotal, int pixel_size,
- int clock, int latency_ns, int *fbc_wm,
- int *display_wm, int *cursor_wm)
-{
-
- unsigned long line_time_us;
- int small, large;
- int entries;
- int line_count, line_size;
-
- if (!latency_ns) {
- *fbc_wm = *display_wm = *cursor_wm = 0;
- return false;
- }
-
- line_time_us = (htotal * 1000) / clock;
- line_count = (latency_ns / line_time_us + 1000) / 1000;
- line_size = hdisplay * pixel_size;
-
- /* Use the minimum of the small and large buffer method for primary */
- small = ((clock * pixel_size / 1000) * latency_ns) / 1000;
- large = line_count * line_size;
-
- entries = DIV_ROUND_UP(min(small, large),
- sandybridge_display_srwm_info.cacheline_size);
- *display_wm = entries + sandybridge_display_srwm_info.guard_size;
+ I915_WRITE(WM2_LP_ILK,
+ WM2_LP_EN |
+ (ILK_READ_WM2_LATENCY() << WM1_LP_LATENCY_SHIFT) |
+ (fbc_wm << WM1_LP_FBC_SHIFT) |
+ (plane_wm << WM1_LP_SR_SHIFT) |
+ cursor_wm);
/*
- * Spec said:
- * FBC WM = ((Final Primary WM * 64) / number of bytes per line) + 2
+ * WM3 is unsupported on ILK, probably because we don't have latency
+ * data for that power state
*/
- *fbc_wm = DIV_ROUND_UP(*display_wm * 64, line_size) + 2;
-
- /* calculate the self-refresh watermark for display cursor */
- entries = line_count * pixel_size * 64;
- entries = DIV_ROUND_UP(entries,
- sandybridge_cursor_srwm_info.cacheline_size);
- *cursor_wm = entries + sandybridge_cursor_srwm_info.guard_size;
-
- return sandybridge_check_srwm(dev, level,
- *fbc_wm, *display_wm, *cursor_wm);
}
static void sandybridge_update_wm(struct drm_device *dev,
@@ -3652,7 +3651,7 @@
int pixel_size)
{
struct drm_i915_private *dev_priv = dev->dev_private;
- int latency = SNB_READ_WM0_LATENCY();
+ int latency = SNB_READ_WM0_LATENCY() * 100; /* In unit 0.1us */
int fbc_wm, plane_wm, cursor_wm, enabled;
int clock;
@@ -3701,9 +3700,11 @@
clock = planea_clock ? planea_clock : planeb_clock;
/* WM1 */
- if (!sandybridge_compute_srwm(dev, 1, hdisplay, htotal, pixel_size,
- clock, SNB_READ_WM1_LATENCY() * 500,
- &fbc_wm, &plane_wm, &cursor_wm))
+ if (!ironlake_compute_srwm(dev, 1, hdisplay, htotal, pixel_size,
+ clock, SNB_READ_WM1_LATENCY() * 500,
+ &sandybridge_display_srwm_info,
+ &sandybridge_cursor_srwm_info,
+ &fbc_wm, &plane_wm, &cursor_wm))
return;
I915_WRITE(WM1_LP_ILK,
@@ -3714,10 +3715,12 @@
cursor_wm);
/* WM2 */
- if (!sandybridge_compute_srwm(dev, 2,
- hdisplay, htotal, pixel_size,
- clock, SNB_READ_WM2_LATENCY() * 500,
- &fbc_wm, &plane_wm, &cursor_wm))
+ if (!ironlake_compute_srwm(dev, 2,
+ hdisplay, htotal, pixel_size,
+ clock, SNB_READ_WM2_LATENCY() * 500,
+ &sandybridge_display_srwm_info,
+ &sandybridge_cursor_srwm_info,
+ &fbc_wm, &plane_wm, &cursor_wm))
return;
I915_WRITE(WM2_LP_ILK,
@@ -3728,10 +3731,12 @@
cursor_wm);
/* WM3 */
- if (!sandybridge_compute_srwm(dev, 3,
- hdisplay, htotal, pixel_size,
- clock, SNB_READ_WM3_LATENCY() * 500,
- &fbc_wm, &plane_wm, &cursor_wm))
+ if (!ironlake_compute_srwm(dev, 3,
+ hdisplay, htotal, pixel_size,
+ clock, SNB_READ_WM3_LATENCY() * 500,
+ &sandybridge_display_srwm_info,
+ &sandybridge_cursor_srwm_info,
+ &fbc_wm, &plane_wm, &cursor_wm))
return;
I915_WRITE(WM3_LP_ILK,
@@ -3951,7 +3956,7 @@
int lane = 0, link_bw, bpp;
/* CPU eDP doesn't require FDI link, so just set DP M/N
according to current link config */
- if (has_edp_encoder && !intel_encoder_is_pch_edp(&encoder->base)) {
+ if (has_edp_encoder && !intel_encoder_is_pch_edp(&has_edp_encoder->base)) {
target_clock = mode->clock;
intel_edp_link_config(has_edp_encoder,
&lane, &link_bw);
@@ -5038,8 +5043,8 @@
drm_i915_private_t *dev_priv = dev->dev_private;
struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
int pipe = intel_crtc->pipe;
- int dpll_reg = (pipe == 0) ? DPLL_A : DPLL_B;
- int dpll = I915_READ(dpll_reg);
+ int dpll_reg = DPLL(pipe);
+ int dpll;
if (HAS_PCH_SPLIT(dev))
return;
@@ -5047,17 +5052,19 @@
if (!dev_priv->lvds_downclock_avail)
return;
+ dpll = I915_READ(dpll_reg);
if (!HAS_PIPE_CXSR(dev) && (dpll & DISPLAY_RATE_SELECT_FPA1)) {
DRM_DEBUG_DRIVER("upclocking LVDS\n");
/* Unlock panel regs */
- I915_WRITE(PP_CONTROL, I915_READ(PP_CONTROL) |
- PANEL_UNLOCK_REGS);
+ I915_WRITE(PP_CONTROL,
+ I915_READ(PP_CONTROL) | PANEL_UNLOCK_REGS);
dpll &= ~DISPLAY_RATE_SELECT_FPA1;
I915_WRITE(dpll_reg, dpll);
- dpll = I915_READ(dpll_reg);
+ POSTING_READ(dpll_reg);
intel_wait_for_vblank(dev, pipe);
+
dpll = I915_READ(dpll_reg);
if (dpll & DISPLAY_RATE_SELECT_FPA1)
DRM_DEBUG_DRIVER("failed to upclock LVDS!\n");
@@ -5802,6 +5809,8 @@
encoder->base.possible_clones =
intel_encoder_clones(dev, encoder->clone_mask);
}
+
+ intel_panel_setup_backlight(dev);
}
static void intel_user_framebuffer_destroy(struct drm_framebuffer *fb)
@@ -6145,6 +6154,10 @@
void gen6_enable_rps(struct drm_i915_private *dev_priv)
{
+ u32 rp_state_cap = I915_READ(GEN6_RP_STATE_CAP);
+ u32 gt_perf_status = I915_READ(GEN6_GT_PERF_STATUS);
+ u32 pcu_mbox;
+ int cur_freq, min_freq, max_freq;
int i;
/* Here begins a magic sequence of register writes to enable
@@ -6216,6 +6229,29 @@
500))
DRM_ERROR("timeout waiting for pcode mailbox to finish\n");
+ min_freq = (rp_state_cap & 0xff0000) >> 16;
+ max_freq = rp_state_cap & 0xff;
+ cur_freq = (gt_perf_status & 0xff00) >> 8;
+
+ /* Check for overclock support */
+ if (wait_for((I915_READ(GEN6_PCODE_MAILBOX) & GEN6_PCODE_READY) == 0,
+ 500))
+ DRM_ERROR("timeout waiting for pcode mailbox to become idle\n");
+ I915_WRITE(GEN6_PCODE_MAILBOX, GEN6_READ_OC_PARAMS);
+ pcu_mbox = I915_READ(GEN6_PCODE_DATA);
+ if (wait_for((I915_READ(GEN6_PCODE_MAILBOX) & GEN6_PCODE_READY) == 0,
+ 500))
+ DRM_ERROR("timeout waiting for pcode mailbox to finish\n");
+ if (pcu_mbox & (1<<31)) { /* OC supported */
+ max_freq = pcu_mbox & 0xff;
+ DRM_DEBUG_DRIVER("overclocking supported, adjusting frequency max to %dMHz\n", pcu_mbox * 100);
+ }
+
+ /* In units of 100MHz */
+ dev_priv->max_delay = max_freq;
+ dev_priv->min_delay = min_freq;
+ dev_priv->cur_delay = cur_freq;
+
/* requires MSI enabled */
I915_WRITE(GEN6_PMIER,
GEN6_PM_MBOX_EVENT |
@@ -6386,42 +6422,6 @@
} else if (IS_I830(dev)) {
I915_WRITE(DSPCLK_GATE_D, OVRUNIT_CLOCK_GATE_DISABLE);
}
-
- /*
- * GPU can automatically power down the render unit if given a page
- * to save state.
- */
- if (IS_IRONLAKE_M(dev) && 0) { /* XXX causes a failure during suspend */
- if (dev_priv->renderctx == NULL)
- dev_priv->renderctx = intel_alloc_context_page(dev);
- if (dev_priv->renderctx) {
- struct drm_i915_gem_object *obj = dev_priv->renderctx;
- if (BEGIN_LP_RING(4) == 0) {
- OUT_RING(MI_SET_CONTEXT);
- OUT_RING(obj->gtt_offset |
- MI_MM_SPACE_GTT |
- MI_SAVE_EXT_STATE_EN |
- MI_RESTORE_EXT_STATE_EN |
- MI_RESTORE_INHIBIT);
- OUT_RING(MI_NOOP);
- OUT_RING(MI_FLUSH);
- ADVANCE_LP_RING();
- }
- } else
- DRM_DEBUG_KMS("Failed to allocate render context."
- "Disable RC6\n");
- }
-
- if (IS_GEN4(dev) && IS_MOBILE(dev)) {
- if (dev_priv->pwrctx == NULL)
- dev_priv->pwrctx = intel_alloc_context_page(dev);
- if (dev_priv->pwrctx) {
- struct drm_i915_gem_object *obj = dev_priv->pwrctx;
- I915_WRITE(PWRCTXA, obj->gtt_offset | PWRCTX_EN);
- I915_WRITE(MCHBAR_RENDER_STANDBY,
- I915_READ(MCHBAR_RENDER_STANDBY) & ~RCX_SW_EXIT);
- }
- }
}
void intel_disable_clock_gating(struct drm_device *dev)
@@ -6451,6 +6451,57 @@
}
}
+static void ironlake_disable_rc6(struct drm_device *dev)
+{
+ struct drm_i915_private *dev_priv = dev->dev_private;
+
+ /* Wake the GPU, prevent RC6, then restore RSTDBYCTL */
+ I915_WRITE(RSTDBYCTL, I915_READ(RSTDBYCTL) | RCX_SW_EXIT);
+ wait_for(((I915_READ(RSTDBYCTL) & RSX_STATUS_MASK) == RSX_STATUS_ON),
+ 10);
+ POSTING_READ(CCID);
+ I915_WRITE(PWRCTXA, 0);
+ POSTING_READ(PWRCTXA);
+ I915_WRITE(RSTDBYCTL, I915_READ(RSTDBYCTL) & ~RCX_SW_EXIT);
+ POSTING_READ(RSTDBYCTL);
+ i915_gem_object_unpin(dev_priv->renderctx);
+ drm_gem_object_unreference(&dev_priv->renderctx->base);
+ dev_priv->renderctx = NULL;
+ i915_gem_object_unpin(dev_priv->pwrctx);
+ drm_gem_object_unreference(&dev_priv->pwrctx->base);
+ dev_priv->pwrctx = NULL;
+}
+
+void ironlake_enable_rc6(struct drm_device *dev)
+{
+ struct drm_i915_private *dev_priv = dev->dev_private;
+ int ret;
+
+ /*
+ * GPU can automatically power down the render unit if given a page
+ * to save state.
+ */
+ ret = BEGIN_LP_RING(6);
+ if (ret) {
+ ironlake_disable_rc6(dev);
+ return;
+ }
+ OUT_RING(MI_SUSPEND_FLUSH | MI_SUSPEND_FLUSH_EN);
+ OUT_RING(MI_SET_CONTEXT);
+ OUT_RING(dev_priv->renderctx->gtt_offset |
+ MI_MM_SPACE_GTT |
+ MI_SAVE_EXT_STATE_EN |
+ MI_RESTORE_EXT_STATE_EN |
+ MI_RESTORE_INHIBIT);
+ OUT_RING(MI_SUSPEND_FLUSH);
+ OUT_RING(MI_NOOP);
+ OUT_RING(MI_FLUSH);
+ ADVANCE_LP_RING();
+
+ I915_WRITE(PWRCTXA, dev_priv->pwrctx->gtt_offset | PWRCTX_EN);
+ I915_WRITE(RSTDBYCTL, I915_READ(RSTDBYCTL) & ~RCX_SW_EXIT);
+}
+
/* Set up chip specific display functions */
static void intel_init_display(struct drm_device *dev)
{
@@ -6665,12 +6716,7 @@
dev->mode_config.max_width = 8192;
dev->mode_config.max_height = 8192;
}
-
- /* set memory base */
- if (IS_GEN2(dev))
- dev->mode_config.fb_base = pci_resource_start(dev->pdev, 0);
- else
- dev->mode_config.fb_base = pci_resource_start(dev->pdev, 2);
+ dev->mode_config.fb_base = dev->agp->base;
if (IS_MOBILE(dev) || !IS_GEN2(dev))
dev_priv->num_pipe = 2;
@@ -6698,6 +6744,21 @@
if (IS_GEN6(dev))
gen6_enable_rps(dev_priv);
+ if (IS_IRONLAKE_M(dev)) {
+ dev_priv->renderctx = intel_alloc_context_page(dev);
+ if (!dev_priv->renderctx)
+ goto skip_rc6;
+ dev_priv->pwrctx = intel_alloc_context_page(dev);
+ if (!dev_priv->pwrctx) {
+ i915_gem_object_unpin(dev_priv->renderctx);
+ drm_gem_object_unreference(&dev_priv->renderctx->base);
+ dev_priv->renderctx = NULL;
+ goto skip_rc6;
+ }
+ ironlake_enable_rc6(dev);
+ }
+
+skip_rc6:
INIT_WORK(&dev_priv->idle_work, intel_idle_update);
setup_timer(&dev_priv->idle_timer, intel_gpu_idle_timer,
(unsigned long)dev);
@@ -6734,7 +6795,8 @@
if (IS_GEN6(dev))
gen6_disable_rps(dev);
- intel_disable_clock_gating(dev);
+ if (IS_IRONLAKE_M(dev))
+ ironlake_disable_rc6(dev);
mutex_unlock(&dev->struct_mutex);
diff --git a/drivers/gpu/drm/i915/intel_dp.c b/drivers/gpu/drm/i915/intel_dp.c
index 1dc6040..1f4242b 100644
--- a/drivers/gpu/drm/i915/intel_dp.c
+++ b/drivers/gpu/drm/i915/intel_dp.c
@@ -1153,18 +1153,27 @@
static uint32_t
intel_gen6_edp_signal_levels(uint8_t train_set)
{
- switch (train_set & (DP_TRAIN_VOLTAGE_SWING_MASK|DP_TRAIN_PRE_EMPHASIS_MASK)) {
+ int signal_levels = train_set & (DP_TRAIN_VOLTAGE_SWING_MASK |
+ DP_TRAIN_PRE_EMPHASIS_MASK);
+ switch (signal_levels) {
case DP_TRAIN_VOLTAGE_SWING_400 | DP_TRAIN_PRE_EMPHASIS_0:
- return EDP_LINK_TRAIN_400MV_0DB_SNB_B;
+ case DP_TRAIN_VOLTAGE_SWING_600 | DP_TRAIN_PRE_EMPHASIS_0:
+ return EDP_LINK_TRAIN_400_600MV_0DB_SNB_B;
+ case DP_TRAIN_VOLTAGE_SWING_400 | DP_TRAIN_PRE_EMPHASIS_3_5:
+ return EDP_LINK_TRAIN_400MV_3_5DB_SNB_B;
case DP_TRAIN_VOLTAGE_SWING_400 | DP_TRAIN_PRE_EMPHASIS_6:
- return EDP_LINK_TRAIN_400MV_6DB_SNB_B;
+ case DP_TRAIN_VOLTAGE_SWING_600 | DP_TRAIN_PRE_EMPHASIS_6:
+ return EDP_LINK_TRAIN_400_600MV_6DB_SNB_B;
case DP_TRAIN_VOLTAGE_SWING_600 | DP_TRAIN_PRE_EMPHASIS_3_5:
- return EDP_LINK_TRAIN_600MV_3_5DB_SNB_B;
+ case DP_TRAIN_VOLTAGE_SWING_800 | DP_TRAIN_PRE_EMPHASIS_3_5:
+ return EDP_LINK_TRAIN_600_800MV_3_5DB_SNB_B;
case DP_TRAIN_VOLTAGE_SWING_800 | DP_TRAIN_PRE_EMPHASIS_0:
- return EDP_LINK_TRAIN_800MV_0DB_SNB_B;
+ case DP_TRAIN_VOLTAGE_SWING_1200 | DP_TRAIN_PRE_EMPHASIS_0:
+ return EDP_LINK_TRAIN_800_1200MV_0DB_SNB_B;
default:
- DRM_DEBUG_KMS("Unsupported voltage swing/pre-emphasis level\n");
- return EDP_LINK_TRAIN_400MV_0DB_SNB_B;
+ DRM_DEBUG_KMS("Unsupported voltage swing/pre-emphasis level:"
+ "0x%x\n", signal_levels);
+ return EDP_LINK_TRAIN_400_600MV_0DB_SNB_B;
}
}
@@ -1334,17 +1343,24 @@
struct drm_device *dev = intel_dp->base.base.dev;
struct drm_i915_private *dev_priv = dev->dev_private;
bool channel_eq = false;
- int tries;
+ int tries, cr_tries;
u32 reg;
uint32_t DP = intel_dp->DP;
/* channel equalization */
tries = 0;
+ cr_tries = 0;
channel_eq = false;
for (;;) {
/* Use intel_dp->train_set[0] to set the voltage and pre emphasis values */
uint32_t signal_levels;
+ if (cr_tries > 5) {
+ DRM_ERROR("failed to train DP, aborting\n");
+ intel_dp_link_down(intel_dp);
+ break;
+ }
+
if (IS_GEN6(dev) && is_edp(intel_dp)) {
signal_levels = intel_gen6_edp_signal_levels(intel_dp->train_set[0]);
DP = (DP & ~EDP_LINK_TRAIN_VOL_EMP_MASK_SNB) | signal_levels;
@@ -1367,14 +1383,26 @@
if (!intel_dp_get_link_status(intel_dp))
break;
+ /* Make sure clock is still ok */
+ if (!intel_clock_recovery_ok(intel_dp->link_status, intel_dp->lane_count)) {
+ intel_dp_start_link_train(intel_dp);
+ cr_tries++;
+ continue;
+ }
+
if (intel_channel_eq_ok(intel_dp)) {
channel_eq = true;
break;
}
- /* Try 5 times */
- if (tries > 5)
- break;
+ /* Try 5 times, then try clock recovery if that fails */
+ if (tries > 5) {
+ intel_dp_link_down(intel_dp);
+ intel_dp_start_link_train(intel_dp);
+ tries = 0;
+ cr_tries++;
+ continue;
+ }
/* Compute new intel_dp->train_set as requested by target */
intel_get_adjust_train(intel_dp);
diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h
index d782ad9..74db255 100644
--- a/drivers/gpu/drm/i915/intel_drv.h
+++ b/drivers/gpu/drm/i915/intel_drv.h
@@ -257,6 +257,9 @@
extern u32 intel_panel_get_max_backlight(struct drm_device *dev);
extern u32 intel_panel_get_backlight(struct drm_device *dev);
extern void intel_panel_set_backlight(struct drm_device *dev, u32 level);
+extern void intel_panel_setup_backlight(struct drm_device *dev);
+extern void intel_panel_enable_backlight(struct drm_device *dev);
+extern void intel_panel_disable_backlight(struct drm_device *dev);
extern void intel_crtc_load_lut(struct drm_crtc *crtc);
extern void intel_encoder_prepare (struct drm_encoder *encoder);
diff --git a/drivers/gpu/drm/i915/intel_fb.c b/drivers/gpu/drm/i915/intel_fb.c
index 701e830..ee145a2 100644
--- a/drivers/gpu/drm/i915/intel_fb.c
+++ b/drivers/gpu/drm/i915/intel_fb.c
@@ -62,6 +62,7 @@
struct drm_fb_helper_surface_size *sizes)
{
struct drm_device *dev = ifbdev->helper.dev;
+ struct drm_i915_private *dev_priv = dev->dev_private;
struct fb_info *info;
struct drm_framebuffer *fb;
struct drm_mode_fb_cmd mode_cmd;
@@ -77,7 +78,7 @@
mode_cmd.height = sizes->surface_height;
mode_cmd.bpp = sizes->surface_bpp;
- mode_cmd.pitch = ALIGN(mode_cmd.width * ((mode_cmd.bpp + 1) / 8), 64);
+ mode_cmd.pitch = ALIGN(mode_cmd.width * ((mode_cmd.bpp + 7) / 8), 64);
mode_cmd.depth = sizes->surface_depth;
size = mode_cmd.pitch * mode_cmd.height;
@@ -120,6 +121,11 @@
info->flags = FBINFO_DEFAULT | FBINFO_CAN_FORCE_OUTPUT;
info->fbops = &intelfb_ops;
+ ret = fb_alloc_cmap(&info->cmap, 256, 0);
+ if (ret) {
+ ret = -ENOMEM;
+ goto out_unpin;
+ }
/* setup aperture base/size for vesafb takeover */
info->apertures = alloc_apertures(1);
if (!info->apertures) {
@@ -127,10 +133,8 @@
goto out_unpin;
}
info->apertures->ranges[0].base = dev->mode_config.fb_base;
- if (!IS_GEN2(dev))
- info->apertures->ranges[0].size = pci_resource_len(dev->pdev, 2);
- else
- info->apertures->ranges[0].size = pci_resource_len(dev->pdev, 0);
+ info->apertures->ranges[0].size =
+ dev_priv->mm.gtt->gtt_mappable_entries << PAGE_SHIFT;
info->fix.smem_start = dev->mode_config.fb_base + obj->gtt_offset;
info->fix.smem_len = size;
@@ -140,12 +144,6 @@
ret = -ENOSPC;
goto out_unpin;
}
-
- ret = fb_alloc_cmap(&info->cmap, 256, 0);
- if (ret) {
- ret = -ENOMEM;
- goto out_unpin;
- }
info->screen_size = size;
// memset(info->screen_base, 0, size);
diff --git a/drivers/gpu/drm/i915/intel_lvds.c b/drivers/gpu/drm/i915/intel_lvds.c
index aa230708..8f4f6bd 100644
--- a/drivers/gpu/drm/i915/intel_lvds.c
+++ b/drivers/gpu/drm/i915/intel_lvds.c
@@ -106,7 +106,7 @@
I915_WRITE(ctl_reg, I915_READ(ctl_reg) | POWER_TARGET_ON);
POSTING_READ(lvds_reg);
- intel_panel_set_backlight(dev, dev_priv->backlight_level);
+ intel_panel_enable_backlight(dev);
}
static void intel_lvds_disable(struct intel_lvds *intel_lvds)
@@ -123,8 +123,7 @@
lvds_reg = LVDS;
}
- dev_priv->backlight_level = intel_panel_get_backlight(dev);
- intel_panel_set_backlight(dev, 0);
+ intel_panel_disable_backlight(dev);
I915_WRITE(ctl_reg, I915_READ(ctl_reg) & ~POWER_TARGET_ON);
@@ -375,6 +374,10 @@
}
out:
+ if ((pfit_control & PFIT_ENABLE) == 0) {
+ pfit_control = 0;
+ pfit_pgm_ratios = 0;
+ }
if (pfit_control != intel_lvds->pfit_control ||
pfit_pgm_ratios != intel_lvds->pfit_pgm_ratios) {
intel_lvds->pfit_control = pfit_control;
@@ -398,8 +401,6 @@
struct drm_i915_private *dev_priv = dev->dev_private;
struct intel_lvds *intel_lvds = to_intel_lvds(encoder);
- dev_priv->backlight_level = intel_panel_get_backlight(dev);
-
/* We try to do the minimum that is necessary in order to unlock
* the registers for mode setting.
*
@@ -430,9 +431,6 @@
struct drm_i915_private *dev_priv = dev->dev_private;
struct intel_lvds *intel_lvds = to_intel_lvds(encoder);
- if (dev_priv->backlight_level == 0)
- dev_priv->backlight_level = intel_panel_get_max_backlight(dev);
-
/* Undo any unlocking done in prepare to prevent accidental
* adjustment of the registers.
*/
diff --git a/drivers/gpu/drm/i915/intel_panel.c b/drivers/gpu/drm/i915/intel_panel.c
index 7350ec2..e00d200 100644
--- a/drivers/gpu/drm/i915/intel_panel.c
+++ b/drivers/gpu/drm/i915/intel_panel.c
@@ -250,3 +250,34 @@
tmp &= ~BACKLIGHT_DUTY_CYCLE_MASK;
I915_WRITE(BLC_PWM_CTL, tmp | level);
}
+
+void intel_panel_disable_backlight(struct drm_device *dev)
+{
+ struct drm_i915_private *dev_priv = dev->dev_private;
+
+ if (dev_priv->backlight_enabled) {
+ dev_priv->backlight_level = intel_panel_get_backlight(dev);
+ dev_priv->backlight_enabled = false;
+ }
+
+ intel_panel_set_backlight(dev, 0);
+}
+
+void intel_panel_enable_backlight(struct drm_device *dev)
+{
+ struct drm_i915_private *dev_priv = dev->dev_private;
+
+ if (dev_priv->backlight_level == 0)
+ dev_priv->backlight_level = intel_panel_get_max_backlight(dev);
+
+ intel_panel_set_backlight(dev, dev_priv->backlight_level);
+ dev_priv->backlight_enabled = true;
+}
+
+void intel_panel_setup_backlight(struct drm_device *dev)
+{
+ struct drm_i915_private *dev_priv = dev->dev_private;
+
+ dev_priv->backlight_level = intel_panel_get_max_backlight(dev);
+ dev_priv->backlight_enabled = dev_priv->backlight_level != 0;
+}
diff --git a/drivers/gpu/drm/i915/intel_ringbuffer.c b/drivers/gpu/drm/i915/intel_ringbuffer.c
index 56bc95c..03e3370 100644
--- a/drivers/gpu/drm/i915/intel_ringbuffer.c
+++ b/drivers/gpu/drm/i915/intel_ringbuffer.c
@@ -48,7 +48,7 @@
return seqno;
}
-static void
+static int
render_ring_flush(struct intel_ring_buffer *ring,
u32 invalidate_domains,
u32 flush_domains)
@@ -56,6 +56,7 @@
struct drm_device *dev = ring->dev;
drm_i915_private_t *dev_priv = dev->dev_private;
u32 cmd;
+ int ret;
#if WATCH_EXEC
DRM_INFO("%s: invalidate %08x flush %08x\n", __func__,
@@ -116,12 +117,16 @@
#if WATCH_EXEC
DRM_INFO("%s: queue flush %08x to ring\n", __func__, cmd);
#endif
- if (intel_ring_begin(ring, 2) == 0) {
- intel_ring_emit(ring, cmd);
- intel_ring_emit(ring, MI_NOOP);
- intel_ring_advance(ring);
- }
+ ret = intel_ring_begin(ring, 2);
+ if (ret)
+ return ret;
+
+ intel_ring_emit(ring, cmd);
+ intel_ring_emit(ring, MI_NOOP);
+ intel_ring_advance(ring);
}
+
+ return 0;
}
static void ring_write_tail(struct intel_ring_buffer *ring,
@@ -480,26 +485,56 @@
return pc->cpu_page[0];
}
+static void
+ironlake_enable_irq(drm_i915_private_t *dev_priv, u32 mask)
+{
+ dev_priv->gt_irq_mask &= ~mask;
+ I915_WRITE(GTIMR, dev_priv->gt_irq_mask);
+ POSTING_READ(GTIMR);
+}
+
+static void
+ironlake_disable_irq(drm_i915_private_t *dev_priv, u32 mask)
+{
+ dev_priv->gt_irq_mask |= mask;
+ I915_WRITE(GTIMR, dev_priv->gt_irq_mask);
+ POSTING_READ(GTIMR);
+}
+
+static void
+i915_enable_irq(drm_i915_private_t *dev_priv, u32 mask)
+{
+ dev_priv->irq_mask &= ~mask;
+ I915_WRITE(IMR, dev_priv->irq_mask);
+ POSTING_READ(IMR);
+}
+
+static void
+i915_disable_irq(drm_i915_private_t *dev_priv, u32 mask)
+{
+ dev_priv->irq_mask |= mask;
+ I915_WRITE(IMR, dev_priv->irq_mask);
+ POSTING_READ(IMR);
+}
+
static bool
render_ring_get_irq(struct intel_ring_buffer *ring)
{
struct drm_device *dev = ring->dev;
+ drm_i915_private_t *dev_priv = dev->dev_private;
if (!dev->irq_enabled)
return false;
- if (atomic_inc_return(&ring->irq_refcount) == 1) {
- drm_i915_private_t *dev_priv = dev->dev_private;
- unsigned long irqflags;
-
- spin_lock_irqsave(&dev_priv->irq_lock, irqflags);
+ spin_lock(&ring->irq_lock);
+ if (ring->irq_refcount++ == 0) {
if (HAS_PCH_SPLIT(dev))
- ironlake_enable_graphics_irq(dev_priv,
- GT_PIPE_NOTIFY | GT_USER_INTERRUPT);
+ ironlake_enable_irq(dev_priv,
+ GT_PIPE_NOTIFY | GT_USER_INTERRUPT);
else
i915_enable_irq(dev_priv, I915_USER_INTERRUPT);
- spin_unlock_irqrestore(&dev_priv->irq_lock, irqflags);
}
+ spin_unlock(&ring->irq_lock);
return true;
}
@@ -508,20 +543,18 @@
render_ring_put_irq(struct intel_ring_buffer *ring)
{
struct drm_device *dev = ring->dev;
+ drm_i915_private_t *dev_priv = dev->dev_private;
- if (atomic_dec_and_test(&ring->irq_refcount)) {
- drm_i915_private_t *dev_priv = dev->dev_private;
- unsigned long irqflags;
-
- spin_lock_irqsave(&dev_priv->irq_lock, irqflags);
+ spin_lock(&ring->irq_lock);
+ if (--ring->irq_refcount == 0) {
if (HAS_PCH_SPLIT(dev))
- ironlake_disable_graphics_irq(dev_priv,
- GT_USER_INTERRUPT |
- GT_PIPE_NOTIFY);
+ ironlake_disable_irq(dev_priv,
+ GT_USER_INTERRUPT |
+ GT_PIPE_NOTIFY);
else
i915_disable_irq(dev_priv, I915_USER_INTERRUPT);
- spin_unlock_irqrestore(&dev_priv->irq_lock, irqflags);
}
+ spin_unlock(&ring->irq_lock);
}
void intel_ring_setup_status_page(struct intel_ring_buffer *ring)
@@ -534,19 +567,24 @@
POSTING_READ(mmio);
}
-static void
+static int
bsd_ring_flush(struct intel_ring_buffer *ring,
u32 invalidate_domains,
u32 flush_domains)
{
- if ((flush_domains & I915_GEM_DOMAIN_RENDER) == 0)
- return;
+ int ret;
- if (intel_ring_begin(ring, 2) == 0) {
- intel_ring_emit(ring, MI_FLUSH);
- intel_ring_emit(ring, MI_NOOP);
- intel_ring_advance(ring);
- }
+ if ((flush_domains & I915_GEM_DOMAIN_RENDER) == 0)
+ return 0;
+
+ ret = intel_ring_begin(ring, 2);
+ if (ret)
+ return ret;
+
+ intel_ring_emit(ring, MI_FLUSH);
+ intel_ring_emit(ring, MI_NOOP);
+ intel_ring_advance(ring);
+ return 0;
}
static int
@@ -577,18 +615,15 @@
ring_get_irq(struct intel_ring_buffer *ring, u32 flag)
{
struct drm_device *dev = ring->dev;
+ drm_i915_private_t *dev_priv = dev->dev_private;
if (!dev->irq_enabled)
return false;
- if (atomic_inc_return(&ring->irq_refcount) == 1) {
- drm_i915_private_t *dev_priv = dev->dev_private;
- unsigned long irqflags;
-
- spin_lock_irqsave(&dev_priv->irq_lock, irqflags);
- ironlake_enable_graphics_irq(dev_priv, flag);
- spin_unlock_irqrestore(&dev_priv->irq_lock, irqflags);
- }
+ spin_lock(&ring->irq_lock);
+ if (ring->irq_refcount++ == 0)
+ ironlake_enable_irq(dev_priv, flag);
+ spin_unlock(&ring->irq_lock);
return true;
}
@@ -597,15 +632,47 @@
ring_put_irq(struct intel_ring_buffer *ring, u32 flag)
{
struct drm_device *dev = ring->dev;
+ drm_i915_private_t *dev_priv = dev->dev_private;
- if (atomic_dec_and_test(&ring->irq_refcount)) {
- drm_i915_private_t *dev_priv = dev->dev_private;
- unsigned long irqflags;
+ spin_lock(&ring->irq_lock);
+ if (--ring->irq_refcount == 0)
+ ironlake_disable_irq(dev_priv, flag);
+ spin_unlock(&ring->irq_lock);
+}
- spin_lock_irqsave(&dev_priv->irq_lock, irqflags);
- ironlake_disable_graphics_irq(dev_priv, flag);
- spin_unlock_irqrestore(&dev_priv->irq_lock, irqflags);
+static bool
+gen6_ring_get_irq(struct intel_ring_buffer *ring, u32 gflag, u32 rflag)
+{
+ struct drm_device *dev = ring->dev;
+ drm_i915_private_t *dev_priv = dev->dev_private;
+
+ if (!dev->irq_enabled)
+ return false;
+
+ spin_lock(&ring->irq_lock);
+ if (ring->irq_refcount++ == 0) {
+ ring->irq_mask &= ~rflag;
+ I915_WRITE_IMR(ring, ring->irq_mask);
+ ironlake_enable_irq(dev_priv, gflag);
}
+ spin_unlock(&ring->irq_lock);
+
+ return true;
+}
+
+static void
+gen6_ring_put_irq(struct intel_ring_buffer *ring, u32 gflag, u32 rflag)
+{
+ struct drm_device *dev = ring->dev;
+ drm_i915_private_t *dev_priv = dev->dev_private;
+
+ spin_lock(&ring->irq_lock);
+ if (--ring->irq_refcount == 0) {
+ ring->irq_mask |= rflag;
+ I915_WRITE_IMR(ring, ring->irq_mask);
+ ironlake_disable_irq(dev_priv, gflag);
+ }
+ spin_unlock(&ring->irq_lock);
}
static bool
@@ -748,6 +815,9 @@
INIT_LIST_HEAD(&ring->request_list);
INIT_LIST_HEAD(&ring->gpu_write_list);
+ spin_lock_init(&ring->irq_lock);
+ ring->irq_mask = ~0;
+
if (I915_NEED_GFX_HWS(dev)) {
ret = init_status_page(ring);
if (ret)
@@ -785,6 +855,14 @@
if (ret)
goto err_unmap;
+ /* Workaround an erratum on the i830 which causes a hang if
+ * the TAIL pointer points to within the last 2 cachelines
+ * of the buffer.
+ */
+ ring->effective_size = ring->size;
+ if (IS_I830(ring->dev))
+ ring->effective_size -= 128;
+
return 0;
err_unmap:
@@ -827,8 +905,7 @@
static int intel_wrap_ring_buffer(struct intel_ring_buffer *ring)
{
unsigned int *virt;
- int rem;
- rem = ring->size - ring->tail;
+ int rem = ring->size - ring->tail;
if (ring->space < rem) {
int ret = intel_wait_ring_buffer(ring, rem);
@@ -895,7 +972,7 @@
int n = 4*num_dwords;
int ret;
- if (unlikely(ring->tail + n > ring->size)) {
+ if (unlikely(ring->tail + n > ring->effective_size)) {
ret = intel_wrap_ring_buffer(ring);
if (unlikely(ret))
return ret;
@@ -973,20 +1050,25 @@
GEN6_BSD_SLEEP_PSMI_CONTROL_RC_ILDL_MESSAGE_ENABLE);
}
-static void gen6_ring_flush(struct intel_ring_buffer *ring,
- u32 invalidate_domains,
- u32 flush_domains)
+static int gen6_ring_flush(struct intel_ring_buffer *ring,
+ u32 invalidate_domains,
+ u32 flush_domains)
{
- if ((flush_domains & I915_GEM_DOMAIN_RENDER) == 0)
- return;
+ int ret;
- if (intel_ring_begin(ring, 4) == 0) {
- intel_ring_emit(ring, MI_FLUSH_DW);
- intel_ring_emit(ring, 0);
- intel_ring_emit(ring, 0);
- intel_ring_emit(ring, 0);
- intel_ring_advance(ring);
- }
+ if ((flush_domains & I915_GEM_DOMAIN_RENDER) == 0)
+ return 0;
+
+ ret = intel_ring_begin(ring, 4);
+ if (ret)
+ return ret;
+
+ intel_ring_emit(ring, MI_FLUSH_DW);
+ intel_ring_emit(ring, 0);
+ intel_ring_emit(ring, 0);
+ intel_ring_emit(ring, 0);
+ intel_ring_advance(ring);
+ return 0;
}
static int
@@ -1008,15 +1090,35 @@
}
static bool
+gen6_render_ring_get_irq(struct intel_ring_buffer *ring)
+{
+ return gen6_ring_get_irq(ring,
+ GT_USER_INTERRUPT,
+ GEN6_RENDER_USER_INTERRUPT);
+}
+
+static void
+gen6_render_ring_put_irq(struct intel_ring_buffer *ring)
+{
+ return gen6_ring_put_irq(ring,
+ GT_USER_INTERRUPT,
+ GEN6_RENDER_USER_INTERRUPT);
+}
+
+static bool
gen6_bsd_ring_get_irq(struct intel_ring_buffer *ring)
{
- return ring_get_irq(ring, GT_GEN6_BSD_USER_INTERRUPT);
+ return gen6_ring_get_irq(ring,
+ GT_GEN6_BSD_USER_INTERRUPT,
+ GEN6_BSD_USER_INTERRUPT);
}
static void
gen6_bsd_ring_put_irq(struct intel_ring_buffer *ring)
{
- ring_put_irq(ring, GT_GEN6_BSD_USER_INTERRUPT);
+ return gen6_ring_put_irq(ring,
+ GT_GEN6_BSD_USER_INTERRUPT,
+ GEN6_BSD_USER_INTERRUPT);
}
/* ring buffer for Video Codec for Gen6+ */
@@ -1040,13 +1142,17 @@
static bool
blt_ring_get_irq(struct intel_ring_buffer *ring)
{
- return ring_get_irq(ring, GT_BLT_USER_INTERRUPT);
+ return gen6_ring_get_irq(ring,
+ GT_BLT_USER_INTERRUPT,
+ GEN6_BLITTER_USER_INTERRUPT);
}
static void
blt_ring_put_irq(struct intel_ring_buffer *ring)
{
- ring_put_irq(ring, GT_BLT_USER_INTERRUPT);
+ gen6_ring_put_irq(ring,
+ GT_BLT_USER_INTERRUPT,
+ GEN6_BLITTER_USER_INTERRUPT);
}
@@ -1115,20 +1221,25 @@
return intel_ring_begin(ring, 4);
}
-static void blt_ring_flush(struct intel_ring_buffer *ring,
+static int blt_ring_flush(struct intel_ring_buffer *ring,
u32 invalidate_domains,
u32 flush_domains)
{
- if ((flush_domains & I915_GEM_DOMAIN_RENDER) == 0)
- return;
+ int ret;
- if (blt_ring_begin(ring, 4) == 0) {
- intel_ring_emit(ring, MI_FLUSH_DW);
- intel_ring_emit(ring, 0);
- intel_ring_emit(ring, 0);
- intel_ring_emit(ring, 0);
- intel_ring_advance(ring);
- }
+ if ((flush_domains & I915_GEM_DOMAIN_RENDER) == 0)
+ return 0;
+
+ ret = blt_ring_begin(ring, 4);
+ if (ret)
+ return ret;
+
+ intel_ring_emit(ring, MI_FLUSH_DW);
+ intel_ring_emit(ring, 0);
+ intel_ring_emit(ring, 0);
+ intel_ring_emit(ring, 0);
+ intel_ring_advance(ring);
+ return 0;
}
static void blt_ring_cleanup(struct intel_ring_buffer *ring)
@@ -1165,6 +1276,8 @@
*ring = render_ring;
if (INTEL_INFO(dev)->gen >= 6) {
ring->add_request = gen6_add_request;
+ ring->irq_get = gen6_render_ring_get_irq;
+ ring->irq_put = gen6_render_ring_put_irq;
} else if (IS_GEN5(dev)) {
ring->add_request = pc_render_add_request;
ring->get_seqno = pc_render_get_seqno;
diff --git a/drivers/gpu/drm/i915/intel_ringbuffer.h b/drivers/gpu/drm/i915/intel_ringbuffer.h
index 8e2e357..be9087e 100644
--- a/drivers/gpu/drm/i915/intel_ringbuffer.h
+++ b/drivers/gpu/drm/i915/intel_ringbuffer.h
@@ -16,21 +16,24 @@
#define I915_RING_READ(reg) i915_safe_read(dev_priv, reg)
-#define I915_READ_TAIL(ring) I915_RING_READ(RING_TAIL(ring->mmio_base))
-#define I915_WRITE_TAIL(ring, val) I915_WRITE(RING_TAIL(ring->mmio_base), val)
+#define I915_READ_TAIL(ring) I915_RING_READ(RING_TAIL((ring)->mmio_base))
+#define I915_WRITE_TAIL(ring, val) I915_WRITE(RING_TAIL((ring)->mmio_base), val)
-#define I915_READ_START(ring) I915_RING_READ(RING_START(ring->mmio_base))
-#define I915_WRITE_START(ring, val) I915_WRITE(RING_START(ring->mmio_base), val)
+#define I915_READ_START(ring) I915_RING_READ(RING_START((ring)->mmio_base))
+#define I915_WRITE_START(ring, val) I915_WRITE(RING_START((ring)->mmio_base), val)
-#define I915_READ_HEAD(ring) I915_RING_READ(RING_HEAD(ring->mmio_base))
-#define I915_WRITE_HEAD(ring, val) I915_WRITE(RING_HEAD(ring->mmio_base), val)
+#define I915_READ_HEAD(ring) I915_RING_READ(RING_HEAD((ring)->mmio_base))
+#define I915_WRITE_HEAD(ring, val) I915_WRITE(RING_HEAD((ring)->mmio_base), val)
-#define I915_READ_CTL(ring) I915_RING_READ(RING_CTL(ring->mmio_base))
-#define I915_WRITE_CTL(ring, val) I915_WRITE(RING_CTL(ring->mmio_base), val)
+#define I915_READ_CTL(ring) I915_RING_READ(RING_CTL((ring)->mmio_base))
+#define I915_WRITE_CTL(ring, val) I915_WRITE(RING_CTL((ring)->mmio_base), val)
-#define I915_READ_NOPID(ring) I915_RING_READ(RING_NOPID(ring->mmio_base))
-#define I915_READ_SYNC_0(ring) I915_RING_READ(RING_SYNC_0(ring->mmio_base))
-#define I915_READ_SYNC_1(ring) I915_RING_READ(RING_SYNC_1(ring->mmio_base))
+#define I915_WRITE_IMR(ring, val) I915_WRITE(RING_IMR((ring)->mmio_base), val)
+#define I915_READ_IMR(ring) I915_RING_READ(RING_IMR((ring)->mmio_base))
+
+#define I915_READ_NOPID(ring) I915_RING_READ(RING_NOPID((ring)->mmio_base))
+#define I915_READ_SYNC_0(ring) I915_RING_READ(RING_SYNC_0((ring)->mmio_base))
+#define I915_READ_SYNC_1(ring) I915_RING_READ(RING_SYNC_1((ring)->mmio_base))
struct intel_ring_buffer {
const char *name;
@@ -49,12 +52,15 @@
u32 tail;
int space;
int size;
+ int effective_size;
struct intel_hw_status_page status_page;
+ spinlock_t irq_lock;
+ u32 irq_refcount;
+ u32 irq_mask;
u32 irq_seqno; /* last seq seem at irq time */
u32 waiting_seqno;
u32 sync_seqno[I915_NUM_RINGS-1];
- atomic_t irq_refcount;
bool __must_check (*irq_get)(struct intel_ring_buffer *ring);
void (*irq_put)(struct intel_ring_buffer *ring);
@@ -62,9 +68,9 @@
void (*write_tail)(struct intel_ring_buffer *ring,
u32 value);
- void (*flush)(struct intel_ring_buffer *ring,
- u32 invalidate_domains,
- u32 flush_domains);
+ int __must_check (*flush)(struct intel_ring_buffer *ring,
+ u32 invalidate_domains,
+ u32 flush_domains);
int (*add_request)(struct intel_ring_buffer *ring,
u32 *seqno);
u32 (*get_seqno)(struct intel_ring_buffer *ring);
diff --git a/drivers/gpu/drm/i915/intel_sdvo.c b/drivers/gpu/drm/i915/intel_sdvo.c
index 9d0af36..45cd376 100644
--- a/drivers/gpu/drm/i915/intel_sdvo.c
+++ b/drivers/gpu/drm/i915/intel_sdvo.c
@@ -1024,9 +1024,13 @@
if (!intel_sdvo_set_target_input(intel_sdvo))
return;
- if (intel_sdvo->has_hdmi_monitor &&
- !intel_sdvo_set_avi_infoframe(intel_sdvo))
- return;
+ if (intel_sdvo->has_hdmi_monitor) {
+ intel_sdvo_set_encode(intel_sdvo, SDVO_ENCODE_HDMI);
+ intel_sdvo_set_colorimetry(intel_sdvo,
+ SDVO_COLORIMETRY_RGB256);
+ intel_sdvo_set_avi_infoframe(intel_sdvo);
+ } else
+ intel_sdvo_set_encode(intel_sdvo, SDVO_ENCODE_DVI);
if (intel_sdvo->is_tv &&
!intel_sdvo_set_tv_format(intel_sdvo))
@@ -1398,6 +1402,9 @@
intel_sdvo->attached_output = response;
+ intel_sdvo->has_hdmi_monitor = false;
+ intel_sdvo->has_hdmi_audio = false;
+
if ((intel_sdvo_connector->output_flag & response) == 0)
ret = connector_status_disconnected;
else if (response & SDVO_TMDS_MASK)
@@ -1922,20 +1929,7 @@
static bool
intel_sdvo_is_hdmi_connector(struct intel_sdvo *intel_sdvo, int device)
{
- int is_hdmi;
-
- if (!intel_sdvo_check_supp_encode(intel_sdvo))
- return false;
-
- if (!intel_sdvo_set_target_output(intel_sdvo,
- device == 0 ? SDVO_OUTPUT_TMDS0 : SDVO_OUTPUT_TMDS1))
- return false;
-
- is_hdmi = 0;
- if (!intel_sdvo_get_value(intel_sdvo, SDVO_CMD_GET_ENCODE, &is_hdmi, 1))
- return false;
-
- return !!is_hdmi;
+ return intel_sdvo_check_supp_encode(intel_sdvo);
}
static u8
@@ -2037,12 +2031,7 @@
connector->connector_type = DRM_MODE_CONNECTOR_DVID;
if (intel_sdvo_is_hdmi_connector(intel_sdvo, device)) {
- /* enable hdmi encoding mode if supported */
- intel_sdvo_set_encode(intel_sdvo, SDVO_ENCODE_HDMI);
- intel_sdvo_set_colorimetry(intel_sdvo,
- SDVO_COLORIMETRY_RGB256);
connector->connector_type = DRM_MODE_CONNECTOR_HDMIA;
-
intel_sdvo->is_hdmi = true;
}
intel_sdvo->base.clone_mask = ((1 << INTEL_SDVO_NON_TV_CLONE_BIT) |
diff --git a/drivers/gpu/drm/nouveau/nouveau_backlight.c b/drivers/gpu/drm/nouveau/nouveau_backlight.c
index b14c811..d3a9c6e 100644
--- a/drivers/gpu/drm/nouveau/nouveau_backlight.c
+++ b/drivers/gpu/drm/nouveau/nouveau_backlight.c
@@ -59,7 +59,7 @@
return 0;
}
-static struct backlight_ops nv40_bl_ops = {
+static const struct backlight_ops nv40_bl_ops = {
.options = BL_CORE_SUSPENDRESUME,
.get_brightness = nv40_get_intensity,
.update_status = nv40_set_intensity,
@@ -82,7 +82,7 @@
return 0;
}
-static struct backlight_ops nv50_bl_ops = {
+static const struct backlight_ops nv50_bl_ops = {
.options = BL_CORE_SUSPENDRESUME,
.get_brightness = nv50_get_intensity,
.update_status = nv50_set_intensity,
diff --git a/drivers/gpu/drm/nouveau/nouveau_bios.c b/drivers/gpu/drm/nouveau/nouveau_bios.c
index d304655..2aef5cd 100644
--- a/drivers/gpu/drm/nouveau/nouveau_bios.c
+++ b/drivers/gpu/drm/nouveau/nouveau_bios.c
@@ -1927,7 +1927,7 @@
* offset (8 bit): opcode
* offset + 1 (16 bit): time
*
- * Sleep for "time" miliseconds.
+ * Sleep for "time" milliseconds.
*/
unsigned time = ROM16(bios->data[offset + 1]);
@@ -1935,7 +1935,7 @@
if (!iexec->execute)
return 3;
- BIOSLOG(bios, "0x%04X: Sleeping for 0x%04X miliseconds\n",
+ BIOSLOG(bios, "0x%04X: Sleeping for 0x%04X milliseconds\n",
offset, time);
msleep(time);
diff --git a/drivers/gpu/drm/radeon/atombios.h b/drivers/gpu/drm/radeon/atombios.h
index 58a0cd0..04b269d 100644
--- a/drivers/gpu/drm/radeon/atombios.h
+++ b/drivers/gpu/drm/radeon/atombios.h
@@ -1629,7 +1629,7 @@
typedef struct _READ_EDID_FROM_HW_I2C_DATA_PARAMETERS
{
USHORT usPrescale; //Ratio between Engine clock and I2C clock
- USHORT usVRAMAddress; //Adress in Frame Buffer where to pace raw EDID
+ USHORT usVRAMAddress; //Address in Frame Buffer where to pace raw EDID
USHORT usStatus; //When use output: lower byte EDID checksum, high byte hardware status
//WHen use input: lower byte as 'byte to read':currently limited to 128byte or 1byte
UCHAR ucSlaveAddr; //Read from which slave
diff --git a/drivers/hid/Kconfig b/drivers/hid/Kconfig
index ffbc278..24cca2f 100644
--- a/drivers/hid/Kconfig
+++ b/drivers/hid/Kconfig
@@ -295,6 +295,23 @@
---help---
Support for Monterey Genius KB29E.
+config HID_MULTITOUCH
+ tristate "HID Multitouch panels"
+ depends on USB_HID
+ ---help---
+ Generic support for HID multitouch panels.
+
+ Say Y here if you have one of the following devices:
+ - Cypress TrueTouch panels
+ - Hanvon dual touch panels
+ - Pixcir dual touch panels
+ - 'Sensing Win7-TwoFinger' panel by GeneralTouch
+
+ If unsure, say N.
+
+ To compile this driver as a module, choose M here: the
+ module will be called hid-multitouch.
+
config HID_NTRIG
tristate "N-Trig touch screen"
depends on USB_HID
diff --git a/drivers/hid/Makefile b/drivers/hid/Makefile
index 6eae9a9..6efc2a0 100644
--- a/drivers/hid/Makefile
+++ b/drivers/hid/Makefile
@@ -47,6 +47,7 @@
obj-$(CONFIG_HID_MICROSOFT) += hid-microsoft.o
obj-$(CONFIG_HID_MONTEREY) += hid-monterey.o
obj-$(CONFIG_HID_MOSART) += hid-mosart.o
+obj-$(CONFIG_HID_MULTITOUCH) += hid-multitouch.o
obj-$(CONFIG_HID_NTRIG) += hid-ntrig.o
obj-$(CONFIG_HID_ORTEK) += hid-ortek.o
obj-$(CONFIG_HID_PRODIKEYS) += hid-prodikeys.o
diff --git a/drivers/hid/hid-cando.c b/drivers/hid/hid-cando.c
index 375b509..1ea066c 100644
--- a/drivers/hid/hid-cando.c
+++ b/drivers/hid/hid-cando.c
@@ -236,6 +236,8 @@
{ HID_USB_DEVICE(USB_VENDOR_ID_CANDO,
USB_DEVICE_ID_CANDO_MULTI_TOUCH) },
{ HID_USB_DEVICE(USB_VENDOR_ID_CANDO,
+ USB_DEVICE_ID_CANDO_MULTI_TOUCH_10_1) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_CANDO,
USB_DEVICE_ID_CANDO_MULTI_TOUCH_11_6) },
{ HID_USB_DEVICE(USB_VENDOR_ID_CANDO,
USB_DEVICE_ID_CANDO_MULTI_TOUCH_15_6) },
diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c
index 2611686..d678cf3 100644
--- a/drivers/hid/hid-core.c
+++ b/drivers/hid/hid-core.c
@@ -1312,7 +1312,9 @@
{ HID_USB_DEVICE(USB_VENDOR_ID_BELKIN, USB_DEVICE_ID_FLIP_KVM) },
{ HID_USB_DEVICE(USB_VENDOR_ID_BTC, USB_DEVICE_ID_BTC_EMPREX_REMOTE) },
{ HID_USB_DEVICE(USB_VENDOR_ID_BTC, USB_DEVICE_ID_BTC_EMPREX_REMOTE_2) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_CANDO, USB_DEVICE_ID_CANDO_PIXCIR_MULTI_TOUCH) },
{ HID_USB_DEVICE(USB_VENDOR_ID_CANDO, USB_DEVICE_ID_CANDO_MULTI_TOUCH) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_CANDO, USB_DEVICE_ID_CANDO_MULTI_TOUCH_10_1) },
{ HID_USB_DEVICE(USB_VENDOR_ID_CANDO, USB_DEVICE_ID_CANDO_MULTI_TOUCH_11_6) },
{ HID_USB_DEVICE(USB_VENDOR_ID_CANDO, USB_DEVICE_ID_CANDO_MULTI_TOUCH_15_6) },
{ HID_USB_DEVICE(USB_VENDOR_ID_CHERRY, USB_DEVICE_ID_CHERRY_CYMOTION) },
@@ -1324,6 +1326,7 @@
{ HID_USB_DEVICE(USB_VENDOR_ID_CYPRESS, USB_DEVICE_ID_CYPRESS_BARCODE_2) },
{ HID_USB_DEVICE(USB_VENDOR_ID_CYPRESS, USB_DEVICE_ID_CYPRESS_BARCODE_3) },
{ HID_USB_DEVICE(USB_VENDOR_ID_CYPRESS, USB_DEVICE_ID_CYPRESS_MOUSE) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_CYPRESS, USB_DEVICE_ID_CYPRESS_TRUETOUCH) },
{ HID_USB_DEVICE(USB_VENDOR_ID_DRAGONRISE, 0x0006) },
{ HID_USB_DEVICE(USB_VENDOR_ID_DWAV, USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH) },
{ HID_USB_DEVICE(USB_VENDOR_ID_DWAV, USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH1) },
@@ -1335,11 +1338,13 @@
{ HID_USB_DEVICE(USB_VENDOR_ID_EZKEY, USB_DEVICE_ID_BTC_8193) },
{ HID_USB_DEVICE(USB_VENDOR_ID_GAMERON, USB_DEVICE_ID_GAMERON_DUAL_PSX_ADAPTOR) },
{ HID_USB_DEVICE(USB_VENDOR_ID_GAMERON, USB_DEVICE_ID_GAMERON_DUAL_PCS_ADAPTOR) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_GENERAL_TOUCH, USB_DEVICE_ID_GENERAL_TOUCH_WIN7_TWOFINGERS) },
{ HID_USB_DEVICE(USB_VENDOR_ID_GREENASIA, 0x0003) },
{ HID_USB_DEVICE(USB_VENDOR_ID_GREENASIA, 0x0012) },
{ HID_USB_DEVICE(USB_VENDOR_ID_GYRATION, USB_DEVICE_ID_GYRATION_REMOTE) },
{ HID_USB_DEVICE(USB_VENDOR_ID_GYRATION, USB_DEVICE_ID_GYRATION_REMOTE_2) },
{ HID_USB_DEVICE(USB_VENDOR_ID_GYRATION, USB_DEVICE_ID_GYRATION_REMOTE_3) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_HANVON, USB_DEVICE_ID_HANVON_MULTITOUCH) },
{ HID_USB_DEVICE(USB_VENDOR_ID_KENSINGTON, USB_DEVICE_ID_KS_SLIMBLADE) },
{ HID_USB_DEVICE(USB_VENDOR_ID_KYE, USB_DEVICE_ID_KYE_ERGO_525V) },
{ HID_USB_DEVICE(USB_VENDOR_ID_LABTEC, USB_DEVICE_ID_LABTEC_WIRELESS_KEYBOARD) },
@@ -1422,6 +1427,7 @@
{ HID_USB_DEVICE(USB_VENDOR_ID_TOPSEED, USB_DEVICE_ID_TOPSEED_CYBERLINK) },
{ HID_USB_DEVICE(USB_VENDOR_ID_TOPSEED2, USB_DEVICE_ID_TOPSEED2_RF_COMBO) },
{ HID_USB_DEVICE(USB_VENDOR_ID_TWINHAN, USB_DEVICE_ID_TWINHAN_IR_REMOTE) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_TURBOX, USB_DEVICE_ID_TURBOX_TOUCHSCREEN_MOSART) },
{ HID_USB_DEVICE(USB_VENDOR_ID_UCLOGIC, USB_DEVICE_ID_UCLOGIC_TABLET_PF1209) },
{ HID_USB_DEVICE(USB_VENDOR_ID_UCLOGIC, USB_DEVICE_ID_UCLOGIC_TABLET_WP4030U) },
{ HID_USB_DEVICE(USB_VENDOR_ID_UCLOGIC, USB_DEVICE_ID_UCLOGIC_TABLET_WP5540U) },
@@ -1640,7 +1646,6 @@
{ HID_USB_DEVICE(USB_VENDOR_ID_ESSENTIAL_REALITY, USB_DEVICE_ID_ESSENTIAL_REALITY_P5) },
{ HID_USB_DEVICE(USB_VENDOR_ID_ETT, USB_DEVICE_ID_TC5UH) },
{ HID_USB_DEVICE(USB_VENDOR_ID_ETT, USB_DEVICE_ID_TC4UM) },
- { HID_USB_DEVICE(USB_VENDOR_ID_GENERAL_TOUCH, 0x0001) },
{ HID_USB_DEVICE(USB_VENDOR_ID_GENERAL_TOUCH, 0x0002) },
{ HID_USB_DEVICE(USB_VENDOR_ID_GENERAL_TOUCH, 0x0003) },
{ HID_USB_DEVICE(USB_VENDOR_ID_GENERAL_TOUCH, 0x0004) },
diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h
index f65cace..92a0d61 100644
--- a/drivers/hid/hid-ids.h
+++ b/drivers/hid/hid-ids.h
@@ -140,7 +140,9 @@
#define USB_DEVICE_ID_BTC_EMPREX_REMOTE_2 0x5577
#define USB_VENDOR_ID_CANDO 0x2087
+#define USB_DEVICE_ID_CANDO_PIXCIR_MULTI_TOUCH 0x0703
#define USB_DEVICE_ID_CANDO_MULTI_TOUCH 0x0a01
+#define USB_DEVICE_ID_CANDO_MULTI_TOUCH_10_1 0x0a02
#define USB_DEVICE_ID_CANDO_MULTI_TOUCH_11_6 0x0b03
#define USB_DEVICE_ID_CANDO_MULTI_TOUCH_15_6 0x0f01
@@ -186,6 +188,7 @@
#define USB_DEVICE_ID_CYPRESS_BARCODE_1 0xde61
#define USB_DEVICE_ID_CYPRESS_BARCODE_2 0xde64
#define USB_DEVICE_ID_CYPRESS_BARCODE_3 0xbca1
+#define USB_DEVICE_ID_CYPRESS_TRUETOUCH 0xc001
#define USB_VENDOR_ID_DEALEXTREAME 0x10c5
#define USB_DEVICE_ID_DEALEXTREAME_RADIO_SI4701 0x819a
@@ -236,6 +239,7 @@
#define USB_DEVICE_ID_GAMERON_DUAL_PCS_ADAPTOR 0x0002
#define USB_VENDOR_ID_GENERAL_TOUCH 0x0dfc
+#define USB_DEVICE_ID_GENERAL_TOUCH_WIN7_TWOFINGERS 0x0001
#define USB_VENDOR_ID_GLAB 0x06c2
#define USB_DEVICE_ID_4_PHIDGETSERVO_30 0x0038
@@ -318,6 +322,9 @@
#define USB_DEVICE_ID_HANWANG_TABLET_FIRST 0x5000
#define USB_DEVICE_ID_HANWANG_TABLET_LAST 0x8fff
+#define USB_VENDOR_ID_HANVON 0x20b3
+#define USB_DEVICE_ID_HANVON_MULTITOUCH 0x0a18
+
#define USB_VENDOR_ID_HAPP 0x078b
#define USB_DEVICE_ID_UGCI_DRIVING 0x0010
#define USB_DEVICE_ID_UGCI_FLYING 0x0020
diff --git a/drivers/hid/hid-input.c b/drivers/hid/hid-input.c
index e60fdb8..7f552bf 100644
--- a/drivers/hid/hid-input.c
+++ b/drivers/hid/hid-input.c
@@ -290,6 +290,14 @@
goto ignore;
}
+ if (field->report_type == HID_FEATURE_REPORT) {
+ if (device->driver->feature_mapping) {
+ device->driver->feature_mapping(device, hidinput, field,
+ usage);
+ }
+ goto ignore;
+ }
+
if (device->driver->input_mapping) {
int ret = device->driver->input_mapping(device, hidinput, field,
usage, &bit, &max);
@@ -839,7 +847,6 @@
struct hid_input *hidinput = NULL;
struct input_dev *input_dev;
int i, j, k;
- int max_report_type = HID_OUTPUT_REPORT;
INIT_LIST_HEAD(&hid->inputs);
@@ -856,10 +863,11 @@
return -1;
}
- if (hid->quirks & HID_QUIRK_SKIP_OUTPUT_REPORTS)
- max_report_type = HID_INPUT_REPORT;
+ for (k = HID_INPUT_REPORT; k <= HID_FEATURE_REPORT; k++) {
+ if (k == HID_OUTPUT_REPORT &&
+ hid->quirks & HID_QUIRK_SKIP_OUTPUT_REPORTS)
+ continue;
- for (k = HID_INPUT_REPORT; k <= max_report_type; k++)
list_for_each_entry(report, &hid->report_enum[k].report_list, list) {
if (!report->maxfield)
@@ -912,6 +920,7 @@
hidinput = NULL;
}
}
+ }
if (hidinput && input_register_device(hidinput->input))
goto out_cleanup;
diff --git a/drivers/hid/hid-mosart.c b/drivers/hid/hid-mosart.c
index 9fb050c..aed7ffe 100644
--- a/drivers/hid/hid-mosart.c
+++ b/drivers/hid/hid-mosart.c
@@ -257,6 +257,7 @@
static const struct hid_device_id mosart_devices[] = {
{ HID_USB_DEVICE(USB_VENDOR_ID_ASUS, USB_DEVICE_ID_ASUS_T91MT) },
{ HID_USB_DEVICE(USB_VENDOR_ID_ASUS, USB_DEVICE_ID_ASUSTEK_MULTITOUCH_YFO) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_TURBOX, USB_DEVICE_ID_TURBOX_TOUCHSCREEN_MOSART) },
{ }
};
MODULE_DEVICE_TABLE(hid, mosart_devices);
diff --git a/drivers/hid/hid-multitouch.c b/drivers/hid/hid-multitouch.c
new file mode 100644
index 0000000..07d3183
--- /dev/null
+++ b/drivers/hid/hid-multitouch.c
@@ -0,0 +1,516 @@
+/*
+ * HID driver for multitouch panels
+ *
+ * Copyright (c) 2010-2011 Stephane Chatty <chatty@enac.fr>
+ * Copyright (c) 2010-2011 Benjamin Tissoires <benjamin.tissoires@gmail.com>
+ * Copyright (c) 2010-2011 Ecole Nationale de l'Aviation Civile, France
+ *
+ */
+
+/*
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ */
+
+#include <linux/device.h>
+#include <linux/hid.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/usb.h>
+#include <linux/input/mt.h>
+#include "usbhid/usbhid.h"
+
+
+MODULE_AUTHOR("Stephane Chatty <chatty@enac.fr>");
+MODULE_DESCRIPTION("HID multitouch panels");
+MODULE_LICENSE("GPL");
+
+#include "hid-ids.h"
+
+/* quirks to control the device */
+#define MT_QUIRK_NOT_SEEN_MEANS_UP (1 << 0)
+#define MT_QUIRK_SLOT_IS_CONTACTID (1 << 1)
+#define MT_QUIRK_CYPRESS (1 << 2)
+#define MT_QUIRK_SLOT_IS_CONTACTNUMBER (1 << 3)
+#define MT_QUIRK_VALID_IS_INRANGE (1 << 4)
+#define MT_QUIRK_VALID_IS_CONFIDENCE (1 << 5)
+
+struct mt_slot {
+ __s32 x, y, p, w, h;
+ __s32 contactid; /* the device ContactID assigned to this slot */
+ bool touch_state; /* is the touch valid? */
+ bool seen_in_this_frame;/* has this slot been updated */
+};
+
+struct mt_device {
+ struct mt_slot curdata; /* placeholder of incoming data */
+ struct mt_class *mtclass; /* our mt device class */
+ unsigned last_field_index; /* last field index of the report */
+ unsigned last_slot_field; /* the last field of a slot */
+ __s8 inputmode; /* InputMode HID feature, -1 if non-existent */
+ __u8 num_received; /* how many contacts we received */
+ __u8 num_expected; /* expected last contact index */
+ bool curvalid; /* is the current contact valid? */
+ struct mt_slot slots[0]; /* first slot */
+};
+
+struct mt_class {
+ __s32 name; /* MT_CLS */
+ __s32 quirks;
+ __s32 sn_move; /* Signal/noise ratio for move events */
+ __s32 sn_pressure; /* Signal/noise ratio for pressure events */
+ __u8 maxcontacts;
+};
+
+/* classes of device behavior */
+#define MT_CLS_DEFAULT 1
+#define MT_CLS_DUAL1 2
+#define MT_CLS_DUAL2 3
+#define MT_CLS_CYPRESS 4
+
+/*
+ * these device-dependent functions determine what slot corresponds
+ * to a valid contact that was just read.
+ */
+
+static int cypress_compute_slot(struct mt_device *td)
+{
+ if (td->curdata.contactid != 0 || td->num_received == 0)
+ return td->curdata.contactid;
+ else
+ return -1;
+}
+
+static int find_slot_from_contactid(struct mt_device *td)
+{
+ int i;
+ for (i = 0; i < td->mtclass->maxcontacts; ++i) {
+ if (td->slots[i].contactid == td->curdata.contactid &&
+ td->slots[i].touch_state)
+ return i;
+ }
+ for (i = 0; i < td->mtclass->maxcontacts; ++i) {
+ if (!td->slots[i].seen_in_this_frame &&
+ !td->slots[i].touch_state)
+ return i;
+ }
+ /* should not occurs. If this happens that means
+ * that the device sent more touches that it says
+ * in the report descriptor. It is ignored then. */
+ return -1;
+}
+
+struct mt_class mt_classes[] = {
+ { .name = MT_CLS_DEFAULT,
+ .quirks = MT_QUIRK_VALID_IS_INRANGE,
+ .maxcontacts = 10 },
+ { .name = MT_CLS_DUAL1,
+ .quirks = MT_QUIRK_VALID_IS_INRANGE |
+ MT_QUIRK_SLOT_IS_CONTACTID,
+ .maxcontacts = 2 },
+ { .name = MT_CLS_DUAL2,
+ .quirks = MT_QUIRK_VALID_IS_INRANGE |
+ MT_QUIRK_SLOT_IS_CONTACTNUMBER,
+ .maxcontacts = 2 },
+ { .name = MT_CLS_CYPRESS,
+ .quirks = MT_QUIRK_NOT_SEEN_MEANS_UP |
+ MT_QUIRK_CYPRESS,
+ .maxcontacts = 10 },
+
+ { }
+};
+
+static void mt_feature_mapping(struct hid_device *hdev, struct hid_input *hi,
+ struct hid_field *field, struct hid_usage *usage)
+{
+ if (usage->hid == HID_DG_INPUTMODE) {
+ struct mt_device *td = hid_get_drvdata(hdev);
+ td->inputmode = field->report->id;
+ }
+}
+
+static void set_abs(struct input_dev *input, unsigned int code,
+ struct hid_field *field, int snratio)
+{
+ int fmin = field->logical_minimum;
+ int fmax = field->logical_maximum;
+ int fuzz = snratio ? (fmax - fmin) / snratio : 0;
+ input_set_abs_params(input, code, fmin, fmax, fuzz, 0);
+}
+
+static int mt_input_mapping(struct hid_device *hdev, struct hid_input *hi,
+ struct hid_field *field, struct hid_usage *usage,
+ unsigned long **bit, int *max)
+{
+ struct mt_device *td = hid_get_drvdata(hdev);
+ struct mt_class *cls = td->mtclass;
+ switch (usage->hid & HID_USAGE_PAGE) {
+
+ case HID_UP_GENDESK:
+ switch (usage->hid) {
+ case HID_GD_X:
+ hid_map_usage(hi, usage, bit, max,
+ EV_ABS, ABS_MT_POSITION_X);
+ set_abs(hi->input, ABS_MT_POSITION_X, field,
+ cls->sn_move);
+ /* touchscreen emulation */
+ set_abs(hi->input, ABS_X, field, cls->sn_move);
+ td->last_slot_field = usage->hid;
+ return 1;
+ case HID_GD_Y:
+ hid_map_usage(hi, usage, bit, max,
+ EV_ABS, ABS_MT_POSITION_Y);
+ set_abs(hi->input, ABS_MT_POSITION_Y, field,
+ cls->sn_move);
+ /* touchscreen emulation */
+ set_abs(hi->input, ABS_Y, field, cls->sn_move);
+ td->last_slot_field = usage->hid;
+ return 1;
+ }
+ return 0;
+
+ case HID_UP_DIGITIZER:
+ switch (usage->hid) {
+ case HID_DG_INRANGE:
+ td->last_slot_field = usage->hid;
+ return 1;
+ case HID_DG_CONFIDENCE:
+ td->last_slot_field = usage->hid;
+ return 1;
+ case HID_DG_TIPSWITCH:
+ hid_map_usage(hi, usage, bit, max, EV_KEY, BTN_TOUCH);
+ input_set_capability(hi->input, EV_KEY, BTN_TOUCH);
+ td->last_slot_field = usage->hid;
+ return 1;
+ case HID_DG_CONTACTID:
+ input_mt_init_slots(hi->input,
+ td->mtclass->maxcontacts);
+ td->last_slot_field = usage->hid;
+ return 1;
+ case HID_DG_WIDTH:
+ hid_map_usage(hi, usage, bit, max,
+ EV_ABS, ABS_MT_TOUCH_MAJOR);
+ td->last_slot_field = usage->hid;
+ return 1;
+ case HID_DG_HEIGHT:
+ hid_map_usage(hi, usage, bit, max,
+ EV_ABS, ABS_MT_TOUCH_MINOR);
+ field->logical_maximum = 1;
+ field->logical_minimum = 0;
+ set_abs(hi->input, ABS_MT_ORIENTATION, field, 0);
+ td->last_slot_field = usage->hid;
+ return 1;
+ case HID_DG_TIPPRESSURE:
+ hid_map_usage(hi, usage, bit, max,
+ EV_ABS, ABS_MT_PRESSURE);
+ set_abs(hi->input, ABS_MT_PRESSURE, field,
+ cls->sn_pressure);
+ /* touchscreen emulation */
+ set_abs(hi->input, ABS_PRESSURE, field,
+ cls->sn_pressure);
+ td->last_slot_field = usage->hid;
+ return 1;
+ case HID_DG_CONTACTCOUNT:
+ td->last_field_index = field->report->maxfield - 1;
+ return 1;
+ case HID_DG_CONTACTMAX:
+ /* we don't set td->last_slot_field as contactcount and
+ * contact max are global to the report */
+ return -1;
+ }
+ /* let hid-input decide for the others */
+ return 0;
+
+ case 0xff000000:
+ /* we do not want to map these: no input-oriented meaning */
+ return -1;
+ }
+
+ return 0;
+}
+
+static int mt_input_mapped(struct hid_device *hdev, struct hid_input *hi,
+ struct hid_field *field, struct hid_usage *usage,
+ unsigned long **bit, int *max)
+{
+ if (usage->type == EV_KEY || usage->type == EV_ABS)
+ set_bit(usage->type, hi->input->evbit);
+
+ return -1;
+}
+
+static int mt_compute_slot(struct mt_device *td)
+{
+ __s32 quirks = td->mtclass->quirks;
+
+ if (quirks & MT_QUIRK_SLOT_IS_CONTACTID)
+ return td->curdata.contactid;
+
+ if (quirks & MT_QUIRK_CYPRESS)
+ return cypress_compute_slot(td);
+
+ if (quirks & MT_QUIRK_SLOT_IS_CONTACTNUMBER)
+ return td->num_received;
+
+ return find_slot_from_contactid(td);
+}
+
+/*
+ * this function is called when a whole contact has been processed,
+ * so that it can assign it to a slot and store the data there
+ */
+static void mt_complete_slot(struct mt_device *td)
+{
+ td->curdata.seen_in_this_frame = true;
+ if (td->curvalid) {
+ int slotnum = mt_compute_slot(td);
+
+ if (slotnum >= 0 && slotnum < td->mtclass->maxcontacts)
+ td->slots[slotnum] = td->curdata;
+ }
+ td->num_received++;
+}
+
+
+/*
+ * this function is called when a whole packet has been received and processed,
+ * so that it can decide what to send to the input layer.
+ */
+static void mt_emit_event(struct mt_device *td, struct input_dev *input)
+{
+ int i;
+
+ for (i = 0; i < td->mtclass->maxcontacts; ++i) {
+ struct mt_slot *s = &(td->slots[i]);
+ if ((td->mtclass->quirks & MT_QUIRK_NOT_SEEN_MEANS_UP) &&
+ !s->seen_in_this_frame) {
+ s->touch_state = false;
+ }
+
+ input_mt_slot(input, i);
+ input_mt_report_slot_state(input, MT_TOOL_FINGER,
+ s->touch_state);
+ if (s->touch_state) {
+ input_event(input, EV_ABS, ABS_MT_POSITION_X, s->x);
+ input_event(input, EV_ABS, ABS_MT_POSITION_Y, s->y);
+ input_event(input, EV_ABS, ABS_MT_PRESSURE, s->p);
+ input_event(input, EV_ABS, ABS_MT_TOUCH_MAJOR, s->w);
+ input_event(input, EV_ABS, ABS_MT_TOUCH_MINOR, s->h);
+ }
+ s->seen_in_this_frame = false;
+
+ }
+
+ input_mt_report_pointer_emulation(input, true);
+ input_sync(input);
+ td->num_received = 0;
+}
+
+
+
+static int mt_event(struct hid_device *hid, struct hid_field *field,
+ struct hid_usage *usage, __s32 value)
+{
+ struct mt_device *td = hid_get_drvdata(hid);
+ __s32 quirks = td->mtclass->quirks;
+
+ if (hid->claimed & HID_CLAIMED_INPUT) {
+ switch (usage->hid) {
+ case HID_DG_INRANGE:
+ if (quirks & MT_QUIRK_VALID_IS_INRANGE)
+ td->curvalid = value;
+ break;
+ case HID_DG_TIPSWITCH:
+ if (quirks & MT_QUIRK_NOT_SEEN_MEANS_UP)
+ td->curvalid = value;
+ td->curdata.touch_state = value;
+ break;
+ case HID_DG_CONFIDENCE:
+ if (quirks & MT_QUIRK_VALID_IS_CONFIDENCE)
+ td->curvalid = value;
+ break;
+ case HID_DG_CONTACTID:
+ td->curdata.contactid = value;
+ break;
+ case HID_DG_TIPPRESSURE:
+ td->curdata.p = value;
+ break;
+ case HID_GD_X:
+ td->curdata.x = value;
+ break;
+ case HID_GD_Y:
+ td->curdata.y = value;
+ break;
+ case HID_DG_WIDTH:
+ td->curdata.w = value;
+ break;
+ case HID_DG_HEIGHT:
+ td->curdata.h = value;
+ break;
+ case HID_DG_CONTACTCOUNT:
+ /*
+ * Includes multi-packet support where subsequent
+ * packets are sent with zero contactcount.
+ */
+ if (value)
+ td->num_expected = value;
+ break;
+
+ default:
+ /* fallback to the generic hidinput handling */
+ return 0;
+ }
+
+ if (usage->hid == td->last_slot_field)
+ mt_complete_slot(td);
+
+ if (field->index == td->last_field_index
+ && td->num_received >= td->num_expected)
+ mt_emit_event(td, field->hidinput->input);
+
+ }
+
+ /* we have handled the hidinput part, now remains hiddev */
+ if (hid->claimed & HID_CLAIMED_HIDDEV && hid->hiddev_hid_event)
+ hid->hiddev_hid_event(hid, field, usage, value);
+
+ return 1;
+}
+
+static void mt_set_input_mode(struct hid_device *hdev)
+{
+ struct mt_device *td = hid_get_drvdata(hdev);
+ struct hid_report *r;
+ struct hid_report_enum *re;
+
+ if (td->inputmode < 0)
+ return;
+
+ re = &(hdev->report_enum[HID_FEATURE_REPORT]);
+ r = re->report_id_hash[td->inputmode];
+ if (r) {
+ r->field[0]->value[0] = 0x02;
+ usbhid_submit_report(hdev, r, USB_DIR_OUT);
+ }
+}
+
+static int mt_probe(struct hid_device *hdev, const struct hid_device_id *id)
+{
+ int ret, i;
+ struct mt_device *td;
+ struct mt_class *mtclass = mt_classes; /* MT_CLS_DEFAULT */
+
+ for (i = 0; mt_classes[i].name ; i++) {
+ if (id->driver_data == mt_classes[i].name) {
+ mtclass = &(mt_classes[i]);
+ break;
+ }
+ }
+
+ /* This allows the driver to correctly support devices
+ * that emit events over several HID messages.
+ */
+ hdev->quirks |= HID_QUIRK_NO_INPUT_SYNC;
+
+ td = kzalloc(sizeof(struct mt_device) +
+ mtclass->maxcontacts * sizeof(struct mt_slot),
+ GFP_KERNEL);
+ if (!td) {
+ dev_err(&hdev->dev, "cannot allocate multitouch data\n");
+ return -ENOMEM;
+ }
+ td->mtclass = mtclass;
+ td->inputmode = -1;
+ hid_set_drvdata(hdev, td);
+
+ ret = hid_parse(hdev);
+ if (ret != 0)
+ goto fail;
+
+ ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT);
+ if (ret)
+ goto fail;
+
+ mt_set_input_mode(hdev);
+
+ return 0;
+
+fail:
+ kfree(td);
+ return ret;
+}
+
+#ifdef CONFIG_PM
+static int mt_reset_resume(struct hid_device *hdev)
+{
+ mt_set_input_mode(hdev);
+ return 0;
+}
+#endif
+
+static void mt_remove(struct hid_device *hdev)
+{
+ struct mt_device *td = hid_get_drvdata(hdev);
+ hid_hw_stop(hdev);
+ kfree(td);
+ hid_set_drvdata(hdev, NULL);
+}
+
+static const struct hid_device_id mt_devices[] = {
+
+ /* Cypress panel */
+ { .driver_data = MT_CLS_CYPRESS,
+ HID_USB_DEVICE(USB_VENDOR_ID_CYPRESS,
+ USB_DEVICE_ID_CYPRESS_TRUETOUCH) },
+
+ /* GeneralTouch panel */
+ { .driver_data = MT_CLS_DUAL2,
+ HID_USB_DEVICE(USB_VENDOR_ID_GENERAL_TOUCH,
+ USB_DEVICE_ID_GENERAL_TOUCH_WIN7_TWOFINGERS) },
+
+ /* PixCir-based panels */
+ { .driver_data = MT_CLS_DUAL1,
+ HID_USB_DEVICE(USB_VENDOR_ID_HANVON,
+ USB_DEVICE_ID_HANVON_MULTITOUCH) },
+ { .driver_data = MT_CLS_DUAL1,
+ HID_USB_DEVICE(USB_VENDOR_ID_CANDO,
+ USB_DEVICE_ID_CANDO_PIXCIR_MULTI_TOUCH) },
+
+ { }
+};
+MODULE_DEVICE_TABLE(hid, mt_devices);
+
+static const struct hid_usage_id mt_grabbed_usages[] = {
+ { HID_ANY_ID, HID_ANY_ID, HID_ANY_ID },
+ { HID_ANY_ID - 1, HID_ANY_ID - 1, HID_ANY_ID - 1}
+};
+
+static struct hid_driver mt_driver = {
+ .name = "hid-multitouch",
+ .id_table = mt_devices,
+ .probe = mt_probe,
+ .remove = mt_remove,
+ .input_mapping = mt_input_mapping,
+ .input_mapped = mt_input_mapped,
+ .feature_mapping = mt_feature_mapping,
+ .usage_table = mt_grabbed_usages,
+ .event = mt_event,
+#ifdef CONFIG_PM
+ .reset_resume = mt_reset_resume,
+#endif
+};
+
+static int __init mt_init(void)
+{
+ return hid_register_driver(&mt_driver);
+}
+
+static void __exit mt_exit(void)
+{
+ hid_unregister_driver(&mt_driver);
+}
+
+module_init(mt_init);
+module_exit(mt_exit);
diff --git a/drivers/hid/usbhid/hid-quirks.c b/drivers/hid/usbhid/hid-quirks.c
index 76b9a14..9a94b64 100644
--- a/drivers/hid/usbhid/hid-quirks.c
+++ b/drivers/hid/usbhid/hid-quirks.c
@@ -35,7 +35,6 @@
{ USB_VENDOR_ID_CHIC, USB_DEVICE_ID_CHIC_GAMEPAD, HID_QUIRK_BADPAD },
{ USB_VENDOR_ID_DWAV, USB_DEVICE_ID_EGALAX_TOUCHCONTROLLER, HID_QUIRK_MULTI_INPUT | HID_QUIRK_NOGET },
{ USB_VENDOR_ID_MOJO, USB_DEVICE_ID_RETRO_ADAPTER, HID_QUIRK_MULTI_INPUT },
- { USB_VENDOR_ID_TURBOX, USB_DEVICE_ID_TURBOX_TOUCHSCREEN_MOSART, HID_QUIRK_MULTI_INPUT },
{ USB_VENDOR_ID_HAPP, USB_DEVICE_ID_UGCI_DRIVING, HID_QUIRK_BADPAD | HID_QUIRK_MULTI_INPUT },
{ USB_VENDOR_ID_HAPP, USB_DEVICE_ID_UGCI_FLYING, HID_QUIRK_BADPAD | HID_QUIRK_MULTI_INPUT },
{ USB_VENDOR_ID_HAPP, USB_DEVICE_ID_UGCI_FIGHTING, HID_QUIRK_BADPAD | HID_QUIRK_MULTI_INPUT },
diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig
index bdc13d2..35f00da 100644
--- a/drivers/hwmon/Kconfig
+++ b/drivers/hwmon/Kconfig
@@ -809,10 +809,10 @@
will be called dme1737.
config SENSORS_EMC1403
- tristate "SMSC EMC1403 thermal sensor"
+ tristate "SMSC EMC1403/23 thermal sensor"
depends on I2C
help
- If you say yes here you get support for the SMSC EMC1403
+ If you say yes here you get support for the SMSC EMC1403/23
temperature monitoring chip.
Threshold values can be configured using sysfs.
diff --git a/drivers/hwmon/adm9240.c b/drivers/hwmon/adm9240.c
index 0727ad2..9e234b9 100644
--- a/drivers/hwmon/adm9240.c
+++ b/drivers/hwmon/adm9240.c
@@ -20,7 +20,7 @@
* Alarms 16-bit map of active alarms
* Analog Out 0..1250 mV output
*
- * Chassis Intrusion: clear CI latch with 'echo 1 > chassis_clear'
+ * Chassis Intrusion: clear CI latch with 'echo 0 > intrusion0_alarm'
*
* Test hardware: Intel SE440BX-2 desktop motherboard --Grant
*
@@ -476,13 +476,16 @@
static DEVICE_ATTR(aout_output, S_IRUGO | S_IWUSR, show_aout, set_aout);
/* chassis_clear */
-static ssize_t chassis_clear(struct device *dev,
+static ssize_t chassis_clear_legacy(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
struct i2c_client *client = to_i2c_client(dev);
unsigned long val = simple_strtol(buf, NULL, 10);
+ dev_warn(dev, "Attribute chassis_clear is deprecated, "
+ "use intrusion0_alarm instead\n");
+
if (val == 1) {
i2c_smbus_write_byte_data(client,
ADM9240_REG_CHASSIS_CLEAR, 0x80);
@@ -490,7 +493,29 @@
}
return count;
}
-static DEVICE_ATTR(chassis_clear, S_IWUSR, NULL, chassis_clear);
+static DEVICE_ATTR(chassis_clear, S_IWUSR, NULL, chassis_clear_legacy);
+
+static ssize_t chassis_clear(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct adm9240_data *data = i2c_get_clientdata(client);
+ unsigned long val;
+
+ if (strict_strtoul(buf, 10, &val) || val != 0)
+ return -EINVAL;
+
+ mutex_lock(&data->update_lock);
+ i2c_smbus_write_byte_data(client, ADM9240_REG_CHASSIS_CLEAR, 0x80);
+ data->valid = 0; /* Force cache refresh */
+ mutex_unlock(&data->update_lock);
+ dev_dbg(&client->dev, "chassis intrusion latch cleared\n");
+
+ return count;
+}
+static SENSOR_DEVICE_ATTR(intrusion0_alarm, S_IRUGO | S_IWUSR, show_alarm,
+ chassis_clear, 12);
static struct attribute *adm9240_attributes[] = {
&sensor_dev_attr_in0_input.dev_attr.attr,
@@ -532,6 +557,7 @@
&dev_attr_alarms.attr,
&dev_attr_aout_output.attr,
&dev_attr_chassis_clear.attr,
+ &sensor_dev_attr_intrusion0_alarm.dev_attr.attr,
&dev_attr_cpu0_vid.attr,
NULL
};
diff --git a/drivers/hwmon/ads7828.c b/drivers/hwmon/ads7828.c
index aac85f3..c42c5a6 100644
--- a/drivers/hwmon/ads7828.c
+++ b/drivers/hwmon/ads7828.c
@@ -4,7 +4,7 @@
This driver is based on the lm75 and other lm_sensors/hwmon drivers
- Written by Steve Hardy <steve@linuxrealtime.co.uk>
+ Written by Steve Hardy <shardy@redhat.com>
Datasheet available at: http://focus.ti.com/lit/ds/symlink/ads7828.pdf
@@ -271,7 +271,7 @@
i2c_del_driver(&ads7828_driver);
}
-MODULE_AUTHOR("Steve Hardy <steve@linuxrealtime.co.uk>");
+MODULE_AUTHOR("Steve Hardy <shardy@redhat.com>");
MODULE_DESCRIPTION("ADS7828 driver");
MODULE_LICENSE("GPL");
diff --git a/drivers/hwmon/dme1737.c b/drivers/hwmon/dme1737.c
index e9a610b..d9c5927 100644
--- a/drivers/hwmon/dme1737.c
+++ b/drivers/hwmon/dme1737.c
@@ -77,12 +77,14 @@
* in4 +12V
* in5 VTR (+3.3V stby)
* in6 Vbat
+ * in7 Vtrip (sch5127 only)
*
* --------------------------------------------------------------------- */
-/* Voltages (in) numbered 0-6 (ix) */
-#define DME1737_REG_IN(ix) ((ix) < 5 ? 0x20 + (ix) \
- : 0x94 + (ix))
+/* Voltages (in) numbered 0-7 (ix) */
+#define DME1737_REG_IN(ix) ((ix) < 5 ? 0x20 + (ix) : \
+ (ix) < 7 ? 0x94 + (ix) : \
+ 0x1f)
#define DME1737_REG_IN_MIN(ix) ((ix) < 5 ? 0x44 + (ix) * 2 \
: 0x91 + (ix) * 2)
#define DME1737_REG_IN_MAX(ix) ((ix) < 5 ? 0x45 + (ix) * 2 \
@@ -101,10 +103,11 @@
* IN_TEMP_LSB(1) = [temp3, temp1]
* IN_TEMP_LSB(2) = [in4, temp2]
* IN_TEMP_LSB(3) = [in3, in0]
- * IN_TEMP_LSB(4) = [in2, in1] */
+ * IN_TEMP_LSB(4) = [in2, in1]
+ * IN_TEMP_LSB(5) = [res, in7] */
#define DME1737_REG_IN_TEMP_LSB(ix) (0x84 + (ix))
-static const u8 DME1737_REG_IN_LSB[] = {3, 4, 4, 3, 2, 0, 0};
-static const u8 DME1737_REG_IN_LSB_SHL[] = {4, 4, 0, 0, 0, 0, 4};
+static const u8 DME1737_REG_IN_LSB[] = {3, 4, 4, 3, 2, 0, 0, 5};
+static const u8 DME1737_REG_IN_LSB_SHL[] = {4, 4, 0, 0, 0, 0, 4, 4};
static const u8 DME1737_REG_TEMP_LSB[] = {1, 2, 1};
static const u8 DME1737_REG_TEMP_LSB_SHL[] = {4, 4, 0};
@@ -145,7 +148,7 @@
#define DME1737_REG_ALARM1 0x41
#define DME1737_REG_ALARM2 0x42
#define DME1737_REG_ALARM3 0x83
-static const u8 DME1737_BIT_ALARM_IN[] = {0, 1, 2, 3, 8, 16, 17};
+static const u8 DME1737_BIT_ALARM_IN[] = {0, 1, 2, 3, 8, 16, 17, 18};
static const u8 DME1737_BIT_ALARM_TEMP[] = {4, 5, 6};
static const u8 DME1737_BIT_ALARM_FAN[] = {10, 11, 12, 13, 22, 23};
@@ -190,6 +193,7 @@
#define HAS_PWM_MIN (1 << 4) /* bit 4 */
#define HAS_FAN(ix) (1 << ((ix) + 5)) /* bits 5-10 */
#define HAS_PWM(ix) (1 << ((ix) + 11)) /* bits 11-16 */
+#define HAS_IN7 (1 << 17) /* bit 17 */
/* ---------------------------------------------------------------------
* Data structures and manipulation thereof
@@ -213,9 +217,9 @@
u32 has_features;
/* Register values */
- u16 in[7];
- u8 in_min[7];
- u8 in_max[7];
+ u16 in[8];
+ u8 in_min[8];
+ u8 in_max[8];
s16 temp[3];
s8 temp_min[3];
s8 temp_max[3];
@@ -247,7 +251,7 @@
static const int IN_NOMINAL_SCH5027[] = {5000, 2250, 3300, 1125, 1125, 3300,
3300};
static const int IN_NOMINAL_SCH5127[] = {2500, 2250, 3300, 1125, 1125, 3300,
- 3300};
+ 3300, 1500};
#define IN_NOMINAL(type) ((type) == sch311x ? IN_NOMINAL_SCH311x : \
(type) == sch5027 ? IN_NOMINAL_SCH5027 : \
(type) == sch5127 ? IN_NOMINAL_SCH5127 : \
@@ -580,7 +584,7 @@
{
struct dme1737_data *data = dev_get_drvdata(dev);
int ix;
- u8 lsb[5];
+ u8 lsb[6];
mutex_lock(&data->update_lock);
@@ -603,6 +607,9 @@
/* Voltage inputs are stored as 16 bit values even
* though they have only 12 bits resolution. This is
* to make it consistent with the temp inputs. */
+ if (ix == 7 && !(data->has_features & HAS_IN7)) {
+ continue;
+ }
data->in[ix] = dme1737_read(data,
DME1737_REG_IN(ix)) << 8;
data->in_min[ix] = dme1737_read(data,
@@ -635,10 +642,16 @@
* which the registers are read (MSB first, then LSB) is
* important! */
for (ix = 0; ix < ARRAY_SIZE(lsb); ix++) {
+ if (ix == 5 && !(data->has_features & HAS_IN7)) {
+ continue;
+ }
lsb[ix] = dme1737_read(data,
DME1737_REG_IN_TEMP_LSB(ix));
}
for (ix = 0; ix < ARRAY_SIZE(data->in); ix++) {
+ if (ix == 7 && !(data->has_features & HAS_IN7)) {
+ continue;
+ }
data->in[ix] |= (lsb[DME1737_REG_IN_LSB[ix]] <<
DME1737_REG_IN_LSB_SHL[ix]) & 0xf0;
}
@@ -762,7 +775,7 @@
/* ---------------------------------------------------------------------
* Voltage sysfs attributes
- * ix = [0-5]
+ * ix = [0-7]
* --------------------------------------------------------------------- */
#define SYS_IN_INPUT 0
@@ -1439,7 +1452,7 @@
* Sysfs device attribute defines and structs
* --------------------------------------------------------------------- */
-/* Voltages 0-6 */
+/* Voltages 0-7 */
#define SENSOR_DEVICE_ATTR_IN(ix) \
static SENSOR_DEVICE_ATTR_2(in##ix##_input, S_IRUGO, \
@@ -1458,6 +1471,7 @@
SENSOR_DEVICE_ATTR_IN(4);
SENSOR_DEVICE_ATTR_IN(5);
SENSOR_DEVICE_ATTR_IN(6);
+SENSOR_DEVICE_ATTR_IN(7);
/* Temperatures 1-3 */
@@ -1576,7 +1590,7 @@
* created unconditionally. The attributes that need modification of their
* permissions are created read-only and write permissions are added or removed
* on the fly when required */
-static struct attribute *dme1737_attr[] ={
+static struct attribute *dme1737_attr[] = {
/* Voltages */
&sensor_dev_attr_in0_input.dev_attr.attr,
&sensor_dev_attr_in0_min.dev_attr.attr,
@@ -1681,7 +1695,7 @@
};
-/* The following struct holds temp zone hysteresis related attributes, which
+/* The following struct holds temp zone hysteresis related attributes, which
* are not available in all chips. The following chips support them:
* DME1737, SCH311x */
static struct attribute *dme1737_zone_hyst_attr[] = {
@@ -1695,6 +1709,21 @@
.attrs = dme1737_zone_hyst_attr,
};
+/* The following struct holds voltage in7 related attributes, which
+ * are not available in all chips. The following chips support them:
+ * SCH5127 */
+static struct attribute *dme1737_in7_attr[] = {
+ &sensor_dev_attr_in7_input.dev_attr.attr,
+ &sensor_dev_attr_in7_min.dev_attr.attr,
+ &sensor_dev_attr_in7_max.dev_attr.attr,
+ &sensor_dev_attr_in7_alarm.dev_attr.attr,
+ NULL
+};
+
+static const struct attribute_group dme1737_in7_group = {
+ .attrs = dme1737_in7_attr,
+};
+
/* The following structs hold the PWM attributes, some of which are optional.
* Their creation depends on the chip configuration which is determined during
* module load. */
@@ -1986,6 +2015,9 @@
if (data->has_features & HAS_ZONE_HYST) {
sysfs_remove_group(&dev->kobj, &dme1737_zone_hyst_group);
}
+ if (data->has_features & HAS_IN7) {
+ sysfs_remove_group(&dev->kobj, &dme1737_in7_group);
+ }
sysfs_remove_group(&dev->kobj, &dme1737_group);
if (!data->client) {
@@ -1999,43 +2031,58 @@
int err, ix;
/* Create a name attribute for ISA devices */
- if (!data->client &&
- (err = sysfs_create_file(&dev->kobj, &dev_attr_name.attr))) {
- goto exit;
+ if (!data->client) {
+ err = sysfs_create_file(&dev->kobj, &dev_attr_name.attr);
+ if (err) {
+ goto exit;
+ }
}
/* Create standard sysfs attributes */
- if ((err = sysfs_create_group(&dev->kobj, &dme1737_group))) {
+ err = sysfs_create_group(&dev->kobj, &dme1737_group);
+ if (err) {
goto exit_remove;
}
/* Create chip-dependent sysfs attributes */
- if ((data->has_features & HAS_TEMP_OFFSET) &&
- (err = sysfs_create_group(&dev->kobj,
- &dme1737_temp_offset_group))) {
- goto exit_remove;
+ if (data->has_features & HAS_TEMP_OFFSET) {
+ err = sysfs_create_group(&dev->kobj,
+ &dme1737_temp_offset_group);
+ if (err) {
+ goto exit_remove;
+ }
}
- if ((data->has_features & HAS_VID) &&
- (err = sysfs_create_group(&dev->kobj,
- &dme1737_vid_group))) {
- goto exit_remove;
+ if (data->has_features & HAS_VID) {
+ err = sysfs_create_group(&dev->kobj, &dme1737_vid_group);
+ if (err) {
+ goto exit_remove;
+ }
}
- if ((data->has_features & HAS_ZONE3) &&
- (err = sysfs_create_group(&dev->kobj,
- &dme1737_zone3_group))) {
- goto exit_remove;
+ if (data->has_features & HAS_ZONE3) {
+ err = sysfs_create_group(&dev->kobj, &dme1737_zone3_group);
+ if (err) {
+ goto exit_remove;
+ }
}
- if ((data->has_features & HAS_ZONE_HYST) &&
- (err = sysfs_create_group(&dev->kobj,
- &dme1737_zone_hyst_group))) {
- goto exit_remove;
+ if (data->has_features & HAS_ZONE_HYST) {
+ err = sysfs_create_group(&dev->kobj, &dme1737_zone_hyst_group);
+ if (err) {
+ goto exit_remove;
+ }
+ }
+ if (data->has_features & HAS_IN7) {
+ err = sysfs_create_group(&dev->kobj, &dme1737_in7_group);
+ if (err) {
+ goto exit_remove;
+ }
}
/* Create fan sysfs attributes */
for (ix = 0; ix < ARRAY_SIZE(dme1737_fan_group); ix++) {
if (data->has_features & HAS_FAN(ix)) {
- if ((err = sysfs_create_group(&dev->kobj,
- &dme1737_fan_group[ix]))) {
+ err = sysfs_create_group(&dev->kobj,
+ &dme1737_fan_group[ix]);
+ if (err) {
goto exit_remove;
}
}
@@ -2044,14 +2091,17 @@
/* Create PWM sysfs attributes */
for (ix = 0; ix < ARRAY_SIZE(dme1737_pwm_group); ix++) {
if (data->has_features & HAS_PWM(ix)) {
- if ((err = sysfs_create_group(&dev->kobj,
- &dme1737_pwm_group[ix]))) {
+ err = sysfs_create_group(&dev->kobj,
+ &dme1737_pwm_group[ix]);
+ if (err) {
goto exit_remove;
}
- if ((data->has_features & HAS_PWM_MIN) && ix < 3 &&
- (err = sysfs_create_file(&dev->kobj,
- dme1737_auto_pwm_min_attr[ix]))) {
- goto exit_remove;
+ if ((data->has_features & HAS_PWM_MIN) && (ix < 3)) {
+ err = sysfs_create_file(&dev->kobj,
+ dme1737_auto_pwm_min_attr[ix]);
+ if (err) {
+ goto exit_remove;
+ }
}
}
}
@@ -2188,7 +2238,7 @@
data->has_features |= HAS_ZONE3;
break;
case sch5127:
- data->has_features |= HAS_FAN(2) | HAS_PWM(2);
+ data->has_features |= HAS_FAN(2) | HAS_PWM(2) | HAS_IN7;
break;
default:
break;
@@ -2281,8 +2331,9 @@
dme1737_sio_outb(sio_cip, 0x07, 0x0a);
/* Get the base address of the runtime registers */
- if (!(addr = (dme1737_sio_inb(sio_cip, 0x60) << 8) |
- dme1737_sio_inb(sio_cip, 0x61))) {
+ addr = (dme1737_sio_inb(sio_cip, 0x60) << 8) |
+ dme1737_sio_inb(sio_cip, 0x61);
+ if (!addr) {
err = -ENODEV;
goto exit;
}
@@ -2363,13 +2414,15 @@
mutex_init(&data->update_lock);
/* Initialize the DME1737 chip */
- if ((err = dme1737_init_device(dev))) {
+ err = dme1737_init_device(dev);
+ if (err) {
dev_err(dev, "Failed to initialize device.\n");
goto exit_kfree;
}
/* Create sysfs files */
- if ((err = dme1737_create_files(dev))) {
+ err = dme1737_create_files(dev);
+ if (err) {
dev_err(dev, "Failed to create sysfs files.\n");
goto exit_kfree;
}
@@ -2446,8 +2499,9 @@
dme1737_sio_outb(sio_cip, 0x07, 0x0a);
/* Get the base address of the runtime registers */
- if (!(base_addr = (dme1737_sio_inb(sio_cip, 0x60) << 8) |
- dme1737_sio_inb(sio_cip, 0x61))) {
+ base_addr = (dme1737_sio_inb(sio_cip, 0x60) << 8) |
+ dme1737_sio_inb(sio_cip, 0x61);
+ if (!base_addr) {
pr_err("Base address not set\n");
err = -ENODEV;
goto exit;
@@ -2476,18 +2530,21 @@
if (err)
goto exit;
- if (!(pdev = platform_device_alloc("dme1737", addr))) {
+ pdev = platform_device_alloc("dme1737", addr);
+ if (!pdev) {
pr_err("Failed to allocate device\n");
err = -ENOMEM;
goto exit;
}
- if ((err = platform_device_add_resources(pdev, &res, 1))) {
+ err = platform_device_add_resources(pdev, &res, 1);
+ if (err) {
pr_err("Failed to add device resource (err = %d)\n", err);
goto exit_device_put;
}
- if ((err = platform_device_add(pdev))) {
+ err = platform_device_add(pdev);
+ if (err) {
pr_err("Failed to add device (err = %d)\n", err);
goto exit_device_put;
}
@@ -2514,11 +2571,12 @@
dev_err(dev, "Failed to request region 0x%04x-0x%04x.\n",
(unsigned short)res->start,
(unsigned short)res->start + DME1737_EXTENT - 1);
- err = -EBUSY;
- goto exit;
- }
+ err = -EBUSY;
+ goto exit;
+ }
- if (!(data = kzalloc(sizeof(struct dme1737_data), GFP_KERNEL))) {
+ data = kzalloc(sizeof(struct dme1737_data), GFP_KERNEL);
+ if (!data) {
err = -ENOMEM;
goto exit_release_region;
}
@@ -2565,13 +2623,15 @@
data->type == sch5127 ? "SCH5127" : "SCH311x", data->addr);
/* Initialize the chip */
- if ((err = dme1737_init_device(dev))) {
+ err = dme1737_init_device(dev);
+ if (err) {
dev_err(dev, "Failed to initialize device.\n");
goto exit_kfree;
}
/* Create sysfs files */
- if ((err = dme1737_create_files(dev))) {
+ err = dme1737_create_files(dev);
+ if (err) {
dev_err(dev, "Failed to create sysfs files.\n");
goto exit_kfree;
}
@@ -2628,7 +2688,8 @@
int err;
unsigned short addr;
- if ((err = i2c_add_driver(&dme1737_i2c_driver))) {
+ err = i2c_add_driver(&dme1737_i2c_driver);
+ if (err) {
goto exit;
}
@@ -2641,12 +2702,14 @@
return 0;
}
- if ((err = platform_driver_register(&dme1737_isa_driver))) {
+ err = platform_driver_register(&dme1737_isa_driver);
+ if (err) {
goto exit_del_i2c_driver;
}
/* Sets global pdev as a side effect */
- if ((err = dme1737_isa_device_add(addr))) {
+ err = dme1737_isa_device_add(addr);
+ if (err) {
goto exit_del_isa_driver;
}
diff --git a/drivers/hwmon/emc1403.c b/drivers/hwmon/emc1403.c
index 8dee3f3..5dea9fa 100644
--- a/drivers/hwmon/emc1403.c
+++ b/drivers/hwmon/emc1403.c
@@ -269,23 +269,30 @@
struct i2c_board_info *info)
{
int id;
- /* Check if thermal chip is SMSC and EMC1403 */
+ /* Check if thermal chip is SMSC and EMC1403 or EMC1423 */
id = i2c_smbus_read_byte_data(client, THERMAL_SMSC_ID_REG);
if (id != 0x5d)
return -ENODEV;
+ id = i2c_smbus_read_byte_data(client, THERMAL_PID_REG);
+ switch (id) {
+ case 0x21:
+ strlcpy(info->type, "emc1403", I2C_NAME_SIZE);
+ break;
+ case 0x23:
+ strlcpy(info->type, "emc1423", I2C_NAME_SIZE);
+ break;
/* Note: 0x25 is the 1404 which is very similar and this
driver could be extended */
- id = i2c_smbus_read_byte_data(client, THERMAL_PID_REG);
- if (id != 0x21)
+ default:
return -ENODEV;
+ }
id = i2c_smbus_read_byte_data(client, THERMAL_REVISION_REG);
if (id != 0x01)
return -ENODEV;
- strlcpy(info->type, "emc1403", I2C_NAME_SIZE);
return 0;
}
@@ -342,6 +349,7 @@
static const struct i2c_device_id emc1403_idtable[] = {
{ "emc1403", 0 },
+ { "emc1423", 0 },
{ }
};
MODULE_DEVICE_TABLE(i2c, emc1403_idtable);
diff --git a/drivers/hwmon/fschmd.c b/drivers/hwmon/fschmd.c
index d4d4ca6..aa6d8b6 100644
--- a/drivers/hwmon/fschmd.c
+++ b/drivers/hwmon/fschmd.c
@@ -49,7 +49,6 @@
#include <linux/kref.h>
/* Addresses to scan */
-static DEFINE_MUTEX(watchdog_mutex);
static const unsigned short normal_i2c[] = { 0x73, I2C_CLIENT_END };
/* Insmod parameters */
@@ -850,7 +849,7 @@
static long watchdog_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
{
- static struct watchdog_info ident = {
+ struct watchdog_info ident = {
.options = WDIOF_KEEPALIVEPING | WDIOF_SETTIMEOUT |
WDIOF_CARDRESET,
.identity = "FSC watchdog"
@@ -858,7 +857,6 @@
int i, ret = 0;
struct fschmd_data *data = filp->private_data;
- mutex_lock(&watchdog_mutex);
switch (cmd) {
case WDIOC_GETSUPPORT:
ident.firmware_version = data->revision;
@@ -915,7 +913,6 @@
default:
ret = -ENOTTY;
}
- mutex_unlock(&watchdog_mutex);
return ret;
}
diff --git a/drivers/hwmon/it87.c b/drivers/hwmon/it87.c
index a428a92..316b648 100644
--- a/drivers/hwmon/it87.c
+++ b/drivers/hwmon/it87.c
@@ -38,6 +38,8 @@
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
#include <linux/module.h>
#include <linux/init.h>
#include <linux/slab.h>
@@ -1570,26 +1572,25 @@
case 0xffff: /* No device at all */
goto exit;
default:
- pr_debug(DRVNAME ": Unsupported chip (DEVID=0x%x)\n",
- chip_type);
+ pr_debug("Unsupported chip (DEVID=0x%x)\n", chip_type);
goto exit;
}
superio_select(PME);
if (!(superio_inb(IT87_ACT_REG) & 0x01)) {
- pr_info("it87: Device not activated, skipping\n");
+ pr_info("Device not activated, skipping\n");
goto exit;
}
*address = superio_inw(IT87_BASE_REG) & ~(IT87_EXTENT - 1);
if (*address == 0) {
- pr_info("it87: Base address not set, skipping\n");
+ pr_info("Base address not set, skipping\n");
goto exit;
}
err = 0;
sio_data->revision = superio_inb(DEVREV) & 0x0f;
- pr_info("it87: Found IT%04xF chip at 0x%x, revision %d\n",
+ pr_info("Found IT%04xF chip at 0x%x, revision %d\n",
chip_type, *address, sio_data->revision);
/* in8 (Vbat) is always internal */
@@ -1615,7 +1616,7 @@
} else {
/* We need at least 4 VID pins */
if (reg & 0x0f) {
- pr_info("it87: VID is disabled (pins used for GPIO)\n");
+ pr_info("VID is disabled (pins used for GPIO)\n");
sio_data->skip_vid = 1;
}
}
@@ -1651,7 +1652,7 @@
if (sio_data->type == it8720 && !(reg & (1 << 1))) {
reg |= (1 << 1);
superio_outb(IT87_SIO_PINX2_REG, reg);
- pr_notice("it87: Routing internal VCCH to in7\n");
+ pr_notice("Routing internal VCCH to in7\n");
}
if (reg & (1 << 0))
sio_data->internal |= (1 << 0);
@@ -1661,7 +1662,7 @@
sio_data->beep_pin = superio_inb(IT87_SIO_BEEP_PIN_REG) & 0x3f;
}
if (sio_data->beep_pin)
- pr_info("it87: Beeping is supported\n");
+ pr_info("Beeping is supported\n");
/* Disable specific features based on DMI strings */
board_vendor = dmi_get_system_info(DMI_BOARD_VENDOR);
@@ -1675,8 +1676,7 @@
the PWM2 duty cycle, so we disable it.
I use the board name string as the trigger in case
the same board is ever used in other systems. */
- pr_info("it87: Disabling pwm2 due to "
- "hardware constraints\n");
+ pr_info("Disabling pwm2 due to hardware constraints\n");
sio_data->skip_pwm = (1 << 1);
}
}
@@ -2189,28 +2189,26 @@
pdev = platform_device_alloc(DRVNAME, address);
if (!pdev) {
err = -ENOMEM;
- printk(KERN_ERR DRVNAME ": Device allocation failed\n");
+ pr_err("Device allocation failed\n");
goto exit;
}
err = platform_device_add_resources(pdev, &res, 1);
if (err) {
- printk(KERN_ERR DRVNAME ": Device resource addition failed "
- "(%d)\n", err);
+ pr_err("Device resource addition failed (%d)\n", err);
goto exit_device_put;
}
err = platform_device_add_data(pdev, sio_data,
sizeof(struct it87_sio_data));
if (err) {
- printk(KERN_ERR DRVNAME ": Platform data allocation failed\n");
+ pr_err("Platform data allocation failed\n");
goto exit_device_put;
}
err = platform_device_add(pdev);
if (err) {
- printk(KERN_ERR DRVNAME ": Device addition failed (%d)\n",
- err);
+ pr_err("Device addition failed (%d)\n", err);
goto exit_device_put;
}
diff --git a/drivers/hwmon/lm78.c b/drivers/hwmon/lm78.c
index 72ff2c4..4cb24ea 100644
--- a/drivers/hwmon/lm78.c
+++ b/drivers/hwmon/lm78.c
@@ -19,6 +19,8 @@
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
#include <linux/module.h>
#include <linux/init.h>
#include <linux/slab.h>
@@ -858,7 +860,7 @@
* individually for the probing phase. */
for (port = address; port < address + LM78_EXTENT; port++) {
if (!request_region(port, 1, "lm78")) {
- pr_debug("lm78: Failed to request port 0x%x\n", port);
+ pr_debug("Failed to request port 0x%x\n", port);
goto release;
}
}
@@ -920,7 +922,7 @@
found = 1;
if (found)
- pr_info("lm78: Found an %s chip at %#x\n",
+ pr_info("Found an %s chip at %#x\n",
val & 0x80 ? "LM79" : "LM78", (int)address);
release:
@@ -942,21 +944,19 @@
pdev = platform_device_alloc("lm78", address);
if (!pdev) {
err = -ENOMEM;
- printk(KERN_ERR "lm78: Device allocation failed\n");
+ pr_err("Device allocation failed\n");
goto exit;
}
err = platform_device_add_resources(pdev, &res, 1);
if (err) {
- printk(KERN_ERR "lm78: Device resource addition failed "
- "(%d)\n", err);
+ pr_err("Device resource addition failed (%d)\n", err);
goto exit_device_put;
}
err = platform_device_add(pdev);
if (err) {
- printk(KERN_ERR "lm78: Device addition failed (%d)\n",
- err);
+ pr_err("Device addition failed (%d)\n", err);
goto exit_device_put;
}
diff --git a/drivers/hwmon/pc87360.c b/drivers/hwmon/pc87360.c
index 68e69a4..3d99b88 100644
--- a/drivers/hwmon/pc87360.c
+++ b/drivers/hwmon/pc87360.c
@@ -33,6 +33,8 @@
* the standard Super-I/O addresses is used (0x2E/0x2F or 0x4E/0x4F).
*/
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
#include <linux/module.h>
#include <linux/init.h>
#include <linux/slab.h>
@@ -1031,16 +1033,15 @@
val = superio_inb(sioaddr, ACT);
if (!(val & 0x01)) {
- printk(KERN_INFO "pc87360: Device 0x%02x not "
- "activated\n", logdev[i]);
+ pr_info("Device 0x%02x not activated\n", logdev[i]);
continue;
}
val = (superio_inb(sioaddr, BASE) << 8)
| superio_inb(sioaddr, BASE + 1);
if (!val) {
- printk(KERN_INFO "pc87360: Base address not set for "
- "device 0x%02x\n", logdev[i]);
+ pr_info("Base address not set for device 0x%02x\n",
+ logdev[i]);
continue;
}
@@ -1050,17 +1051,15 @@
confreg[0] = superio_inb(sioaddr, 0xF0);
confreg[1] = superio_inb(sioaddr, 0xF1);
-#ifdef DEBUG
- printk(KERN_DEBUG "pc87360: Fan 1: mon=%d "
- "ctrl=%d inv=%d\n", (confreg[0]>>2)&1,
- (confreg[0]>>3)&1, (confreg[0]>>4)&1);
- printk(KERN_DEBUG "pc87360: Fan 2: mon=%d "
- "ctrl=%d inv=%d\n", (confreg[0]>>5)&1,
- (confreg[0]>>6)&1, (confreg[0]>>7)&1);
- printk(KERN_DEBUG "pc87360: Fan 3: mon=%d "
- "ctrl=%d inv=%d\n", confreg[1]&1,
- (confreg[1]>>1)&1, (confreg[1]>>2)&1);
-#endif
+ pr_debug("Fan %d: mon=%d ctrl=%d inv=%d\n", 1,
+ (confreg[0] >> 2) & 1, (confreg[0] >> 3) & 1,
+ (confreg[0] >> 4) & 1);
+ pr_debug("Fan %d: mon=%d ctrl=%d inv=%d\n", 2,
+ (confreg[0] >> 5) & 1, (confreg[0] >> 6) & 1,
+ (confreg[0] >> 7) & 1);
+ pr_debug("Fan %d: mon=%d ctrl=%d inv=%d\n", 3,
+ confreg[1] & 1, (confreg[1] >> 1) & 1,
+ (confreg[1] >> 2) & 1);
} else if (i==1) { /* Voltages */
/* Are we using thermistors? */
if (*devid == 0xE9) { /* PC87366 */
@@ -1071,14 +1070,12 @@
confreg[3] = superio_inb(sioaddr, 0x25);
if (confreg[2] & 0x40) {
- printk(KERN_INFO "pc87360: Using "
- "thermistors for temperature "
- "monitoring\n");
+ pr_info("Using thermistors for "
+ "temperature monitoring\n");
}
if (confreg[3] & 0xE0) {
- printk(KERN_INFO "pc87360: VID "
- "inputs routed (mode %u)\n",
- confreg[3] >> 5);
+ pr_info("VID inputs routed (mode %u)\n",
+ confreg[3] >> 5);
}
}
}
@@ -1616,7 +1613,7 @@
pdev = platform_device_alloc("pc87360", address);
if (!pdev) {
err = -ENOMEM;
- printk(KERN_ERR "pc87360: Device allocation failed\n");
+ pr_err("Device allocation failed\n");
goto exit;
}
@@ -1639,15 +1636,13 @@
err = platform_device_add_resources(pdev, res, res_count);
if (err) {
- printk(KERN_ERR "pc87360: Device resources addition failed "
- "(%d)\n", err);
+ pr_err("Device resources addition failed (%d)\n", err);
goto exit_device_put;
}
err = platform_device_add(pdev);
if (err) {
- printk(KERN_ERR "pc87360: Device addition failed (%d)\n",
- err);
+ pr_err("Device addition failed (%d)\n", err);
goto exit_device_put;
}
@@ -1666,8 +1661,7 @@
if (pc87360_find(0x2e, &devid, extra_isa)
&& pc87360_find(0x4e, &devid, extra_isa)) {
- printk(KERN_WARNING "pc87360: PC8736x not detected, "
- "module not inserted.\n");
+ pr_warn("PC8736x not detected, module not inserted\n");
return -ENODEV;
}
@@ -1680,8 +1674,7 @@
}
if (address == 0x0000) {
- printk(KERN_WARNING "pc87360: No active logical device, "
- "module not inserted.\n");
+ pr_warn("No active logical device, module not inserted\n");
return -ENODEV;
}
diff --git a/drivers/hwmon/pc87427.c b/drivers/hwmon/pc87427.c
index 9ec4daa..8da2181 100644
--- a/drivers/hwmon/pc87427.c
+++ b/drivers/hwmon/pc87427.c
@@ -22,6 +22,8 @@
* mode, and voltages aren't supported at all.
*/
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
#include <linux/module.h>
#include <linux/init.h>
#include <linux/slab.h>
@@ -1077,7 +1079,7 @@
data = kzalloc(sizeof(struct pc87427_data), GFP_KERNEL);
if (!data) {
err = -ENOMEM;
- printk(KERN_ERR DRVNAME ": Out of memory\n");
+ pr_err("Out of memory\n");
goto exit;
}
@@ -1196,28 +1198,26 @@
pdev = platform_device_alloc(DRVNAME, res[0].start);
if (!pdev) {
err = -ENOMEM;
- printk(KERN_ERR DRVNAME ": Device allocation failed\n");
+ pr_err("Device allocation failed\n");
goto exit;
}
err = platform_device_add_resources(pdev, res, res_count);
if (err) {
- printk(KERN_ERR DRVNAME ": Device resource addition failed "
- "(%d)\n", err);
+ pr_err("Device resource addition failed (%d)\n", err);
goto exit_device_put;
}
err = platform_device_add_data(pdev, sio_data,
sizeof(struct pc87427_sio_data));
if (err) {
- printk(KERN_ERR DRVNAME ": Platform data allocation failed\n");
+ pr_err("Platform data allocation failed\n");
goto exit_device_put;
}
err = platform_device_add(pdev);
if (err) {
- printk(KERN_ERR DRVNAME ": Device addition failed (%d)\n",
- err);
+ pr_err("Device addition failed (%d)\n", err);
goto exit_device_put;
}
@@ -1249,23 +1249,23 @@
val = superio_inb(sioaddr, SIOREG_ACT);
if (!(val & 0x01)) {
- printk(KERN_INFO DRVNAME ": Logical device 0x%02x "
- "not activated\n", logdev[i]);
+ pr_info("Logical device 0x%02x not activated\n",
+ logdev[i]);
continue;
}
val = superio_inb(sioaddr, SIOREG_MAP);
if (val & 0x01) {
- printk(KERN_WARNING DRVNAME ": Logical device 0x%02x "
- "is memory-mapped, can't use\n", logdev[i]);
+ pr_warn("Logical device 0x%02x is memory-mapped, "
+ "can't use\n", logdev[i]);
continue;
}
val = (superio_inb(sioaddr, SIOREG_IOBASE) << 8)
| superio_inb(sioaddr, SIOREG_IOBASE + 1);
if (!val) {
- printk(KERN_INFO DRVNAME ": I/O base address not set "
- "for logical device 0x%02x\n", logdev[i]);
+ pr_info("I/O base address not set for logical device "
+ "0x%02x\n", logdev[i]);
continue;
}
sio_data->address[i] = val;
diff --git a/drivers/hwmon/via686a.c b/drivers/hwmon/via686a.c
index 13e8d21..25e9166 100644
--- a/drivers/hwmon/via686a.c
+++ b/drivers/hwmon/via686a.c
@@ -689,6 +689,13 @@
return 0;
}
+static void via686a_update_fan_div(struct via686a_data *data)
+{
+ int reg = via686a_read_value(data, VIA686A_REG_FANDIV);
+ data->fan_div[0] = (reg >> 4) & 0x03;
+ data->fan_div[1] = reg >> 6;
+}
+
static void __devinit via686a_init_device(struct via686a_data *data)
{
u8 reg;
@@ -702,6 +709,9 @@
via686a_write_value(data, VIA686A_REG_TEMP_MODE,
(reg & ~VIA686A_TEMP_MODE_MASK)
| VIA686A_TEMP_MODE_CONTINUOUS);
+
+ /* Pre-read fan clock divisor values */
+ via686a_update_fan_div(data);
}
static struct via686a_data *via686a_update_device(struct device *dev)
@@ -753,9 +763,7 @@
(via686a_read_value(data, VIA686A_REG_TEMP_LOW23) &
0xc0) >> 6;
- i = via686a_read_value(data, VIA686A_REG_FANDIV);
- data->fan_div[0] = (i >> 4) & 0x03;
- data->fan_div[1] = i >> 6;
+ via686a_update_fan_div(data);
data->alarms =
via686a_read_value(data,
VIA686A_REG_ALARM1) |
diff --git a/drivers/hwmon/w83781d.c b/drivers/hwmon/w83781d.c
index c84b9b4..eed43a0 100644
--- a/drivers/hwmon/w83781d.c
+++ b/drivers/hwmon/w83781d.c
@@ -33,6 +33,8 @@
*/
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
#include <linux/module.h>
#include <linux/init.h>
#include <linux/slab.h>
@@ -1798,8 +1800,7 @@
* individually for the probing phase. */
for (port = address; port < address + W83781D_EXTENT; port++) {
if (!request_region(port, 1, "w83781d")) {
- pr_debug("w83781d: Failed to request port 0x%x\n",
- port);
+ pr_debug("Failed to request port 0x%x\n", port);
goto release;
}
}
@@ -1811,7 +1812,7 @@
if (inb_p(address + 2) != val
|| inb_p(address + 3) != val
|| inb_p(address + 7) != val) {
- pr_debug("w83781d: Detection failed at step 1\n");
+ pr_debug("Detection failed at step %d\n", 1);
goto release;
}
#undef REALLY_SLOW_IO
@@ -1820,14 +1821,14 @@
MSB (busy flag) should be clear initially, set after the write. */
save = inb_p(address + W83781D_ADDR_REG_OFFSET);
if (save & 0x80) {
- pr_debug("w83781d: Detection failed at step 2\n");
+ pr_debug("Detection failed at step %d\n", 2);
goto release;
}
val = ~save & 0x7f;
outb_p(val, address + W83781D_ADDR_REG_OFFSET);
if (inb_p(address + W83781D_ADDR_REG_OFFSET) != (val | 0x80)) {
outb_p(save, address + W83781D_ADDR_REG_OFFSET);
- pr_debug("w83781d: Detection failed at step 3\n");
+ pr_debug("Detection failed at step %d\n", 3);
goto release;
}
@@ -1835,7 +1836,7 @@
outb_p(W83781D_REG_CONFIG, address + W83781D_ADDR_REG_OFFSET);
val = inb_p(address + W83781D_DATA_REG_OFFSET);
if (val & 0x80) {
- pr_debug("w83781d: Detection failed at step 4\n");
+ pr_debug("Detection failed at step %d\n", 4);
goto release;
}
outb_p(W83781D_REG_BANK, address + W83781D_ADDR_REG_OFFSET);
@@ -1844,19 +1845,19 @@
val = inb_p(address + W83781D_DATA_REG_OFFSET);
if ((!(save & 0x80) && (val != 0xa3))
|| ((save & 0x80) && (val != 0x5c))) {
- pr_debug("w83781d: Detection failed at step 5\n");
+ pr_debug("Detection failed at step %d\n", 5);
goto release;
}
outb_p(W83781D_REG_I2C_ADDR, address + W83781D_ADDR_REG_OFFSET);
val = inb_p(address + W83781D_DATA_REG_OFFSET);
if (val < 0x03 || val > 0x77) { /* Not a valid I2C address */
- pr_debug("w83781d: Detection failed at step 6\n");
+ pr_debug("Detection failed at step %d\n", 6);
goto release;
}
/* The busy flag should be clear again */
if (inb_p(address + W83781D_ADDR_REG_OFFSET) & 0x80) {
- pr_debug("w83781d: Detection failed at step 7\n");
+ pr_debug("Detection failed at step %d\n", 7);
goto release;
}
@@ -1871,7 +1872,7 @@
found = 1;
if (found)
- pr_info("w83781d: Found a %s chip at %#x\n",
+ pr_info("Found a %s chip at %#x\n",
val == 0x30 ? "W83782D" : "W83781D", (int)address);
release:
@@ -1894,21 +1895,19 @@
pdev = platform_device_alloc("w83781d", address);
if (!pdev) {
err = -ENOMEM;
- printk(KERN_ERR "w83781d: Device allocation failed\n");
+ pr_err("Device allocation failed\n");
goto exit;
}
err = platform_device_add_resources(pdev, &res, 1);
if (err) {
- printk(KERN_ERR "w83781d: Device resource addition failed "
- "(%d)\n", err);
+ pr_err("Device resource addition failed (%d)\n", err);
goto exit_device_put;
}
err = platform_device_add(pdev);
if (err) {
- printk(KERN_ERR "w83781d: Device addition failed (%d)\n",
- err);
+ pr_err("Device addition failed (%d)\n", err);
goto exit_device_put;
}
diff --git a/drivers/hwmon/w83792d.c b/drivers/hwmon/w83792d.c
index 679718e..63841f8 100644
--- a/drivers/hwmon/w83792d.c
+++ b/drivers/hwmon/w83792d.c
@@ -691,7 +691,7 @@
}
static ssize_t
-show_regs_chassis(struct device *dev, struct device_attribute *attr,
+show_chassis(struct device *dev, struct device_attribute *attr,
char *buf)
{
struct w83792d_data *data = w83792d_update_device(dev);
@@ -699,6 +699,16 @@
}
static ssize_t
+show_regs_chassis(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ dev_warn(dev,
+ "Attribute %s is deprecated, use intrusion0_alarm instead\n",
+ "chassis");
+ return show_chassis(dev, attr, buf);
+}
+
+static ssize_t
show_chassis_clear(struct device *dev, struct device_attribute *attr, char *buf)
{
struct w83792d_data *data = w83792d_update_device(dev);
@@ -706,7 +716,7 @@
}
static ssize_t
-store_chassis_clear(struct device *dev, struct device_attribute *attr,
+store_chassis_clear_legacy(struct device *dev, struct device_attribute *attr,
const char *buf, size_t count)
{
struct i2c_client *client = to_i2c_client(dev);
@@ -714,6 +724,10 @@
u32 val;
u8 temp1 = 0, temp2 = 0;
+ dev_warn(dev,
+ "Attribute %s is deprecated, use intrusion0_alarm instead\n",
+ "chassis_clear");
+
val = simple_strtoul(buf, NULL, 10);
mutex_lock(&data->update_lock);
data->chassis_clear = SENSORS_LIMIT(val, 0 ,1);
@@ -726,6 +740,27 @@
return count;
}
+static ssize_t
+store_chassis_clear(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct w83792d_data *data = i2c_get_clientdata(client);
+ unsigned long val;
+ u8 reg;
+
+ if (strict_strtoul(buf, 10, &val) || val != 0)
+ return -EINVAL;
+
+ mutex_lock(&data->update_lock);
+ reg = w83792d_read_value(client, W83792D_REG_CHASSIS_CLR);
+ w83792d_write_value(client, W83792D_REG_CHASSIS_CLR, reg | 0x80);
+ data->valid = 0; /* Force cache refresh */
+ mutex_unlock(&data->update_lock);
+
+ return count;
+}
+
/* For Smart Fan I / Thermal Cruise */
static ssize_t
show_thermal_cruise(struct device *dev, struct device_attribute *attr,
@@ -1012,7 +1047,9 @@
static SENSOR_DEVICE_ATTR(fan6_alarm, S_IRUGO, show_alarm, NULL, 23);
static DEVICE_ATTR(chassis, S_IRUGO, show_regs_chassis, NULL);
static DEVICE_ATTR(chassis_clear, S_IRUGO | S_IWUSR,
- show_chassis_clear, store_chassis_clear);
+ show_chassis_clear, store_chassis_clear_legacy);
+static DEVICE_ATTR(intrusion0_alarm, S_IRUGO | S_IWUSR,
+ show_chassis, store_chassis_clear);
static SENSOR_DEVICE_ATTR(pwm1, S_IWUSR | S_IRUGO, show_pwm, store_pwm, 0);
static SENSOR_DEVICE_ATTR(pwm2, S_IWUSR | S_IRUGO, show_pwm, store_pwm, 1);
static SENSOR_DEVICE_ATTR(pwm3, S_IWUSR | S_IRUGO, show_pwm, store_pwm, 2);
@@ -1214,6 +1251,7 @@
&dev_attr_alarms.attr,
&dev_attr_chassis.attr,
&dev_attr_chassis_clear.attr,
+ &dev_attr_intrusion0_alarm.attr,
&sensor_dev_attr_tolerance1.dev_attr.attr,
&sensor_dev_attr_thermal_cruise1.dev_attr.attr,
&sensor_dev_attr_tolerance2.dev_attr.attr,
diff --git a/drivers/hwmon/w83793.c b/drivers/hwmon/w83793.c
index 8e540ad..e3bdedf 100644
--- a/drivers/hwmon/w83793.c
+++ b/drivers/hwmon/w83793.c
@@ -51,7 +51,6 @@
#define WATCHDOG_TIMEOUT 2 /* 2 minute default timeout */
/* Addresses to scan */
-static DEFINE_MUTEX(watchdog_mutex);
static const unsigned short normal_i2c[] = { 0x2c, 0x2d, 0x2e, 0x2f,
I2C_CLIENT_END };
@@ -421,18 +420,43 @@
/* Write any value to clear chassis alarm */
static ssize_t
+store_chassis_clear_legacy(struct device *dev,
+ struct device_attribute *attr, const char *buf,
+ size_t count)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct w83793_data *data = i2c_get_clientdata(client);
+ u8 val;
+
+ dev_warn(dev, "Attribute chassis is deprecated, "
+ "use intrusion0_alarm instead\n");
+
+ mutex_lock(&data->update_lock);
+ val = w83793_read_value(client, W83793_REG_CLR_CHASSIS);
+ val |= 0x80;
+ w83793_write_value(client, W83793_REG_CLR_CHASSIS, val);
+ mutex_unlock(&data->update_lock);
+ return count;
+}
+
+/* Write 0 to clear chassis alarm */
+static ssize_t
store_chassis_clear(struct device *dev,
struct device_attribute *attr, const char *buf,
size_t count)
{
struct i2c_client *client = to_i2c_client(dev);
struct w83793_data *data = i2c_get_clientdata(client);
- u8 val;
+ unsigned long val;
+ u8 reg;
+
+ if (strict_strtoul(buf, 10, &val) || val != 0)
+ return -EINVAL;
mutex_lock(&data->update_lock);
- val = w83793_read_value(client, W83793_REG_CLR_CHASSIS);
- val |= 0x80;
- w83793_write_value(client, W83793_REG_CLR_CHASSIS, val);
+ reg = w83793_read_value(client, W83793_REG_CLR_CHASSIS);
+ w83793_write_value(client, W83793_REG_CLR_CHASSIS, reg | 0x80);
+ data->valid = 0; /* Force cache refresh */
mutex_unlock(&data->update_lock);
return count;
}
@@ -1102,6 +1126,8 @@
static struct sensor_device_attribute_2 sda_single_files[] = {
SENSOR_ATTR_2(chassis, S_IWUSR | S_IRUGO, show_alarm_beep,
+ store_chassis_clear_legacy, ALARM_STATUS, 30),
+ SENSOR_ATTR_2(intrusion0_alarm, S_IWUSR | S_IRUGO, show_alarm_beep,
store_chassis_clear, ALARM_STATUS, 30),
SENSOR_ATTR_2(beep_enable, S_IWUSR | S_IRUGO, show_beep_enable,
store_beep_enable, NOT_USED, NOT_USED),
@@ -1323,7 +1349,7 @@
static long watchdog_ioctl(struct file *filp, unsigned int cmd,
unsigned long arg)
{
- static struct watchdog_info ident = {
+ struct watchdog_info ident = {
.options = WDIOF_KEEPALIVEPING |
WDIOF_SETTIMEOUT |
WDIOF_CARDRESET,
@@ -1333,7 +1359,6 @@
int val, ret = 0;
struct w83793_data *data = filp->private_data;
- mutex_lock(&watchdog_mutex);
switch (cmd) {
case WDIOC_GETSUPPORT:
if (!nowayout)
@@ -1387,7 +1412,6 @@
default:
ret = -ENOTTY;
}
- mutex_unlock(&watchdog_mutex);
return ret;
}
diff --git a/drivers/hwmon/w83795.c b/drivers/hwmon/w83795.c
index cdbc744..845232d 100644
--- a/drivers/hwmon/w83795.c
+++ b/drivers/hwmon/w83795.c
@@ -458,6 +458,7 @@
{
struct w83795_data *data = i2c_get_clientdata(client);
int i, limit;
+ u8 lsb;
/* Read the voltage limits */
for (i = 0; i < ARRAY_SIZE(data->in); i++) {
@@ -479,9 +480,8 @@
}
/* Read the fan limits */
+ lsb = 0; /* Silent false gcc warning */
for (i = 0; i < ARRAY_SIZE(data->fan); i++) {
- u8 lsb;
-
/* Each register contains LSB for 2 fans, but we want to
* read it only once to save time */
if ((i & 1) == 0 && (data->has_fan & (3 << i)))
diff --git a/drivers/i2c/busses/i2c-nomadik.c b/drivers/i2c/busses/i2c-nomadik.c
index c9fffd0..2bd3469 100644
--- a/drivers/i2c/busses/i2c-nomadik.c
+++ b/drivers/i2c/busses/i2c-nomadik.c
@@ -434,7 +434,7 @@
}
if (timeout == 0) {
- /* controler has timedout, re-init the h/w */
+ /* controller has timedout, re-init the h/w */
dev_err(&dev->pdev->dev, "controller timed out, re-init h/w\n");
(void) init_hw(dev);
status = -ETIMEDOUT;
@@ -498,7 +498,7 @@
}
if (timeout == 0) {
- /* controler has timedout, re-init the h/w */
+ /* controller has timedout, re-init the h/w */
dev_err(&dev->pdev->dev, "controller timed out, re-init h/w\n");
(void) init_hw(dev);
status = -ETIMEDOUT;
diff --git a/drivers/infiniband/hw/cxgb3/cxio_wr.h b/drivers/infiniband/hw/cxgb3/cxio_wr.h
index 4bb997a..83d2e19 100644
--- a/drivers/infiniband/hw/cxgb3/cxio_wr.h
+++ b/drivers/infiniband/hw/cxgb3/cxio_wr.h
@@ -689,7 +689,7 @@
* A T3 WQ implements both the SQ and RQ.
*/
struct t3_wq {
- union t3_wr *queue; /* DMA accessable memory */
+ union t3_wr *queue; /* DMA accessible memory */
dma_addr_t dma_addr; /* DMA address for HW */
DEFINE_DMA_UNMAP_ADDR(mapping); /* unmap kruft */
u32 error; /* 1 once we go to ERROR */
diff --git a/drivers/infiniband/hw/cxgb4/iw_cxgb4.h b/drivers/infiniband/hw/cxgb4/iw_cxgb4.h
index cc600c2..2fe19ec 100644
--- a/drivers/infiniband/hw/cxgb4/iw_cxgb4.h
+++ b/drivers/infiniband/hw/cxgb4/iw_cxgb4.h
@@ -46,7 +46,6 @@
#include <linux/timer.h>
#include <linux/io.h>
#include <linux/kfifo.h>
-#include <linux/mutex.h>
#include <asm/byteorder.h>
diff --git a/drivers/infiniband/hw/qib/qib_iba7322.c b/drivers/infiniband/hw/qib/qib_iba7322.c
index dbbb0e8..abd409d 100644
--- a/drivers/infiniband/hw/qib/qib_iba7322.c
+++ b/drivers/infiniband/hw/qib/qib_iba7322.c
@@ -332,7 +332,7 @@
#define krp_serdesctrl KREG_IBPORT_IDX(IBSerdesCtrl)
/*
- * Per-context kernel registers. Acess only with qib_read_kreg_ctxt()
+ * Per-context kernel registers. Access only with qib_read_kreg_ctxt()
* or qib_write_kreg_ctxt()
*/
#define krc_rcvhdraddr KREG_IDX(RcvHdrAddr0)
diff --git a/drivers/input/joystick/Kconfig b/drivers/input/joystick/Kconfig
index 5b59616..56eb471 100644
--- a/drivers/input/joystick/Kconfig
+++ b/drivers/input/joystick/Kconfig
@@ -255,6 +255,16 @@
To compile this driver as a module, choose M here: the
module will be called amijoy.
+config JOYSTICK_AS5011
+ tristate "Austria Microsystem AS5011 joystick"
+ depends on I2C
+ help
+ Say Y here if you have an AS5011 digital joystick connected to your
+ system.
+
+ To compile this driver as a module, choose M here: the
+ module will be called as5011.
+
config JOYSTICK_JOYDUMP
tristate "Gameport data dumper"
select GAMEPORT
diff --git a/drivers/input/joystick/Makefile b/drivers/input/joystick/Makefile
index f3a8cbe..92dc0de 100644
--- a/drivers/input/joystick/Makefile
+++ b/drivers/input/joystick/Makefile
@@ -7,6 +7,7 @@
obj-$(CONFIG_JOYSTICK_A3D) += a3d.o
obj-$(CONFIG_JOYSTICK_ADI) += adi.o
obj-$(CONFIG_JOYSTICK_AMIGA) += amijoy.o
+obj-$(CONFIG_JOYSTICK_AS5011) += as5011.o
obj-$(CONFIG_JOYSTICK_ANALOG) += analog.o
obj-$(CONFIG_JOYSTICK_COBRA) += cobra.o
obj-$(CONFIG_JOYSTICK_DB9) += db9.o
diff --git a/drivers/input/joystick/as5011.c b/drivers/input/joystick/as5011.c
new file mode 100644
index 0000000..f6732b5
--- /dev/null
+++ b/drivers/input/joystick/as5011.c
@@ -0,0 +1,367 @@
+/*
+ * Copyright (c) 2010, 2011 Fabien Marteau <fabien.marteau@armadeus.com>
+ * Sponsored by ARMadeus Systems
+ *
+ * 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
+ *
+ * Driver for Austria Microsystems joysticks AS5011
+ *
+ * TODO:
+ * - Power on the chip when open() and power down when close()
+ * - Manage power mode
+ */
+
+#include <linux/i2c.h>
+#include <linux/interrupt.h>
+#include <linux/input.h>
+#include <linux/gpio.h>
+#include <linux/delay.h>
+#include <linux/input/as5011.h>
+#include <linux/slab.h>
+
+#define DRIVER_DESC "Driver for Austria Microsystems AS5011 joystick"
+#define MODULE_DEVICE_ALIAS "as5011"
+
+MODULE_AUTHOR("Fabien Marteau <fabien.marteau@armadeus.com>");
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
+
+/* registers */
+#define AS5011_CTRL1 0x76
+#define AS5011_CTRL2 0x75
+#define AS5011_XP 0x43
+#define AS5011_XN 0x44
+#define AS5011_YP 0x53
+#define AS5011_YN 0x54
+#define AS5011_X_REG 0x41
+#define AS5011_Y_REG 0x42
+#define AS5011_X_RES_INT 0x51
+#define AS5011_Y_RES_INT 0x52
+
+/* CTRL1 bits */
+#define AS5011_CTRL1_LP_PULSED 0x80
+#define AS5011_CTRL1_LP_ACTIVE 0x40
+#define AS5011_CTRL1_LP_CONTINUE 0x20
+#define AS5011_CTRL1_INT_WUP_EN 0x10
+#define AS5011_CTRL1_INT_ACT_EN 0x08
+#define AS5011_CTRL1_EXT_CLK_EN 0x04
+#define AS5011_CTRL1_SOFT_RST 0x02
+#define AS5011_CTRL1_DATA_VALID 0x01
+
+/* CTRL2 bits */
+#define AS5011_CTRL2_EXT_SAMPLE_EN 0x08
+#define AS5011_CTRL2_RC_BIAS_ON 0x04
+#define AS5011_CTRL2_INV_SPINNING 0x02
+
+#define AS5011_MAX_AXIS 80
+#define AS5011_MIN_AXIS (-80)
+#define AS5011_FUZZ 8
+#define AS5011_FLAT 40
+
+struct as5011_device {
+ struct input_dev *input_dev;
+ struct i2c_client *i2c_client;
+ unsigned int button_gpio;
+ unsigned int button_irq;
+ unsigned int axis_irq;
+};
+
+static int as5011_i2c_write(struct i2c_client *client,
+ uint8_t aregaddr,
+ uint8_t avalue)
+{
+ uint8_t data[2] = { aregaddr, avalue };
+ struct i2c_msg msg = {
+ client->addr, I2C_M_IGNORE_NAK, 2, (uint8_t *)data
+ };
+ int error;
+
+ error = i2c_transfer(client->adapter, &msg, 1);
+ return error < 0 ? error : 0;
+}
+
+static int as5011_i2c_read(struct i2c_client *client,
+ uint8_t aregaddr, signed char *value)
+{
+ uint8_t data[2] = { aregaddr };
+ struct i2c_msg msg_set[2] = {
+ { client->addr, I2C_M_REV_DIR_ADDR, 1, (uint8_t *)data },
+ { client->addr, I2C_M_RD | I2C_M_NOSTART, 1, (uint8_t *)data }
+ };
+ int error;
+
+ error = i2c_transfer(client->adapter, msg_set, 2);
+ if (error < 0)
+ return error;
+
+ *value = data[0] & 0x80 ? -1 * (1 + ~data[0]) : data[0];
+ return 0;
+}
+
+static irqreturn_t as5011_button_interrupt(int irq, void *dev_id)
+{
+ struct as5011_device *as5011 = dev_id;
+ int val = gpio_get_value_cansleep(as5011->button_gpio);
+
+ input_report_key(as5011->input_dev, BTN_JOYSTICK, !val);
+ input_sync(as5011->input_dev);
+
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t as5011_axis_interrupt(int irq, void *dev_id)
+{
+ struct as5011_device *as5011 = dev_id;
+ int error;
+ signed char x, y;
+
+ error = as5011_i2c_read(as5011->i2c_client, AS5011_X_RES_INT, &x);
+ if (error < 0)
+ goto out;
+
+ error = as5011_i2c_read(as5011->i2c_client, AS5011_Y_RES_INT, &y);
+ if (error < 0)
+ goto out;
+
+ input_report_abs(as5011->input_dev, ABS_X, x);
+ input_report_abs(as5011->input_dev, ABS_Y, y);
+ input_sync(as5011->input_dev);
+
+out:
+ return IRQ_HANDLED;
+}
+
+static int __devinit as5011_configure_chip(struct as5011_device *as5011,
+ const struct as5011_platform_data *plat_dat)
+{
+ struct i2c_client *client = as5011->i2c_client;
+ int error;
+ signed char value;
+
+ /* chip soft reset */
+ error = as5011_i2c_write(client, AS5011_CTRL1,
+ AS5011_CTRL1_SOFT_RST);
+ if (error < 0) {
+ dev_err(&client->dev, "Soft reset failed\n");
+ return error;
+ }
+
+ mdelay(10);
+
+ error = as5011_i2c_write(client, AS5011_CTRL1,
+ AS5011_CTRL1_LP_PULSED |
+ AS5011_CTRL1_LP_ACTIVE |
+ AS5011_CTRL1_INT_ACT_EN);
+ if (error < 0) {
+ dev_err(&client->dev, "Power config failed\n");
+ return error;
+ }
+
+ error = as5011_i2c_write(client, AS5011_CTRL2,
+ AS5011_CTRL2_INV_SPINNING);
+ if (error < 0) {
+ dev_err(&client->dev, "Can't invert spinning\n");
+ return error;
+ }
+
+ /* write threshold */
+ error = as5011_i2c_write(client, AS5011_XP, plat_dat->xp);
+ if (error < 0) {
+ dev_err(&client->dev, "Can't write threshold\n");
+ return error;
+ }
+
+ error = as5011_i2c_write(client, AS5011_XN, plat_dat->xn);
+ if (error < 0) {
+ dev_err(&client->dev, "Can't write threshold\n");
+ return error;
+ }
+
+ error = as5011_i2c_write(client, AS5011_YP, plat_dat->yp);
+ if (error < 0) {
+ dev_err(&client->dev, "Can't write threshold\n");
+ return error;
+ }
+
+ error = as5011_i2c_write(client, AS5011_YN, plat_dat->yn);
+ if (error < 0) {
+ dev_err(&client->dev, "Can't write threshold\n");
+ return error;
+ }
+
+ /* to free irq gpio in chip */
+ error = as5011_i2c_read(client, AS5011_X_RES_INT, &value);
+ if (error < 0) {
+ dev_err(&client->dev, "Can't read i2c X resolution value\n");
+ return error;
+ }
+
+ return 0;
+}
+
+static int __devinit as5011_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ const struct as5011_platform_data *plat_data;
+ struct as5011_device *as5011;
+ struct input_dev *input_dev;
+ int irq;
+ int error;
+
+ plat_data = client->dev.platform_data;
+ if (!plat_data)
+ return -EINVAL;
+
+ if (!plat_data->axis_irq) {
+ dev_err(&client->dev, "No axis IRQ?\n");
+ return -EINVAL;
+ }
+
+ if (!i2c_check_functionality(client->adapter,
+ I2C_FUNC_PROTOCOL_MANGLING)) {
+ dev_err(&client->dev,
+ "need i2c bus that supports protocol mangling\n");
+ return -ENODEV;
+ }
+
+ as5011 = kmalloc(sizeof(struct as5011_device), GFP_KERNEL);
+ input_dev = input_allocate_device();
+ if (!as5011 || !input_dev) {
+ dev_err(&client->dev,
+ "Can't allocate memory for device structure\n");
+ error = -ENOMEM;
+ goto err_free_mem;
+ }
+
+ as5011->i2c_client = client;
+ as5011->input_dev = input_dev;
+ as5011->button_gpio = plat_data->button_gpio;
+ as5011->axis_irq = plat_data->axis_irq;
+
+ input_dev->name = "Austria Microsystem as5011 joystick";
+ input_dev->id.bustype = BUS_I2C;
+ input_dev->dev.parent = &client->dev;
+
+ __set_bit(EV_KEY, input_dev->evbit);
+ __set_bit(EV_ABS, input_dev->evbit);
+ __set_bit(BTN_JOYSTICK, input_dev->keybit);
+
+ input_set_abs_params(input_dev, ABS_X,
+ AS5011_MIN_AXIS, AS5011_MAX_AXIS, AS5011_FUZZ, AS5011_FLAT);
+ input_set_abs_params(as5011->input_dev, ABS_Y,
+ AS5011_MIN_AXIS, AS5011_MAX_AXIS, AS5011_FUZZ, AS5011_FLAT);
+
+ error = gpio_request(as5011->button_gpio, "AS5011 button");
+ if (error < 0) {
+ dev_err(&client->dev, "Failed to request button gpio\n");
+ goto err_free_mem;
+ }
+
+ irq = gpio_to_irq(as5011->button_gpio);
+ if (irq < 0) {
+ dev_err(&client->dev,
+ "Failed to get irq number for button gpio\n");
+ goto err_free_button_gpio;
+ }
+
+ as5011->button_irq = irq;
+
+ error = request_threaded_irq(as5011->button_irq,
+ NULL, as5011_button_interrupt,
+ IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
+ "as5011_button", as5011);
+ if (error < 0) {
+ dev_err(&client->dev,
+ "Can't allocate button irq %d\n", as5011->button_irq);
+ goto err_free_button_gpio;
+ }
+
+ error = as5011_configure_chip(as5011, plat_data);
+ if (error)
+ goto err_free_button_irq;
+
+ error = request_threaded_irq(as5011->axis_irq, NULL,
+ as5011_axis_interrupt,
+ plat_data->axis_irqflags,
+ "as5011_joystick", as5011);
+ if (error) {
+ dev_err(&client->dev,
+ "Can't allocate axis irq %d\n", plat_data->axis_irq);
+ goto err_free_button_irq;
+ }
+
+ error = input_register_device(as5011->input_dev);
+ if (error) {
+ dev_err(&client->dev, "Failed to register input device\n");
+ goto err_free_axis_irq;
+ }
+
+ i2c_set_clientdata(client, as5011);
+
+ return 0;
+
+err_free_axis_irq:
+ free_irq(as5011->axis_irq, as5011);
+err_free_button_irq:
+ free_irq(as5011->button_irq, as5011);
+err_free_button_gpio:
+ gpio_free(as5011->button_gpio);
+err_free_mem:
+ input_free_device(input_dev);
+ kfree(as5011);
+
+ return error;
+}
+
+static int __devexit as5011_remove(struct i2c_client *client)
+{
+ struct as5011_device *as5011 = i2c_get_clientdata(client);
+
+ free_irq(as5011->axis_irq, as5011);
+ free_irq(as5011->button_irq, as5011);
+ gpio_free(as5011->button_gpio);
+
+ input_unregister_device(as5011->input_dev);
+ kfree(as5011);
+
+ return 0;
+}
+
+static const struct i2c_device_id as5011_id[] = {
+ { MODULE_DEVICE_ALIAS, 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, as5011_id);
+
+static struct i2c_driver as5011_driver = {
+ .driver = {
+ .name = "as5011",
+ },
+ .probe = as5011_probe,
+ .remove = __devexit_p(as5011_remove),
+ .id_table = as5011_id,
+};
+
+static int __init as5011_init(void)
+{
+ return i2c_add_driver(&as5011_driver);
+}
+module_init(as5011_init);
+
+static void __exit as5011_exit(void)
+{
+ i2c_del_driver(&as5011_driver);
+}
+module_exit(as5011_exit);
diff --git a/drivers/input/keyboard/Kconfig b/drivers/input/keyboard/Kconfig
index f829998..7b3c0b8 100644
--- a/drivers/input/keyboard/Kconfig
+++ b/drivers/input/keyboard/Kconfig
@@ -12,18 +12,6 @@
if INPUT_KEYBOARD
-config KEYBOARD_AAED2000
- tristate "AAED-2000 keyboard"
- depends on MACH_AAED2000
- select INPUT_POLLDEV
- default y
- help
- Say Y here to enable the keyboard on the Agilent AAED-2000
- development board.
-
- To compile this driver as a module, choose M here: the
- module will be called aaed2000_kbd.
-
config KEYBOARD_ADP5520
tristate "Keypad Support for ADP5520 PMIC"
depends on PMIC_ADP5520
diff --git a/drivers/input/keyboard/Makefile b/drivers/input/keyboard/Makefile
index 8933e9c..4e5571b 100644
--- a/drivers/input/keyboard/Makefile
+++ b/drivers/input/keyboard/Makefile
@@ -4,7 +4,6 @@
# Each configuration option enables a list of files.
-obj-$(CONFIG_KEYBOARD_AAED2000) += aaed2000_kbd.o
obj-$(CONFIG_KEYBOARD_ADP5520) += adp5520-keys.o
obj-$(CONFIG_KEYBOARD_ADP5588) += adp5588-keys.o
obj-$(CONFIG_KEYBOARD_AMIGA) += amikbd.o
diff --git a/drivers/input/keyboard/aaed2000_kbd.c b/drivers/input/keyboard/aaed2000_kbd.c
deleted file mode 100644
index 18222a6..0000000
--- a/drivers/input/keyboard/aaed2000_kbd.c
+++ /dev/null
@@ -1,186 +0,0 @@
-/*
- * Keyboard driver for the AAED-2000 dev board
- *
- * Copyright (c) 2006 Nicolas Bellido Y Ortega
- *
- * Based on corgikbd.c
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- */
-
-#include <linux/delay.h>
-#include <linux/platform_device.h>
-#include <linux/init.h>
-#include <linux/input-polldev.h>
-#include <linux/interrupt.h>
-#include <linux/jiffies.h>
-#include <linux/module.h>
-#include <linux/slab.h>
-
-#include <mach/hardware.h>
-#include <mach/aaed2000.h>
-
-#define KB_ROWS 12
-#define KB_COLS 8
-#define KB_ROWMASK(r) (1 << (r))
-#define SCANCODE(r,c) (((c) * KB_ROWS) + (r))
-#define NR_SCANCODES (KB_COLS * KB_ROWS)
-
-#define SCAN_INTERVAL (50) /* ms */
-#define KB_ACTIVATE_DELAY (20) /* us */
-
-static unsigned char aaedkbd_keycode[NR_SCANCODES] = {
- KEY_9, KEY_0, KEY_MINUS, KEY_EQUAL, KEY_BACKSPACE, 0, KEY_SPACE, KEY_KP6, 0, KEY_KPDOT, 0, 0,
- KEY_K, KEY_M, KEY_O, KEY_DOT, KEY_SLASH, 0, KEY_F, 0, 0, 0, KEY_LEFTSHIFT, 0,
- KEY_I, KEY_P, KEY_LEFTBRACE, KEY_RIGHTBRACE, KEY_BACKSLASH, 0, 0, 0, 0, 0, KEY_RIGHTSHIFT, 0,
- KEY_8, KEY_L, KEY_SEMICOLON, KEY_APOSTROPHE, KEY_ENTER, 0, 0, 0, 0, 0, 0, 0,
- KEY_J, KEY_H, KEY_B, KEY_KP8, KEY_KP4, 0, KEY_C, KEY_D, KEY_S, KEY_A, 0, KEY_CAPSLOCK,
- KEY_Y, KEY_U, KEY_N, KEY_T, 0, 0, KEY_R, KEY_E, KEY_W, KEY_Q, 0, KEY_TAB,
- KEY_7, KEY_6, KEY_G, 0, KEY_5, 0, KEY_4, KEY_3, KEY_2, KEY_1, 0, KEY_GRAVE,
- 0, 0, KEY_COMMA, 0, KEY_KP2, 0, KEY_V, KEY_LEFTALT, KEY_X, KEY_Z, 0, KEY_LEFTCTRL
-};
-
-struct aaedkbd {
- unsigned char keycode[ARRAY_SIZE(aaedkbd_keycode)];
- struct input_polled_dev *poll_dev;
- int kbdscan_state[KB_COLS];
- int kbdscan_count[KB_COLS];
-};
-
-#define KBDSCAN_STABLE_COUNT 2
-
-static void aaedkbd_report_col(struct aaedkbd *aaedkbd,
- unsigned int col, unsigned int rowd)
-{
- unsigned int scancode, pressed;
- unsigned int row;
-
- for (row = 0; row < KB_ROWS; row++) {
- scancode = SCANCODE(row, col);
- pressed = rowd & KB_ROWMASK(row);
-
- input_report_key(aaedkbd->poll_dev->input,
- aaedkbd->keycode[scancode], pressed);
- }
-}
-
-/* Scan the hardware keyboard and push any changes up through the input layer */
-static void aaedkbd_poll(struct input_polled_dev *dev)
-{
- struct aaedkbd *aaedkbd = dev->private;
- unsigned int col, rowd;
-
- col = 0;
- do {
- AAEC_GPIO_KSCAN = col + 8;
- udelay(KB_ACTIVATE_DELAY);
- rowd = AAED_EXT_GPIO & AAED_EGPIO_KBD_SCAN;
-
- if (rowd != aaedkbd->kbdscan_state[col]) {
- aaedkbd->kbdscan_count[col] = 0;
- aaedkbd->kbdscan_state[col] = rowd;
- } else if (++aaedkbd->kbdscan_count[col] >= KBDSCAN_STABLE_COUNT) {
- aaedkbd_report_col(aaedkbd, col, rowd);
- col++;
- }
- } while (col < KB_COLS);
-
- AAEC_GPIO_KSCAN = 0x07;
- input_sync(dev->input);
-}
-
-static int __devinit aaedkbd_probe(struct platform_device *pdev)
-{
- struct aaedkbd *aaedkbd;
- struct input_polled_dev *poll_dev;
- struct input_dev *input_dev;
- int i;
- int error;
-
- aaedkbd = kzalloc(sizeof(struct aaedkbd), GFP_KERNEL);
- poll_dev = input_allocate_polled_device();
- if (!aaedkbd || !poll_dev) {
- error = -ENOMEM;
- goto fail;
- }
-
- platform_set_drvdata(pdev, aaedkbd);
-
- aaedkbd->poll_dev = poll_dev;
- memcpy(aaedkbd->keycode, aaedkbd_keycode, sizeof(aaedkbd->keycode));
-
- poll_dev->private = aaedkbd;
- poll_dev->poll = aaedkbd_poll;
- poll_dev->poll_interval = SCAN_INTERVAL;
-
- input_dev = poll_dev->input;
- input_dev->name = "AAED-2000 Keyboard";
- input_dev->phys = "aaedkbd/input0";
- input_dev->id.bustype = BUS_HOST;
- input_dev->id.vendor = 0x0001;
- input_dev->id.product = 0x0001;
- input_dev->id.version = 0x0100;
- input_dev->dev.parent = &pdev->dev;
-
- input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REP);
- input_dev->keycode = aaedkbd->keycode;
- input_dev->keycodesize = sizeof(unsigned char);
- input_dev->keycodemax = ARRAY_SIZE(aaedkbd_keycode);
-
- for (i = 0; i < ARRAY_SIZE(aaedkbd_keycode); i++)
- set_bit(aaedkbd->keycode[i], input_dev->keybit);
- clear_bit(0, input_dev->keybit);
-
- error = input_register_polled_device(aaedkbd->poll_dev);
- if (error)
- goto fail;
-
- return 0;
-
- fail: kfree(aaedkbd);
- input_free_polled_device(poll_dev);
- return error;
-}
-
-static int __devexit aaedkbd_remove(struct platform_device *pdev)
-{
- struct aaedkbd *aaedkbd = platform_get_drvdata(pdev);
-
- input_unregister_polled_device(aaedkbd->poll_dev);
- input_free_polled_device(aaedkbd->poll_dev);
- kfree(aaedkbd);
-
- return 0;
-}
-
-/* work with hotplug and coldplug */
-MODULE_ALIAS("platform:aaed2000-keyboard");
-
-static struct platform_driver aaedkbd_driver = {
- .probe = aaedkbd_probe,
- .remove = __devexit_p(aaedkbd_remove),
- .driver = {
- .name = "aaed2000-keyboard",
- .owner = THIS_MODULE,
- },
-};
-
-static int __init aaedkbd_init(void)
-{
- return platform_driver_register(&aaedkbd_driver);
-}
-
-static void __exit aaedkbd_exit(void)
-{
- platform_driver_unregister(&aaedkbd_driver);
-}
-
-module_init(aaedkbd_init);
-module_exit(aaedkbd_exit);
-
-MODULE_AUTHOR("Nicolas Bellido Y Ortega");
-MODULE_DESCRIPTION("AAED-2000 Keyboard Driver");
-MODULE_LICENSE("GPL v2");
diff --git a/drivers/input/serio/Kconfig b/drivers/input/serio/Kconfig
index bcb1fde..307eef7 100644
--- a/drivers/input/serio/Kconfig
+++ b/drivers/input/serio/Kconfig
@@ -229,7 +229,7 @@
tristate "TQC PS/2 multiplexer"
help
Say Y here if you have the PS/2 line multiplexer like the one
- present on TQC boads.
+ present on TQC boards.
To compile this driver as a module, choose M here: the
module will be called ps2mult.
diff --git a/drivers/input/serio/i8042-x86ia64io.h b/drivers/input/serio/i8042-x86ia64io.h
index 5ae0fc4..bb9f5d3 100644
--- a/drivers/input/serio/i8042-x86ia64io.h
+++ b/drivers/input/serio/i8042-x86ia64io.h
@@ -424,6 +424,13 @@
DMI_MATCH(DMI_PRODUCT_VERSION, "0100"),
},
},
+ {
+ /* Dell Vostro V13 */
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
+ DMI_MATCH(DMI_PRODUCT_NAME, "Vostro V13"),
+ },
+ },
{ }
};
@@ -545,6 +552,17 @@
};
#endif
+static const struct dmi_system_id __initconst i8042_dmi_notimeout_table[] = {
+ {
+ /* Dell Vostro V13 */
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
+ DMI_MATCH(DMI_PRODUCT_NAME, "Vostro V13"),
+ },
+ },
+ { }
+};
+
/*
* Some Wistron based laptops need us to explicitly enable the 'Dritek
* keyboard extension' to make their extra keys start generating scancodes.
@@ -896,6 +914,9 @@
if (dmi_check_system(i8042_dmi_nomux_table))
i8042_nomux = true;
+ if (dmi_check_system(i8042_dmi_notimeout_table))
+ i8042_notimeout = true;
+
if (dmi_check_system(i8042_dmi_dritek_table))
i8042_dritek = true;
#endif /* CONFIG_X86 */
diff --git a/drivers/input/serio/i8042.c b/drivers/input/serio/i8042.c
index c04ff00..ac4c936 100644
--- a/drivers/input/serio/i8042.c
+++ b/drivers/input/serio/i8042.c
@@ -63,6 +63,10 @@
module_param_named(noloop, i8042_noloop, bool, 0);
MODULE_PARM_DESC(noloop, "Disable the AUX Loopback command while probing for the AUX port");
+static bool i8042_notimeout;
+module_param_named(notimeout, i8042_notimeout, bool, 0);
+MODULE_PARM_DESC(notimeout, "Ignore timeouts signalled by i8042");
+
#ifdef CONFIG_X86
static bool i8042_dritek;
module_param_named(dritek, i8042_dritek, bool, 0);
@@ -504,7 +508,7 @@
} else {
dfl = ((str & I8042_STR_PARITY) ? SERIO_PARITY : 0) |
- ((str & I8042_STR_TIMEOUT) ? SERIO_TIMEOUT : 0);
+ ((str & I8042_STR_TIMEOUT && !i8042_notimeout) ? SERIO_TIMEOUT : 0);
port_no = (str & I8042_STR_AUXDATA) ?
I8042_AUX_PORT_NO : I8042_KBD_PORT_NO;
diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig
index 07ac77d..0c9f4b1 100644
--- a/drivers/input/touchscreen/Kconfig
+++ b/drivers/input/touchscreen/Kconfig
@@ -610,7 +610,7 @@
config TOUCHSCREEN_USB_ETT_TC45USB
default y
- bool "ET&T USB series TC4UM/TC5UH touchscreen controler support" if EMBEDDED
+ bool "ET&T USB series TC4UM/TC5UH touchscreen controller support" if EMBEDDED
depends on TOUCHSCREEN_USB_COMPOSITE
config TOUCHSCREEN_USB_NEXIO
diff --git a/drivers/input/touchscreen/ad7879-i2c.c b/drivers/input/touchscreen/ad7879-i2c.c
index d82a38e..4e4e58c 100644
--- a/drivers/input/touchscreen/ad7879-i2c.c
+++ b/drivers/input/touchscreen/ad7879-i2c.c
@@ -10,14 +10,16 @@
#include <linux/i2c.h>
#include <linux/module.h>
#include <linux/types.h>
+#include <linux/pm.h>
#include "ad7879.h"
#define AD7879_DEVID 0x79 /* AD7879-1/AD7889-1 */
#ifdef CONFIG_PM
-static int ad7879_i2c_suspend(struct i2c_client *client, pm_message_t message)
+static int ad7879_i2c_suspend(struct device *dev)
{
+ struct i2c_client *client = to_i2c_client(dev);
struct ad7879 *ts = i2c_get_clientdata(client);
ad7879_suspend(ts);
@@ -25,17 +27,17 @@
return 0;
}
-static int ad7879_i2c_resume(struct i2c_client *client)
+static int ad7879_i2c_resume(struct device *dev)
{
+ struct i2c_client *client = to_i2c_client(dev);
struct ad7879 *ts = i2c_get_clientdata(client);
ad7879_resume(ts);
return 0;
}
-#else
-# define ad7879_i2c_suspend NULL
-# define ad7879_i2c_resume NULL
+
+static SIMPLE_DEV_PM_OPS(ad7879_i2c_pm, ad7879_i2c_suspend, ad7879_i2c_resume);
#endif
/* All registers are word-sized.
@@ -117,11 +119,12 @@
.driver = {
.name = "ad7879",
.owner = THIS_MODULE,
+#ifdef CONFIG_PM
+ .pm = &ad7879_i2c_pm,
+#endif
},
.probe = ad7879_i2c_probe,
.remove = __devexit_p(ad7879_i2c_remove),
- .suspend = ad7879_i2c_suspend,
- .resume = ad7879_i2c_resume,
.id_table = ad7879_id,
};
diff --git a/drivers/input/touchscreen/cy8ctmg110_ts.c b/drivers/input/touchscreen/cy8ctmg110_ts.c
index d0c3a72..a93c5c2 100644
--- a/drivers/input/touchscreen/cy8ctmg110_ts.c
+++ b/drivers/input/touchscreen/cy8ctmg110_ts.c
@@ -280,8 +280,9 @@
}
#ifdef CONFIG_PM
-static int cy8ctmg110_suspend(struct i2c_client *client, pm_message_t mesg)
+static int cy8ctmg110_suspend(struct device *dev)
{
+ struct i2c_client *client = to_i2c_client(dev);
struct cy8ctmg110 *ts = i2c_get_clientdata(client);
if (device_may_wakeup(&client->dev))
@@ -293,8 +294,9 @@
return 0;
}
-static int cy8ctmg110_resume(struct i2c_client *client)
+static int cy8ctmg110_resume(struct device *dev)
{
+ struct i2c_client *client = to_i2c_client(dev);
struct cy8ctmg110 *ts = i2c_get_clientdata(client);
if (device_may_wakeup(&client->dev))
@@ -305,6 +307,8 @@
}
return 0;
}
+
+static SIMPLE_DEV_PM_OPS(cy8ctmg110_pm, cy8ctmg110_suspend, cy8ctmg110_resume);
#endif
static int __devexit cy8ctmg110_remove(struct i2c_client *client)
@@ -335,14 +339,13 @@
.driver = {
.owner = THIS_MODULE,
.name = CY8CTMG110_DRIVER_NAME,
+#ifdef CONFIG_PM
+ .pm = &cy8ctmg110_pm,
+#endif
},
.id_table = cy8ctmg110_idtable,
.probe = cy8ctmg110_probe,
.remove = __devexit_p(cy8ctmg110_remove),
-#ifdef CONFIG_PM
- .suspend = cy8ctmg110_suspend,
- .resume = cy8ctmg110_resume,
-#endif
};
static int __init cy8ctmg110_init(void)
diff --git a/drivers/input/touchscreen/eeti_ts.c b/drivers/input/touchscreen/eeti_ts.c
index 7a3a916..7f8f538 100644
--- a/drivers/input/touchscreen/eeti_ts.c
+++ b/drivers/input/touchscreen/eeti_ts.c
@@ -261,8 +261,9 @@
}
#ifdef CONFIG_PM
-static int eeti_ts_suspend(struct i2c_client *client, pm_message_t mesg)
+static int eeti_ts_suspend(struct device *dev)
{
+ struct i2c_client *client = to_i2c_client(dev);
struct eeti_ts_priv *priv = i2c_get_clientdata(client);
struct input_dev *input_dev = priv->input;
@@ -279,8 +280,9 @@
return 0;
}
-static int eeti_ts_resume(struct i2c_client *client)
+static int eeti_ts_resume(struct device *dev)
{
+ struct i2c_client *client = to_i2c_client(dev);
struct eeti_ts_priv *priv = i2c_get_clientdata(client);
struct input_dev *input_dev = priv->input;
@@ -296,9 +298,8 @@
return 0;
}
-#else
-#define eeti_ts_suspend NULL
-#define eeti_ts_resume NULL
+
+static SIMPLE_DEV_PM_OPS(eeti_ts_pm, eeti_ts_suspend, eeti_ts_resume);
#endif
static const struct i2c_device_id eeti_ts_id[] = {
@@ -310,11 +311,12 @@
static struct i2c_driver eeti_ts_driver = {
.driver = {
.name = "eeti_ts",
+#ifdef CONFIG_PM
+ .pm = &eeti_ts_pm,
+#endif
},
.probe = eeti_ts_probe,
.remove = __devexit_p(eeti_ts_remove),
- .suspend = eeti_ts_suspend,
- .resume = eeti_ts_resume,
.id_table = eeti_ts_id,
};
diff --git a/drivers/input/touchscreen/mcs5000_ts.c b/drivers/input/touchscreen/mcs5000_ts.c
index 6ee9940..2d84c80 100644
--- a/drivers/input/touchscreen/mcs5000_ts.c
+++ b/drivers/input/touchscreen/mcs5000_ts.c
@@ -261,25 +261,27 @@
}
#ifdef CONFIG_PM
-static int mcs5000_ts_suspend(struct i2c_client *client, pm_message_t mesg)
+static int mcs5000_ts_suspend(struct device *dev)
{
+ struct i2c_client *client = to_i2c_client(dev);
+
/* Touch sleep mode */
i2c_smbus_write_byte_data(client, MCS5000_TS_OP_MODE, OP_MODE_SLEEP);
return 0;
}
-static int mcs5000_ts_resume(struct i2c_client *client)
+static int mcs5000_ts_resume(struct device *dev)
{
+ struct i2c_client *client = to_i2c_client(dev);
struct mcs5000_ts_data *data = i2c_get_clientdata(client);
mcs5000_ts_phys_init(data);
return 0;
}
-#else
-#define mcs5000_ts_suspend NULL
-#define mcs5000_ts_resume NULL
+
+static SIMPLE_DEV_PM_OPS(mcs5000_ts_pm, mcs5000_ts_suspend, mcs5000_ts_resume);
#endif
static const struct i2c_device_id mcs5000_ts_id[] = {
@@ -291,10 +293,11 @@
static struct i2c_driver mcs5000_ts_driver = {
.probe = mcs5000_ts_probe,
.remove = __devexit_p(mcs5000_ts_remove),
- .suspend = mcs5000_ts_suspend,
- .resume = mcs5000_ts_resume,
.driver = {
.name = "mcs5000_ts",
+#ifdef CONFIG_PM
+ .pm = &mcs5000_ts_pm,
+#endif
},
.id_table = mcs5000_ts_id,
};
diff --git a/drivers/input/touchscreen/migor_ts.c b/drivers/input/touchscreen/migor_ts.c
index defe5dd..5803bd0 100644
--- a/drivers/input/touchscreen/migor_ts.c
+++ b/drivers/input/touchscreen/migor_ts.c
@@ -23,6 +23,7 @@
#include <linux/kernel.h>
#include <linux/input.h>
#include <linux/interrupt.h>
+#include <linux/pm.h>
#include <linux/slab.h>
#include <asm/io.h>
#include <linux/i2c.h>
@@ -226,8 +227,9 @@
return 0;
}
-static int migor_ts_suspend(struct i2c_client *client, pm_message_t mesg)
+static int migor_ts_suspend(struct device *dev)
{
+ struct i2c_client *client = to_i2c_client(dev);
struct migor_ts_priv *priv = dev_get_drvdata(&client->dev);
if (device_may_wakeup(&client->dev))
@@ -236,8 +238,9 @@
return 0;
}
-static int migor_ts_resume(struct i2c_client *client)
+static int migor_ts_resume(struct device *dev)
{
+ struct i2c_client *client = to_i2c_client(dev);
struct migor_ts_priv *priv = dev_get_drvdata(&client->dev);
if (device_may_wakeup(&client->dev))
@@ -246,6 +249,8 @@
return 0;
}
+static SIMPLE_DEV_PM_OPS(migor_ts_pm, migor_ts_suspend, migor_ts_resume);
+
static const struct i2c_device_id migor_ts_id[] = {
{ "migor_ts", 0 },
{ }
@@ -255,11 +260,10 @@
static struct i2c_driver migor_ts_driver = {
.driver = {
.name = "migor_ts",
+ .pm = &migor_ts_pm,
},
.probe = migor_ts_probe,
.remove = migor_ts_remove,
- .suspend = migor_ts_suspend,
- .resume = migor_ts_resume,
.id_table = migor_ts_id,
};
diff --git a/drivers/input/touchscreen/wacom_w8001.c b/drivers/input/touchscreen/wacom_w8001.c
index 8ed53ad..5cb8449 100644
--- a/drivers/input/touchscreen/wacom_w8001.c
+++ b/drivers/input/touchscreen/wacom_w8001.c
@@ -3,6 +3,7 @@
*
* Copyright (c) 2008 Jaya Kumar
* Copyright (c) 2010 Red Hat, Inc.
+ * Copyright (c) 2010 - 2011 Ping Cheng, Wacom. <pingc@wacom.com>
*
* This file is subject to the terms and conditions of the GNU General Public
* License. See the file COPYING in the main directory of this archive for
@@ -64,11 +65,11 @@
/* touch query reply packet */
struct w8001_touch_query {
+ u16 x;
+ u16 y;
u8 panel_res;
u8 capacity_res;
u8 sensor_id;
- u16 x;
- u16 y;
};
/*
@@ -87,9 +88,14 @@
char phys[32];
int type;
unsigned int pktlen;
+ u16 max_touch_x;
+ u16 max_touch_y;
+ u16 max_pen_x;
+ u16 max_pen_y;
+ char name[64];
};
-static void parse_data(u8 *data, struct w8001_coord *coord)
+static void parse_pen_data(u8 *data, struct w8001_coord *coord)
{
memset(coord, 0, sizeof(*coord));
@@ -113,11 +119,30 @@
coord->tilt_y = data[8] & 0x7F;
}
-static void parse_touch(struct w8001 *w8001)
+static void parse_single_touch(u8 *data, struct w8001_coord *coord)
+{
+ coord->x = (data[1] << 7) | data[2];
+ coord->y = (data[3] << 7) | data[4];
+ coord->tsw = data[0] & 0x01;
+}
+
+static void scale_touch_coordinates(struct w8001 *w8001,
+ unsigned int *x, unsigned int *y)
+{
+ if (w8001->max_pen_x && w8001->max_touch_x)
+ *x = *x * w8001->max_pen_x / w8001->max_touch_x;
+
+ if (w8001->max_pen_y && w8001->max_touch_y)
+ *y = *y * w8001->max_pen_y / w8001->max_touch_y;
+}
+
+static void parse_multi_touch(struct w8001 *w8001)
{
struct input_dev *dev = w8001->dev;
unsigned char *data = w8001->data;
+ unsigned int x, y;
int i;
+ int count = 0;
for (i = 0; i < 2; i++) {
bool touch = data[0] & (1 << i);
@@ -125,15 +150,29 @@
input_mt_slot(dev, i);
input_mt_report_slot_state(dev, MT_TOOL_FINGER, touch);
if (touch) {
- int x = (data[6 * i + 1] << 7) | (data[6 * i + 2]);
- int y = (data[6 * i + 3] << 7) | (data[6 * i + 4]);
+ x = (data[6 * i + 1] << 7) | data[6 * i + 2];
+ y = (data[6 * i + 3] << 7) | data[6 * i + 4];
/* data[5,6] and [11,12] is finger capacity */
+ /* scale to pen maximum */
+ scale_touch_coordinates(w8001, &x, &y);
+
input_report_abs(dev, ABS_MT_POSITION_X, x);
input_report_abs(dev, ABS_MT_POSITION_Y, y);
+ count++;
}
}
+ /* emulate single touch events when stylus is out of proximity.
+ * This is to make single touch backward support consistent
+ * across all Wacom single touch devices.
+ */
+ if (w8001->type != BTN_TOOL_PEN &&
+ w8001->type != BTN_TOOL_RUBBER) {
+ w8001->type = count == 1 ? BTN_TOOL_FINGER : KEY_RESERVED;
+ input_mt_report_pointer_emulation(dev, true);
+ }
+
input_sync(dev);
}
@@ -152,6 +191,15 @@
query->y = data[5] << 9;
query->y |= data[6] << 2;
query->y |= (data[2] >> 3) & 0x3;
+
+ /* Early days' single-finger touch models need the following defaults */
+ if (!query->x && !query->y) {
+ query->x = 1024;
+ query->y = 1024;
+ if (query->panel_res)
+ query->x = query->y = (1 << query->panel_res);
+ query->panel_res = 10;
+ }
}
static void report_pen_events(struct w8001 *w8001, struct w8001_coord *coord)
@@ -161,16 +209,15 @@
/*
* We have 1 bit for proximity (rdy) and 3 bits for tip, side,
* side2/eraser. If rdy && f2 are set, this can be either pen + side2,
- * or eraser. assume
+ * or eraser. Assume:
* - if dev is already in proximity and f2 is toggled → pen + side2
* - if dev comes into proximity with f2 set → eraser
* If f2 disappears after assuming eraser, fake proximity out for
* eraser and in for pen.
*/
- if (!w8001->type) {
- w8001->type = coord->f2 ? BTN_TOOL_RUBBER : BTN_TOOL_PEN;
- } else if (w8001->type == BTN_TOOL_RUBBER) {
+ switch (w8001->type) {
+ case BTN_TOOL_RUBBER:
if (!coord->f2) {
input_report_abs(dev, ABS_PRESSURE, 0);
input_report_key(dev, BTN_TOUCH, 0);
@@ -180,8 +227,21 @@
input_sync(dev);
w8001->type = BTN_TOOL_PEN;
}
- } else {
+ break;
+
+ case BTN_TOOL_FINGER:
+ input_report_key(dev, BTN_TOUCH, 0);
+ input_report_key(dev, BTN_TOOL_FINGER, 0);
+ input_sync(dev);
+ /* fall through */
+
+ case KEY_RESERVED:
+ w8001->type = coord->f2 ? BTN_TOOL_RUBBER : BTN_TOOL_PEN;
+ break;
+
+ default:
input_report_key(dev, BTN_STYLUS2, coord->f2);
+ break;
}
input_report_abs(dev, ABS_X, coord->x);
@@ -193,7 +253,26 @@
input_sync(dev);
if (!coord->rdy)
- w8001->type = 0;
+ w8001->type = KEY_RESERVED;
+}
+
+static void report_single_touch(struct w8001 *w8001, struct w8001_coord *coord)
+{
+ struct input_dev *dev = w8001->dev;
+ unsigned int x = coord->x;
+ unsigned int y = coord->y;
+
+ /* scale to pen maximum */
+ scale_touch_coordinates(w8001, &x, &y);
+
+ input_report_abs(dev, ABS_X, x);
+ input_report_abs(dev, ABS_Y, y);
+ input_report_key(dev, BTN_TOUCH, coord->tsw);
+ input_report_key(dev, BTN_TOOL_FINGER, coord->tsw);
+
+ input_sync(dev);
+
+ w8001->type = coord->tsw ? BTN_TOOL_FINGER : KEY_RESERVED;
}
static irqreturn_t w8001_interrupt(struct serio *serio,
@@ -214,9 +293,18 @@
case W8001_PKTLEN_TOUCH93 - 1:
case W8001_PKTLEN_TOUCH9A - 1:
- /* ignore one-finger touch packet. */
- if (w8001->pktlen == w8001->idx)
+ tmp = w8001->data[0] & W8001_TOUCH_BYTE;
+ if (tmp != W8001_TOUCH_BYTE)
+ break;
+
+ if (w8001->pktlen == w8001->idx) {
w8001->idx = 0;
+ if (w8001->type != BTN_TOOL_PEN &&
+ w8001->type != BTN_TOOL_RUBBER) {
+ parse_single_touch(w8001->data, &coord);
+ report_single_touch(w8001, &coord);
+ }
+ }
break;
/* Pen coordinates packet */
@@ -225,18 +313,18 @@
if (unlikely(tmp == W8001_TAB_BYTE))
break;
- tmp = (w8001->data[0] & W8001_TOUCH_BYTE);
+ tmp = w8001->data[0] & W8001_TOUCH_BYTE;
if (tmp == W8001_TOUCH_BYTE)
break;
w8001->idx = 0;
- parse_data(w8001->data, &coord);
+ parse_pen_data(w8001->data, &coord);
report_pen_events(w8001, &coord);
break;
/* control packet */
case W8001_PKTLEN_TPCCTL - 1:
- tmp = (w8001->data[0] & W8001_TOUCH_MASK);
+ tmp = w8001->data[0] & W8001_TOUCH_MASK;
if (tmp == W8001_TOUCH_BYTE)
break;
@@ -249,7 +337,7 @@
/* 2 finger touch packet */
case W8001_PKTLEN_TOUCH2FG - 1:
w8001->idx = 0;
- parse_touch(w8001);
+ parse_multi_touch(w8001);
break;
}
@@ -279,6 +367,7 @@
{
struct input_dev *dev = w8001->dev;
struct w8001_coord coord;
+ struct w8001_touch_query touch;
int error;
error = w8001_command(w8001, W8001_CMD_STOP, false);
@@ -287,14 +376,21 @@
msleep(250); /* wait 250ms before querying the device */
+ dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
+ strlcat(w8001->name, "Wacom Serial", sizeof(w8001->name));
+
/* penabled? */
error = w8001_command(w8001, W8001_CMD_QUERY, true);
if (!error) {
+ __set_bit(BTN_TOUCH, dev->keybit);
__set_bit(BTN_TOOL_PEN, dev->keybit);
__set_bit(BTN_TOOL_RUBBER, dev->keybit);
__set_bit(BTN_STYLUS, dev->keybit);
__set_bit(BTN_STYLUS2, dev->keybit);
- parse_data(w8001->response, &coord);
+
+ parse_pen_data(w8001->response, &coord);
+ w8001->max_pen_x = coord.x;
+ w8001->max_pen_y = coord.y;
input_set_abs_params(dev, ABS_X, 0, coord.x, 0, 0);
input_set_abs_params(dev, ABS_Y, 0, coord.y, 0, 0);
@@ -303,6 +399,8 @@
input_set_abs_params(dev, ABS_TILT_X, 0, coord.tilt_x, 0, 0);
input_set_abs_params(dev, ABS_TILT_Y, 0, coord.tilt_y, 0, 0);
}
+ w8001->id = 0x90;
+ strlcat(w8001->name, " Penabled", sizeof(w8001->name));
}
/* Touch enabled? */
@@ -313,24 +411,38 @@
* second byte is empty, which indicates touch is not supported.
*/
if (!error && w8001->response[1]) {
- struct w8001_touch_query touch;
+ __set_bit(BTN_TOUCH, dev->keybit);
+ __set_bit(BTN_TOOL_FINGER, dev->keybit);
parse_touchquery(w8001->response, &touch);
+ w8001->max_touch_x = touch.x;
+ w8001->max_touch_y = touch.y;
+
+ /* scale to pen maximum */
+ if (w8001->max_pen_x && w8001->max_pen_y) {
+ touch.x = w8001->max_pen_x;
+ touch.y = w8001->max_pen_y;
+ }
input_set_abs_params(dev, ABS_X, 0, touch.x, 0, 0);
input_set_abs_params(dev, ABS_Y, 0, touch.y, 0, 0);
- __set_bit(BTN_TOOL_FINGER, dev->keybit);
switch (touch.sensor_id) {
case 0:
case 2:
w8001->pktlen = W8001_PKTLEN_TOUCH93;
+ w8001->id = 0x93;
+ strlcat(w8001->name, " 1FG", sizeof(w8001->name));
break;
+
case 1:
case 3:
case 4:
w8001->pktlen = W8001_PKTLEN_TOUCH9A;
+ strlcat(w8001->name, " 1FG", sizeof(w8001->name));
+ w8001->id = 0x9a;
break;
+
case 5:
w8001->pktlen = W8001_PKTLEN_TOUCH2FG;
@@ -341,10 +453,18 @@
0, touch.y, 0, 0);
input_set_abs_params(dev, ABS_MT_TOOL_TYPE,
0, MT_TOOL_MAX, 0, 0);
+
+ strlcat(w8001->name, " 2FG", sizeof(w8001->name));
+ if (w8001->max_pen_x && w8001->max_pen_y)
+ w8001->id = 0xE3;
+ else
+ w8001->id = 0xE2;
break;
}
}
+ strlcat(w8001->name, " Touchscreen", sizeof(w8001->name));
+
return w8001_command(w8001, W8001_CMD_START, false);
}
@@ -384,22 +504,10 @@
}
w8001->serio = serio;
- w8001->id = serio->id.id;
w8001->dev = input_dev;
init_completion(&w8001->cmd_done);
snprintf(w8001->phys, sizeof(w8001->phys), "%s/input0", serio->phys);
- input_dev->name = "Wacom W8001 Penabled Serial TouchScreen";
- input_dev->phys = w8001->phys;
- input_dev->id.bustype = BUS_RS232;
- input_dev->id.vendor = SERIO_W8001;
- input_dev->id.product = w8001->id;
- input_dev->id.version = 0x0100;
- input_dev->dev.parent = &serio->dev;
-
- input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
- __set_bit(BTN_TOUCH, input_dev->keybit);
-
serio_set_drvdata(serio, w8001);
err = serio_open(serio, drv);
if (err)
@@ -409,6 +517,14 @@
if (err)
goto fail3;
+ input_dev->name = w8001->name;
+ input_dev->phys = w8001->phys;
+ input_dev->id.product = w8001->id;
+ input_dev->id.bustype = BUS_RS232;
+ input_dev->id.vendor = 0x056a;
+ input_dev->id.version = 0x0100;
+ input_dev->dev.parent = &serio->dev;
+
err = input_register_device(w8001->dev);
if (err)
goto fail3;
diff --git a/drivers/isdn/gigaset/bas-gigaset.c b/drivers/isdn/gigaset/bas-gigaset.c
index 178942a..8a3c5cf 100644
--- a/drivers/isdn/gigaset/bas-gigaset.c
+++ b/drivers/isdn/gigaset/bas-gigaset.c
@@ -2318,7 +2318,7 @@
__func__, le16_to_cpu(udev->descriptor.idVendor),
le16_to_cpu(udev->descriptor.idProduct));
- /* allocate memory for our device state and intialize it */
+ /* allocate memory for our device state and initialize it */
cs = gigaset_initcs(driver, BAS_CHANNELS, 0, 0, cidmode,
GIGASET_MODULENAME);
if (!cs)
@@ -2576,7 +2576,7 @@
{
int result;
- /* allocate memory for our driver state and intialize it */
+ /* allocate memory for our driver state and initialize it */
driver = gigaset_initdriver(GIGASET_MINOR, GIGASET_MINORS,
GIGASET_MODULENAME, GIGASET_DEVNAME,
&gigops, THIS_MODULE);
diff --git a/drivers/isdn/gigaset/ser-gigaset.c b/drivers/isdn/gigaset/ser-gigaset.c
index d151dcb..0ef09d0 100644
--- a/drivers/isdn/gigaset/ser-gigaset.c
+++ b/drivers/isdn/gigaset/ser-gigaset.c
@@ -513,7 +513,7 @@
return -ENODEV;
}
- /* allocate memory for our device state and intialize it */
+ /* allocate memory for our device state and initialize it */
cs = gigaset_initcs(driver, 1, 1, 0, cidmode, GIGASET_MODULENAME);
if (!cs)
goto error;
@@ -771,7 +771,7 @@
return rc;
}
- /* allocate memory for our driver state and intialize it */
+ /* allocate memory for our driver state and initialize it */
driver = gigaset_initdriver(GIGASET_MINOR, GIGASET_MINORS,
GIGASET_MODULENAME, GIGASET_DEVNAME,
&ops, THIS_MODULE);
diff --git a/drivers/isdn/gigaset/usb-gigaset.c b/drivers/isdn/gigaset/usb-gigaset.c
index 4a66338..5e3300d 100644
--- a/drivers/isdn/gigaset/usb-gigaset.c
+++ b/drivers/isdn/gigaset/usb-gigaset.c
@@ -695,7 +695,7 @@
dev_info(&udev->dev, "%s: Device matched ... !\n", __func__);
- /* allocate memory for our device state and intialize it */
+ /* allocate memory for our device state and initialize it */
cs = gigaset_initcs(driver, 1, 1, 0, cidmode, GIGASET_MODULENAME);
if (!cs)
return -ENODEV;
@@ -894,7 +894,7 @@
{
int result;
- /* allocate memory for our driver state and intialize it */
+ /* allocate memory for our driver state and initialize it */
driver = gigaset_initdriver(GIGASET_MINOR, GIGASET_MINORS,
GIGASET_MODULENAME, GIGASET_DEVNAME,
&ops, THIS_MODULE);
diff --git a/drivers/isdn/hardware/mISDN/ipac.h b/drivers/isdn/hardware/mISDN/ipac.h
index 74a6ccf..8121e04 100644
--- a/drivers/isdn/hardware/mISDN/ipac.h
+++ b/drivers/isdn/hardware/mISDN/ipac.h
@@ -29,7 +29,7 @@
u32 type;
u32 off; /* offset to isac regs */
char *name;
- spinlock_t *hwlock; /* lock HW acccess */
+ spinlock_t *hwlock; /* lock HW access */
read_reg_func *read_reg;
write_reg_func *write_reg;
fifo_func *read_fifo;
@@ -70,7 +70,7 @@
struct hscx_hw hscx[2];
char *name;
void *hw;
- spinlock_t *hwlock; /* lock HW acccess */
+ spinlock_t *hwlock; /* lock HW access */
struct module *owner;
u32 type;
read_reg_func *read_reg;
diff --git a/drivers/isdn/hardware/mISDN/isar.h b/drivers/isdn/hardware/mISDN/isar.h
index 4a134ac..9962bdf 100644
--- a/drivers/isdn/hardware/mISDN/isar.h
+++ b/drivers/isdn/hardware/mISDN/isar.h
@@ -44,7 +44,7 @@
struct isar_hw {
struct isar_ch ch[2];
void *hw;
- spinlock_t *hwlock; /* lock HW acccess */
+ spinlock_t *hwlock; /* lock HW access */
char *name;
struct module *owner;
read_reg_func *read_reg;
diff --git a/drivers/isdn/mISDN/dsp_cmx.c b/drivers/isdn/mISDN/dsp_cmx.c
index 76d9e67..309bacf 100644
--- a/drivers/isdn/mISDN/dsp_cmx.c
+++ b/drivers/isdn/mISDN/dsp_cmx.c
@@ -112,7 +112,7 @@
* Disable rx-data:
* If cmx is realized in hardware, rx data will be disabled if requested by
* the upper layer. If dtmf decoding is done by software and enabled, rx data
- * will not be diabled but blocked to the upper layer.
+ * will not be disabled but blocked to the upper layer.
*
* HFC conference engine:
* If it is possible to realize all features using hardware, hardware will be
diff --git a/drivers/leds/leds-lp5521.c b/drivers/leds/leds-lp5521.c
index 33facd0..80a3ae3 100644
--- a/drivers/leds/leds-lp5521.c
+++ b/drivers/leds/leds-lp5521.c
@@ -98,7 +98,6 @@
#define LP5521_EXT_CLK_USED 0x08
struct lp5521_engine {
- const struct attribute_group *attributes;
int id;
u8 mode;
u8 prog_page;
@@ -225,25 +224,22 @@
curr);
}
-static void lp5521_init_engine(struct lp5521_chip *chip,
- const struct attribute_group *attr_group)
+static void lp5521_init_engine(struct lp5521_chip *chip)
{
int i;
for (i = 0; i < ARRAY_SIZE(chip->engines); i++) {
chip->engines[i].id = i + 1;
chip->engines[i].engine_mask = LP5521_ENG_MASK_BASE >> (i * 2);
chip->engines[i].prog_page = i;
- chip->engines[i].attributes = &attr_group[i];
}
}
-static int lp5521_configure(struct i2c_client *client,
- const struct attribute_group *attr_group)
+static int lp5521_configure(struct i2c_client *client)
{
struct lp5521_chip *chip = i2c_get_clientdata(client);
int ret;
- lp5521_init_engine(chip, attr_group);
+ lp5521_init_engine(chip);
/* Set all PWMs to direct control mode */
ret = lp5521_write(client, LP5521_REG_OP_MODE, 0x3F);
@@ -329,9 +325,6 @@
/* Set engine mode and create appropriate sysfs attributes, if required. */
static int lp5521_set_mode(struct lp5521_engine *engine, u8 mode)
{
- struct lp5521_chip *chip = engine_to_lp5521(engine);
- struct i2c_client *client = chip->client;
- struct device *dev = &client->dev;
int ret = 0;
/* if in that mode already do nothing, except for run */
@@ -343,18 +336,10 @@
} else if (mode == LP5521_CMD_LOAD) {
lp5521_set_engine_mode(engine, LP5521_CMD_DISABLED);
lp5521_set_engine_mode(engine, LP5521_CMD_LOAD);
-
- ret = sysfs_create_group(&dev->kobj, engine->attributes);
- if (ret)
- return ret;
} else if (mode == LP5521_CMD_DISABLED) {
lp5521_set_engine_mode(engine, LP5521_CMD_DISABLED);
}
- /* remove load attribute from sysfs if not in load mode */
- if (engine->mode == LP5521_CMD_LOAD && mode != LP5521_CMD_LOAD)
- sysfs_remove_group(&dev->kobj, engine->attributes);
-
engine->mode = mode;
return ret;
@@ -373,6 +358,8 @@
while ((offset < len - 1) && (i < LP5521_PROGRAM_LENGTH)) {
/* separate sscanfs because length is working only for %s */
ret = sscanf(buf + offset, "%2s%n ", c, &nrchars);
+ if (ret != 2)
+ goto fail;
ret = sscanf(c, "%2x", &cmd);
if (ret != 1)
goto fail;
@@ -387,7 +374,10 @@
goto fail;
mutex_lock(&chip->lock);
- ret = lp5521_load_program(engine, pattern);
+ if (engine->mode == LP5521_CMD_LOAD)
+ ret = lp5521_load_program(engine, pattern);
+ else
+ ret = -EINVAL;
mutex_unlock(&chip->lock);
if (ret) {
@@ -574,20 +564,8 @@
&dev_attr_engine2_mode.attr,
&dev_attr_engine3_mode.attr,
&dev_attr_selftest.attr,
- NULL
-};
-
-static struct attribute *lp5521_engine1_attributes[] = {
&dev_attr_engine1_load.attr,
- NULL
-};
-
-static struct attribute *lp5521_engine2_attributes[] = {
&dev_attr_engine2_load.attr,
- NULL
-};
-
-static struct attribute *lp5521_engine3_attributes[] = {
&dev_attr_engine3_load.attr,
NULL
};
@@ -596,12 +574,6 @@
.attrs = lp5521_attributes,
};
-static const struct attribute_group lp5521_engine_group[] = {
- {.attrs = lp5521_engine1_attributes },
- {.attrs = lp5521_engine2_attributes },
- {.attrs = lp5521_engine3_attributes },
-};
-
static int lp5521_register_sysfs(struct i2c_client *client)
{
struct device *dev = &client->dev;
@@ -616,12 +588,6 @@
sysfs_remove_group(&dev->kobj, &lp5521_group);
- for (i = 0; i < ARRAY_SIZE(chip->engines); i++) {
- if (chip->engines[i].mode == LP5521_CMD_LOAD)
- sysfs_remove_group(&dev->kobj,
- chip->engines[i].attributes);
- }
-
for (i = 0; i < chip->num_leds; i++)
sysfs_remove_group(&chip->leds[i].cdev.dev->kobj,
&lp5521_led_attribute_group);
@@ -651,7 +617,8 @@
return -EINVAL;
}
- snprintf(name, sizeof(name), "%s:channel%d", client->name, chan);
+ snprintf(name, sizeof(name), "%s:channel%d",
+ pdata->label ?: client->name, chan);
led->cdev.brightness_set = lp5521_set_brightness;
led->cdev.name = name;
res = led_classdev_register(dev, &led->cdev);
@@ -723,7 +690,7 @@
dev_info(&client->dev, "%s programmable led chip found\n", id->name);
- ret = lp5521_configure(client, lp5521_engine_group);
+ ret = lp5521_configure(client);
if (ret < 0) {
dev_err(&client->dev, "error configuring chip\n");
goto fail2;
diff --git a/drivers/leds/leds-lp5523.c b/drivers/leds/leds-lp5523.c
index 0cc4ead..d0c4068 100644
--- a/drivers/leds/leds-lp5523.c
+++ b/drivers/leds/leds-lp5523.c
@@ -105,7 +105,6 @@
#define SHIFT_MASK(id) (((id) - 1) * 2)
struct lp5523_engine {
- const struct attribute_group *attributes;
int id;
u8 mode;
u8 prog_page;
@@ -403,14 +402,23 @@
struct i2c_client *client = to_i2c_client(dev);
struct lp5523_chip *chip = i2c_get_clientdata(client);
u16 mux = 0;
+ ssize_t ret;
if (lp5523_mux_parse(buf, &mux, len))
return -EINVAL;
- if (lp5523_load_mux(&chip->engines[nr - 1], mux))
- return -EINVAL;
+ mutex_lock(&chip->lock);
+ ret = -EINVAL;
+ if (chip->engines[nr - 1].mode != LP5523_CMD_LOAD)
+ goto leave;
- return len;
+ if (lp5523_load_mux(&chip->engines[nr - 1], mux))
+ goto leave;
+
+ ret = len;
+leave:
+ mutex_unlock(&chip->lock);
+ return ret;
}
#define store_leds(nr) \
@@ -556,7 +564,11 @@
mutex_lock(&chip->lock);
- ret = lp5523_load_program(engine, pattern);
+ if (engine->mode == LP5523_CMD_LOAD)
+ ret = lp5523_load_program(engine, pattern);
+ else
+ ret = -EINVAL;
+
mutex_unlock(&chip->lock);
if (ret) {
@@ -737,37 +749,18 @@
&dev_attr_engine2_mode.attr,
&dev_attr_engine3_mode.attr,
&dev_attr_selftest.attr,
- NULL
-};
-
-static struct attribute *lp5523_engine1_attributes[] = {
&dev_attr_engine1_load.attr,
&dev_attr_engine1_leds.attr,
- NULL
-};
-
-static struct attribute *lp5523_engine2_attributes[] = {
&dev_attr_engine2_load.attr,
&dev_attr_engine2_leds.attr,
- NULL
-};
-
-static struct attribute *lp5523_engine3_attributes[] = {
&dev_attr_engine3_load.attr,
&dev_attr_engine3_leds.attr,
- NULL
};
static const struct attribute_group lp5523_group = {
.attrs = lp5523_attributes,
};
-static const struct attribute_group lp5523_engine_group[] = {
- {.attrs = lp5523_engine1_attributes },
- {.attrs = lp5523_engine2_attributes },
- {.attrs = lp5523_engine3_attributes },
-};
-
static int lp5523_register_sysfs(struct i2c_client *client)
{
struct device *dev = &client->dev;
@@ -788,10 +781,6 @@
sysfs_remove_group(&dev->kobj, &lp5523_group);
- for (i = 0; i < ARRAY_SIZE(chip->engines); i++)
- if (chip->engines[i].mode == LP5523_CMD_LOAD)
- sysfs_remove_group(&dev->kobj, &lp5523_engine_group[i]);
-
for (i = 0; i < chip->num_leds; i++)
sysfs_remove_group(&chip->leds[i].cdev.dev->kobj,
&lp5523_led_attribute_group);
@@ -802,10 +791,6 @@
/*--------------------------------------------------------------*/
static int lp5523_set_mode(struct lp5523_engine *engine, u8 mode)
{
- /* engine to chip */
- struct lp5523_chip *chip = engine_to_lp5523(engine);
- struct i2c_client *client = chip->client;
- struct device *dev = &client->dev;
int ret = 0;
/* if in that mode already do nothing, except for run */
@@ -817,18 +802,10 @@
} else if (mode == LP5523_CMD_LOAD) {
lp5523_set_engine_mode(engine, LP5523_CMD_DISABLED);
lp5523_set_engine_mode(engine, LP5523_CMD_LOAD);
-
- ret = sysfs_create_group(&dev->kobj, engine->attributes);
- if (ret)
- return ret;
} else if (mode == LP5523_CMD_DISABLED) {
lp5523_set_engine_mode(engine, LP5523_CMD_DISABLED);
}
- /* remove load attribute from sysfs if not in load mode */
- if (engine->mode == LP5523_CMD_LOAD && mode != LP5523_CMD_LOAD)
- sysfs_remove_group(&dev->kobj, engine->attributes);
-
engine->mode = mode;
return ret;
@@ -845,7 +822,6 @@
engine->engine_mask = LP5523_ENG_MASK_BASE >> SHIFT_MASK(id);
engine->prog_page = id - 1;
engine->mux_page = id + 2;
- engine->attributes = &lp5523_engine_group[id - 1];
return 0;
}
@@ -870,7 +846,8 @@
return -EINVAL;
}
- snprintf(name, 32, "lp5523:channel%d", chan);
+ snprintf(name, sizeof(name), "%s:channel%d",
+ pdata->label ?: "lp5523", chan);
led->cdev.name = name;
led->cdev.brightness_set = lp5523_set_brightness;
diff --git a/drivers/leds/leds-pca9532.c b/drivers/leds/leds-pca9532.c
index 43d0875..afac338 100644
--- a/drivers/leds/leds-pca9532.c
+++ b/drivers/leds/leds-pca9532.c
@@ -200,6 +200,32 @@
pca9532_setled(led);
}
+static void pca9532_destroy_devices(struct pca9532_data *data, int n_devs)
+{
+ int i = n_devs;
+
+ if (!data)
+ return;
+
+ while (--i >= 0) {
+ switch (data->leds[i].type) {
+ case PCA9532_TYPE_NONE:
+ break;
+ case PCA9532_TYPE_LED:
+ led_classdev_unregister(&data->leds[i].ldev);
+ cancel_work_sync(&data->leds[i].work);
+ break;
+ case PCA9532_TYPE_N2100_BEEP:
+ if (data->idev != NULL) {
+ input_unregister_device(data->idev);
+ cancel_work_sync(&data->work);
+ data->idev = NULL;
+ }
+ break;
+ }
+ }
+}
+
static int pca9532_configure(struct i2c_client *client,
struct pca9532_data *data, struct pca9532_platform_data *pdata)
{
@@ -274,25 +300,7 @@
return 0;
exit:
- if (i > 0)
- for (i = i - 1; i >= 0; i--)
- switch (data->leds[i].type) {
- case PCA9532_TYPE_NONE:
- break;
- case PCA9532_TYPE_LED:
- led_classdev_unregister(&data->leds[i].ldev);
- cancel_work_sync(&data->leds[i].work);
- break;
- case PCA9532_TYPE_N2100_BEEP:
- if (data->idev != NULL) {
- input_unregister_device(data->idev);
- input_free_device(data->idev);
- cancel_work_sync(&data->work);
- data->idev = NULL;
- }
- break;
- }
-
+ pca9532_destroy_devices(data, i);
return err;
}
@@ -329,25 +337,7 @@
static int pca9532_remove(struct i2c_client *client)
{
struct pca9532_data *data = i2c_get_clientdata(client);
- int i;
- for (i = 0; i < 16; i++)
- switch (data->leds[i].type) {
- case PCA9532_TYPE_NONE:
- break;
- case PCA9532_TYPE_LED:
- led_classdev_unregister(&data->leds[i].ldev);
- cancel_work_sync(&data->leds[i].work);
- break;
- case PCA9532_TYPE_N2100_BEEP:
- if (data->idev != NULL) {
- input_unregister_device(data->idev);
- input_free_device(data->idev);
- cancel_work_sync(&data->work);
- data->idev = NULL;
- }
- break;
- }
-
+ pca9532_destroy_devices(data, 16);
kfree(data);
return 0;
}
diff --git a/drivers/leds/ledtrig-backlight.c b/drivers/leds/ledtrig-backlight.c
index f948e57..2b513a2 100644
--- a/drivers/leds/ledtrig-backlight.c
+++ b/drivers/leds/ledtrig-backlight.c
@@ -26,6 +26,7 @@
int brightness;
int old_status;
struct notifier_block notifier;
+ unsigned invert;
};
static int fb_notifier_callback(struct notifier_block *p,
@@ -36,23 +37,64 @@
struct led_classdev *led = n->led;
struct fb_event *fb_event = data;
int *blank = fb_event->data;
+ int new_status = *blank ? BLANK : UNBLANK;
switch (event) {
case FB_EVENT_BLANK :
- if (*blank && n->old_status == UNBLANK) {
+ if (new_status == n->old_status)
+ break;
+
+ if ((n->old_status == UNBLANK) ^ n->invert) {
n->brightness = led->brightness;
led_set_brightness(led, LED_OFF);
- n->old_status = BLANK;
- } else if (!*blank && n->old_status == BLANK) {
+ } else {
led_set_brightness(led, n->brightness);
- n->old_status = UNBLANK;
}
+
+ n->old_status = new_status;
+
break;
}
return 0;
}
+static ssize_t bl_trig_invert_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct led_classdev *led = dev_get_drvdata(dev);
+ struct bl_trig_notifier *n = led->trigger_data;
+
+ return sprintf(buf, "%u\n", n->invert);
+}
+
+static ssize_t bl_trig_invert_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t num)
+{
+ struct led_classdev *led = dev_get_drvdata(dev);
+ struct bl_trig_notifier *n = led->trigger_data;
+ unsigned long invert;
+ int ret;
+
+ ret = strict_strtoul(buf, 10, &invert);
+ if (ret < 0)
+ return ret;
+
+ if (invert > 1)
+ return -EINVAL;
+
+ n->invert = invert;
+
+ /* After inverting, we need to update the LED. */
+ if ((n->old_status == BLANK) ^ n->invert)
+ led_set_brightness(led, LED_OFF);
+ else
+ led_set_brightness(led, n->brightness);
+
+ return num;
+}
+static DEVICE_ATTR(inverted, 0644, bl_trig_invert_show, bl_trig_invert_store);
+
static void bl_trig_activate(struct led_classdev *led)
{
int ret;
@@ -66,6 +108,10 @@
return;
}
+ ret = device_create_file(led->dev, &dev_attr_inverted);
+ if (ret)
+ goto err_invert;
+
n->led = led;
n->brightness = led->brightness;
n->old_status = UNBLANK;
@@ -74,6 +120,12 @@
ret = fb_register_client(&n->notifier);
if (ret)
dev_err(led->dev, "unable to register backlight trigger\n");
+
+ return;
+
+err_invert:
+ led->trigger_data = NULL;
+ kfree(n);
}
static void bl_trig_deactivate(struct led_classdev *led)
@@ -82,6 +134,7 @@
(struct bl_trig_notifier *) led->trigger_data;
if (n) {
+ device_remove_file(led->dev, &dev_attr_inverted);
fb_unregister_client(&n->notifier);
kfree(n);
}
diff --git a/drivers/macintosh/via-pmu-backlight.c b/drivers/macintosh/via-pmu-backlight.c
index 1cec02f..ade1e65 100644
--- a/drivers/macintosh/via-pmu-backlight.c
+++ b/drivers/macintosh/via-pmu-backlight.c
@@ -15,7 +15,7 @@
#define MAX_PMU_LEVEL 0xFF
-static struct backlight_ops pmu_backlight_data;
+static const struct backlight_ops pmu_backlight_data;
static DEFINE_SPINLOCK(pmu_backlight_lock);
static int sleeping, uses_pmu_bl;
static u8 bl_curve[FB_BACKLIGHT_LEVELS];
@@ -115,7 +115,7 @@
return bd->props.brightness;
}
-static struct backlight_ops pmu_backlight_data = {
+static const struct backlight_ops pmu_backlight_data = {
.get_brightness = pmu_backlight_get_brightness,
.update_status = pmu_backlight_update_status,
diff --git a/drivers/macintosh/via-pmu.c b/drivers/macintosh/via-pmu.c
index cd29c82..8b021eb 100644
--- a/drivers/macintosh/via-pmu.c
+++ b/drivers/macintosh/via-pmu.c
@@ -2257,7 +2257,7 @@
&& (pmac_call_feature(PMAC_FTR_SLEEP_STATE, NULL, 0, -1) >= 0);
}
-static struct platform_suspend_ops pmu_pm_ops = {
+static const struct platform_suspend_ops pmu_pm_ops = {
.enter = powerbook_sleep,
.valid = pmu_sleep_valid,
};
diff --git a/drivers/media/video/cx18/cx23418.h b/drivers/media/video/cx18/cx23418.h
index 2c00980..7e40035 100644
--- a/drivers/media/video/cx18/cx23418.h
+++ b/drivers/media/video/cx18/cx23418.h
@@ -177,7 +177,7 @@
IN[0] - Task handle.
IN[1] - luma type: 0 = disable, 1 = 1D horizontal only, 2 = 1D vertical only,
3 = 2D H/V separable, 4 = 2D symmetric non-separable
- IN[2] - chroma type: 0 - diable, 1 = 1D horizontal
+ IN[2] - chroma type: 0 - disable, 1 = 1D horizontal
ReturnCode - One of the ERR_CAPTURE_... */
#define CX18_CPU_SET_SPATIAL_FILTER_TYPE (CPU_CMD_MASK_CAPTURE | 0x000C)
diff --git a/drivers/media/video/cx25840/cx25840-ir.c b/drivers/media/video/cx25840/cx25840-ir.c
index 627926f..7eb79af 100644
--- a/drivers/media/video/cx25840/cx25840-ir.c
+++ b/drivers/media/video/cx25840/cx25840-ir.c
@@ -261,7 +261,7 @@
u32 rem;
/*
- * The 2 lsb's of the pulse width timer count are not accessable, hence
+ * The 2 lsb's of the pulse width timer count are not accessible, hence
* the (1 << 2)
*/
n = ((u64) ns) * CX25840_IR_REFCLK_FREQ / 1000000; /* millicycles */
diff --git a/drivers/media/video/davinci/vpif.h b/drivers/media/video/davinci/vpif.h
index 188841b..ebd5c43 100644
--- a/drivers/media/video/davinci/vpif.h
+++ b/drivers/media/video/davinci/vpif.h
@@ -33,7 +33,7 @@
#define regr(reg) readl((reg) + vpif_base)
#define regw(value, reg) writel(value, (reg + vpif_base))
-/* Register Addresss Offsets */
+/* Register Address Offsets */
#define VPIF_PID (0x0000)
#define VPIF_CH0_CTRL (0x0004)
#define VPIF_CH1_CTRL (0x0008)
diff --git a/drivers/media/video/davinci/vpss.c b/drivers/media/video/davinci/vpss.c
index 7918680..3e5cf27 100644
--- a/drivers/media/video/davinci/vpss.c
+++ b/drivers/media/video/davinci/vpss.c
@@ -85,7 +85,7 @@
/*
* vpss operations. Depends on platform. Not all functions are available
* on all platforms. The api, first check if a functio is available before
- * invoking it. In the probe, the function ptrs are intialized based on
+ * invoking it. In the probe, the function ptrs are initialized based on
* vpss name. vpss name can be "dm355_vpss", "dm644x_vpss" etc.
*/
struct vpss_hw_ops {
diff --git a/drivers/media/video/omap/omap_vout.c b/drivers/media/video/omap/omap_vout.c
index 83de97a..029a4ba 100644
--- a/drivers/media/video/omap/omap_vout.c
+++ b/drivers/media/video/omap/omap_vout.c
@@ -1286,7 +1286,7 @@
videobuf_mmap_free(q);
/* Even if apply changes fails we should continue
- freeing allocated memeory */
+ freeing allocated memory */
if (vout->streaming) {
u32 mask = 0;
diff --git a/drivers/media/video/saa7164/saa7164-core.c b/drivers/media/video/saa7164/saa7164-core.c
index d6bf3f8..58af67f 100644
--- a/drivers/media/video/saa7164/saa7164-core.c
+++ b/drivers/media/video/saa7164/saa7164-core.c
@@ -655,8 +655,8 @@
goto out;
}
- /* Check that the hardware is accessable. If the status bytes are
- * 0xFF then the device is not accessable, the the IRQ belongs
+ /* Check that the hardware is accessible. If the status bytes are
+ * 0xFF then the device is not accessible, the the IRQ belongs
* to another driver.
* 4 x u32 interrupt registers.
*/
diff --git a/drivers/media/video/sn9c102/sn9c102_sensor.h b/drivers/media/video/sn9c102/sn9c102_sensor.h
index 494957b..7f38549 100644
--- a/drivers/media/video/sn9c102/sn9c102_sensor.h
+++ b/drivers/media/video/sn9c102/sn9c102_sensor.h
@@ -147,7 +147,7 @@
struct sn9c102_sensor {
char name[32], /* sensor name */
- maintainer[64]; /* name of the mantainer <email> */
+ maintainer[64]; /* name of the maintainer <email> */
enum sn9c102_bridge supported_bridge; /* supported SN9C1xx bridges */
diff --git a/drivers/media/video/tvp7002.c b/drivers/media/video/tvp7002.c
index e63b40f..c799e4e 100644
--- a/drivers/media/video/tvp7002.c
+++ b/drivers/media/video/tvp7002.c
@@ -789,7 +789,7 @@
* Get the value of a TVP7002 decoder device register.
* Returns zero when successful, -EINVAL if register read fails or
* access to I2C client fails, -EPERM if the call is not allowed
- * by diabled CAP_SYS_ADMIN.
+ * by disabled CAP_SYS_ADMIN.
*/
static int tvp7002_g_register(struct v4l2_subdev *sd,
struct v4l2_dbg_register *reg)
diff --git a/drivers/media/video/via-camera.c b/drivers/media/video/via-camera.c
index e25aca5..2f973cd 100644
--- a/drivers/media/video/via-camera.c
+++ b/drivers/media/video/via-camera.c
@@ -13,14 +13,12 @@
#include <linux/pci.h>
#include <linux/gpio.h>
#include <linux/interrupt.h>
-#include <linux/pci.h>
#include <linux/platform_device.h>
#include <linux/videodev2.h>
#include <media/v4l2-device.h>
#include <media/v4l2-ioctl.h>
#include <media/v4l2-chip-ident.h>
#include <media/videobuf-dma-sg.h>
-#include <linux/device.h>
#include <linux/delay.h>
#include <linux/dma-mapping.h>
#include <linux/pm_qos_params.h>
diff --git a/drivers/memstick/core/memstick.c b/drivers/memstick/core/memstick.c
index c00fe82..e9a3eab 100644
--- a/drivers/memstick/core/memstick.c
+++ b/drivers/memstick/core/memstick.c
@@ -465,6 +465,7 @@
if (!host->card) {
host->card = card;
if (device_register(&card->dev)) {
+ put_device(&card->dev);
kfree(host->card);
host->card = NULL;
}
@@ -510,14 +511,18 @@
{
int rc;
- if (!idr_pre_get(&memstick_host_idr, GFP_KERNEL))
- return -ENOMEM;
+ while (1) {
+ if (!idr_pre_get(&memstick_host_idr, GFP_KERNEL))
+ return -ENOMEM;
- spin_lock(&memstick_host_lock);
- rc = idr_get_new(&memstick_host_idr, host, &host->id);
- spin_unlock(&memstick_host_lock);
- if (rc)
- return rc;
+ spin_lock(&memstick_host_lock);
+ rc = idr_get_new(&memstick_host_idr, host, &host->id);
+ spin_unlock(&memstick_host_lock);
+ if (!rc)
+ break;
+ else if (rc != -EAGAIN)
+ return rc;
+ }
dev_set_name(&host->dev, "memstick%u", host->id);
diff --git a/drivers/memstick/core/mspro_block.c b/drivers/memstick/core/mspro_block.c
index 02362ec..57b42bf 100644
--- a/drivers/memstick/core/mspro_block.c
+++ b/drivers/memstick/core/mspro_block.c
@@ -23,7 +23,6 @@
#define DRIVER_NAME "mspro_block"
-static DEFINE_MUTEX(mspro_block_mutex);
static int major;
module_param(major, int, 0644);
@@ -160,6 +159,13 @@
int (*mrq_handler)(struct memstick_dev *card,
struct memstick_request **mrq);
+
+ /* Default request setup function for data access method preferred by
+ * this host instance.
+ */
+ void (*setup_transfer)(struct memstick_dev *card,
+ u64 offset, size_t length);
+
struct attribute_group attr_group;
struct scatterlist req_sg[MSPRO_BLOCK_MAX_SEGS];
@@ -181,7 +187,6 @@
struct mspro_block_data *msb = disk->private_data;
int rc = -ENXIO;
- mutex_lock(&mspro_block_mutex);
mutex_lock(&mspro_block_disk_lock);
if (msb && msb->card) {
@@ -193,7 +198,6 @@
}
mutex_unlock(&mspro_block_disk_lock);
- mutex_unlock(&mspro_block_mutex);
return rc;
}
@@ -225,11 +229,7 @@
static int mspro_block_bd_release(struct gendisk *disk, fmode_t mode)
{
- int ret;
- mutex_lock(&mspro_block_mutex);
- ret = mspro_block_disk_release(disk);
- mutex_unlock(&mspro_block_mutex);
- return ret;
+ return mspro_block_disk_release(disk);
}
static int mspro_block_bd_getgeo(struct block_device *bdev,
@@ -663,14 +663,43 @@
}
}
+/*** Transfer setup functions for different access methods. ***/
+
+/** Setup data transfer request for SET_CMD TPC with arguments in card
+ * registers.
+ *
+ * @card Current media instance
+ * @offset Target data offset in bytes
+ * @length Required transfer length in bytes.
+ */
+static void h_mspro_block_setup_cmd(struct memstick_dev *card, u64 offset,
+ size_t length)
+{
+ struct mspro_block_data *msb = memstick_get_drvdata(card);
+ struct mspro_param_register param = {
+ .system = msb->system,
+ .data_count = cpu_to_be16((uint16_t)(length / msb->page_size)),
+ /* ISO C90 warning precludes direct initialization for now. */
+ .data_address = 0,
+ .tpc_param = 0
+ };
+
+ do_div(offset, msb->page_size);
+ param.data_address = cpu_to_be32((uint32_t)offset);
+
+ card->next_request = h_mspro_block_req_init;
+ msb->mrq_handler = h_mspro_block_transfer_data;
+ memstick_init_req(&card->current_mrq, MS_TPC_WRITE_REG,
+ ¶m, sizeof(param));
+}
+
/*** Data transfer ***/
static int mspro_block_issue_req(struct memstick_dev *card, int chunk)
{
struct mspro_block_data *msb = memstick_get_drvdata(card);
- sector_t t_sec;
+ u64 t_off;
unsigned int count;
- struct mspro_param_register param;
try_again:
while (chunk) {
@@ -685,30 +714,17 @@
continue;
}
- t_sec = blk_rq_pos(msb->block_req) << 9;
- sector_div(t_sec, msb->page_size);
-
+ t_off = blk_rq_pos(msb->block_req);
+ t_off <<= 9;
count = blk_rq_bytes(msb->block_req);
- count /= msb->page_size;
- param.system = msb->system;
- param.data_count = cpu_to_be16(count);
- param.data_address = cpu_to_be32((uint32_t)t_sec);
- param.tpc_param = 0;
+ msb->setup_transfer(card, t_off, count);
msb->data_dir = rq_data_dir(msb->block_req);
msb->transfer_cmd = msb->data_dir == READ
? MSPRO_CMD_READ_DATA
: MSPRO_CMD_WRITE_DATA;
- dev_dbg(&card->dev, "data transfer: cmd %x, "
- "lba %x, count %x\n", msb->transfer_cmd,
- be32_to_cpu(param.data_address), count);
-
- card->next_request = h_mspro_block_req_init;
- msb->mrq_handler = h_mspro_block_transfer_data;
- memstick_init_req(&card->current_mrq, MS_TPC_WRITE_REG,
- ¶m, sizeof(param));
memstick_new_req(card->host);
return 0;
}
@@ -963,18 +979,16 @@
static int mspro_block_read_attributes(struct memstick_dev *card)
{
struct mspro_block_data *msb = memstick_get_drvdata(card);
- struct mspro_param_register param = {
- .system = msb->system,
- .data_count = cpu_to_be16(1),
- .data_address = 0,
- .tpc_param = 0
- };
struct mspro_attribute *attr = NULL;
struct mspro_sys_attr *s_attr = NULL;
unsigned char *buffer = NULL;
int cnt, rc, attr_count;
- unsigned int addr;
- unsigned short page_count;
+ /* While normally physical device offsets, represented here by
+ * attr_offset and attr_len will be of large numeric types, we can be
+ * sure, that attributes are close enough to the beginning of the
+ * device, to save ourselves some trouble.
+ */
+ unsigned int addr, attr_offset = 0, attr_len = msb->page_size;
attr = kmalloc(msb->page_size, GFP_KERNEL);
if (!attr)
@@ -987,10 +1001,8 @@
msb->data_dir = READ;
msb->transfer_cmd = MSPRO_CMD_READ_ATRB;
- card->next_request = h_mspro_block_req_init;
- msb->mrq_handler = h_mspro_block_transfer_data;
- memstick_init_req(&card->current_mrq, MS_TPC_WRITE_REG, ¶m,
- sizeof(param));
+ msb->setup_transfer(card, attr_offset, attr_len);
+
memstick_new_req(card->host);
wait_for_completion(&card->mrq_complete);
if (card->current_mrq.error) {
@@ -1021,13 +1033,12 @@
}
msb->attr_group.name = "media_attributes";
- buffer = kmalloc(msb->page_size, GFP_KERNEL);
+ buffer = kmalloc(attr_len, GFP_KERNEL);
if (!buffer) {
rc = -ENOMEM;
goto out_free_attr;
}
- memcpy(buffer, (char *)attr, msb->page_size);
- page_count = 1;
+ memcpy(buffer, (char *)attr, attr_len);
for (cnt = 0; cnt < attr_count; ++cnt) {
s_attr = kzalloc(sizeof(struct mspro_sys_attr), GFP_KERNEL);
@@ -1038,9 +1049,10 @@
msb->attr_group.attrs[cnt] = &s_attr->dev_attr.attr;
addr = be32_to_cpu(attr->entries[cnt].address);
- rc = be32_to_cpu(attr->entries[cnt].size);
+ s_attr->size = be32_to_cpu(attr->entries[cnt].size);
dev_dbg(&card->dev, "adding attribute %d: id %x, address %x, "
- "size %x\n", cnt, attr->entries[cnt].id, addr, rc);
+ "size %zx\n", cnt, attr->entries[cnt].id, addr,
+ s_attr->size);
s_attr->id = attr->entries[cnt].id;
if (mspro_block_attr_name(s_attr->id))
snprintf(s_attr->name, sizeof(s_attr->name), "%s",
@@ -1054,57 +1066,47 @@
s_attr->dev_attr.attr.mode = S_IRUGO;
s_attr->dev_attr.show = mspro_block_attr_show(s_attr->id);
- if (!rc)
+ if (!s_attr->size)
continue;
- s_attr->size = rc;
- s_attr->data = kmalloc(rc, GFP_KERNEL);
+ s_attr->data = kmalloc(s_attr->size, GFP_KERNEL);
if (!s_attr->data) {
rc = -ENOMEM;
goto out_free_buffer;
}
- if (((addr / msb->page_size)
- == be32_to_cpu(param.data_address))
- && (((addr + rc - 1) / msb->page_size)
- == be32_to_cpu(param.data_address))) {
+ if (((addr / msb->page_size) == (attr_offset / msb->page_size))
+ && (((addr + s_attr->size - 1) / msb->page_size)
+ == (attr_offset / msb->page_size))) {
memcpy(s_attr->data, buffer + addr % msb->page_size,
- rc);
+ s_attr->size);
continue;
}
- if (page_count <= (rc / msb->page_size)) {
+ attr_offset = (addr / msb->page_size) * msb->page_size;
+
+ if ((attr_offset + attr_len) < (addr + s_attr->size)) {
kfree(buffer);
- page_count = (rc / msb->page_size) + 1;
- buffer = kmalloc(page_count * msb->page_size,
- GFP_KERNEL);
+ attr_len = (((addr + s_attr->size) / msb->page_size)
+ + 1 ) * msb->page_size - attr_offset;
+ buffer = kmalloc(attr_len, GFP_KERNEL);
if (!buffer) {
rc = -ENOMEM;
goto out_free_attr;
}
}
- param.system = msb->system;
- param.data_count = cpu_to_be16((rc / msb->page_size) + 1);
- param.data_address = cpu_to_be32(addr / msb->page_size);
- param.tpc_param = 0;
-
- sg_init_one(&msb->req_sg[0], buffer,
- be16_to_cpu(param.data_count) * msb->page_size);
+ sg_init_one(&msb->req_sg[0], buffer, attr_len);
msb->seg_count = 1;
msb->current_seg = 0;
msb->current_page = 0;
msb->data_dir = READ;
msb->transfer_cmd = MSPRO_CMD_READ_ATRB;
- dev_dbg(&card->dev, "reading attribute pages %x, %x\n",
- be32_to_cpu(param.data_address),
- be16_to_cpu(param.data_count));
+ dev_dbg(&card->dev, "reading attribute range %x, %x\n",
+ attr_offset, attr_len);
- card->next_request = h_mspro_block_req_init;
- msb->mrq_handler = h_mspro_block_transfer_data;
- memstick_init_req(&card->current_mrq, MS_TPC_WRITE_REG,
- (char *)¶m, sizeof(param));
+ msb->setup_transfer(card, attr_offset, attr_len);
memstick_new_req(card->host);
wait_for_completion(&card->mrq_complete);
if (card->current_mrq.error) {
@@ -1112,7 +1114,8 @@
goto out_free_buffer;
}
- memcpy(s_attr->data, buffer + addr % msb->page_size, rc);
+ memcpy(s_attr->data, buffer + addr % msb->page_size,
+ s_attr->size);
}
rc = 0;
@@ -1130,6 +1133,8 @@
int rc = 0;
msb->system = MEMSTICK_SYS_SERIAL;
+ msb->setup_transfer = h_mspro_block_setup_cmd;
+
card->reg_addr.r_offset = offsetof(struct mspro_register, status);
card->reg_addr.r_length = sizeof(struct ms_status_register);
card->reg_addr.w_offset = offsetof(struct mspro_register, param);
@@ -1206,10 +1211,12 @@
msb->page_size = be16_to_cpu(sys_info->unit_size);
- if (!idr_pre_get(&mspro_block_disk_idr, GFP_KERNEL))
- return -ENOMEM;
-
mutex_lock(&mspro_block_disk_lock);
+ if (!idr_pre_get(&mspro_block_disk_idr, GFP_KERNEL)) {
+ mutex_unlock(&mspro_block_disk_lock);
+ return -ENOMEM;
+ }
+
rc = idr_get_new(&mspro_block_disk_idr, card, &disk_id);
mutex_unlock(&mspro_block_disk_lock);
diff --git a/drivers/memstick/host/jmb38x_ms.c b/drivers/memstick/host/jmb38x_ms.c
index f2b894c..d89d925 100644
--- a/drivers/memstick/host/jmb38x_ms.c
+++ b/drivers/memstick/host/jmb38x_ms.c
@@ -61,6 +61,7 @@
struct memstick_request *req;
unsigned char cmd_flags;
unsigned char io_pos;
+ unsigned char ifmode;
unsigned int io_word[2];
};
@@ -136,15 +137,14 @@
#define PAD_PU_PD_ON_MS_SOCK0 0x5f8f0000
#define PAD_PU_PD_ON_MS_SOCK1 0x0f0f0000
+#define CLOCK_CONTROL_BY_MMIO 0x00000008
#define CLOCK_CONTROL_40MHZ 0x00000001
-#define CLOCK_CONTROL_50MHZ 0x0000000a
-#define CLOCK_CONTROL_60MHZ 0x00000008
-#define CLOCK_CONTROL_62_5MHZ 0x0000000c
+#define CLOCK_CONTROL_50MHZ 0x00000002
+#define CLOCK_CONTROL_60MHZ 0x00000010
+#define CLOCK_CONTROL_62_5MHZ 0x00000004
#define CLOCK_CONTROL_OFF 0x00000000
#define PCI_CTL_CLOCK_DLY_ADDR 0x000000b0
-#define PCI_CTL_CLOCK_DLY_MASK_A 0x00000f00
-#define PCI_CTL_CLOCK_DLY_MASK_B 0x0000f000
enum {
CMD_READY = 0x01,
@@ -390,8 +390,13 @@
if (host->req->data_dir == READ)
cmd |= TPC_DIR;
- if (host->req->need_card_int)
- cmd |= TPC_WAIT_INT;
+
+ if (host->req->need_card_int) {
+ if (host->ifmode == MEMSTICK_SERIAL)
+ cmd |= TPC_GET_INT;
+ else
+ cmd |= TPC_WAIT_INT;
+ }
data = host->req->data;
@@ -529,7 +534,10 @@
if (irq_status & INT_STATUS_ANY_ERR) {
if (irq_status & INT_STATUS_CRC_ERR)
host->req->error = -EILSEQ;
- else
+ else if (irq_status & INT_STATUS_TPC_ERR) {
+ dev_dbg(&host->chip->pdev->dev, "TPC_ERR\n");
+ jmb38x_ms_complete_cmd(msh, 0);
+ } else
host->req->error = -ETIME;
} else {
if (host->cmd_flags & DMA_DATA) {
@@ -644,7 +652,6 @@
ndelay(20);
}
dev_dbg(&host->chip->pdev->dev, "reset_req timeout\n");
- /* return -EIO; */
reset_next:
writel(HOST_CONTROL_RESET | HOST_CONTROL_CLOCK_EN
@@ -675,7 +682,7 @@
{
struct jmb38x_ms_host *host = memstick_priv(msh);
unsigned int host_ctl = readl(host->addr + HOST_CONTROL);
- unsigned int clock_ctl = CLOCK_CONTROL_40MHZ, clock_delay = 0;
+ unsigned int clock_ctl = CLOCK_CONTROL_BY_MMIO, clock_delay = 0;
int rc = 0;
switch (param) {
@@ -687,9 +694,7 @@
host_ctl = 7;
host_ctl |= HOST_CONTROL_POWER_EN
- | HOST_CONTROL_CLOCK_EN
- | HOST_CONTROL_HW_OC_P
- | HOST_CONTROL_TDELAY_EN;
+ | HOST_CONTROL_CLOCK_EN;
writel(host_ctl, host->addr + HOST_CONTROL);
writel(host->id ? PAD_PU_PD_ON_MS_SOCK1
@@ -712,46 +717,88 @@
return -EINVAL;
break;
case MEMSTICK_INTERFACE:
+ dev_dbg(&host->chip->pdev->dev,
+ "Set Host Interface Mode to %d\n", value);
+ host_ctl &= ~(HOST_CONTROL_FAST_CLK | HOST_CONTROL_REI |
+ HOST_CONTROL_REO);
+ host_ctl |= HOST_CONTROL_TDELAY_EN | HOST_CONTROL_HW_OC_P;
host_ctl &= ~(3 << HOST_CONTROL_IF_SHIFT);
- pci_read_config_dword(host->chip->pdev,
- PCI_CTL_CLOCK_DLY_ADDR,
- &clock_delay);
- clock_delay &= host->id ? ~PCI_CTL_CLOCK_DLY_MASK_B
- : ~PCI_CTL_CLOCK_DLY_MASK_A;
if (value == MEMSTICK_SERIAL) {
- host_ctl &= ~HOST_CONTROL_FAST_CLK;
- host_ctl &= ~HOST_CONTROL_REO;
host_ctl |= HOST_CONTROL_IF_SERIAL
<< HOST_CONTROL_IF_SHIFT;
host_ctl |= HOST_CONTROL_REI;
- clock_ctl = CLOCK_CONTROL_40MHZ;
+ clock_ctl |= CLOCK_CONTROL_40MHZ;
+ clock_delay = 0;
} else if (value == MEMSTICK_PAR4) {
- host_ctl |= HOST_CONTROL_FAST_CLK | HOST_CONTROL_REO;
+ host_ctl |= HOST_CONTROL_FAST_CLK;
host_ctl |= HOST_CONTROL_IF_PAR4
<< HOST_CONTROL_IF_SHIFT;
- host_ctl &= ~HOST_CONTROL_REI;
- clock_ctl = CLOCK_CONTROL_40MHZ;
- clock_delay |= host->id ? (4 << 12) : (4 << 8);
+ host_ctl |= HOST_CONTROL_REO;
+ clock_ctl |= CLOCK_CONTROL_40MHZ;
+ clock_delay = 4;
} else if (value == MEMSTICK_PAR8) {
host_ctl |= HOST_CONTROL_FAST_CLK;
host_ctl |= HOST_CONTROL_IF_PAR8
<< HOST_CONTROL_IF_SHIFT;
- host_ctl &= ~(HOST_CONTROL_REI | HOST_CONTROL_REO);
- clock_ctl = CLOCK_CONTROL_50MHZ;
+ clock_ctl |= CLOCK_CONTROL_50MHZ;
+ clock_delay = 0;
} else
return -EINVAL;
writel(host_ctl, host->addr + HOST_CONTROL);
+ writel(CLOCK_CONTROL_OFF, host->addr + CLOCK_CONTROL);
writel(clock_ctl, host->addr + CLOCK_CONTROL);
- pci_write_config_dword(host->chip->pdev,
- PCI_CTL_CLOCK_DLY_ADDR,
- clock_delay);
+ pci_write_config_byte(host->chip->pdev,
+ PCI_CTL_CLOCK_DLY_ADDR + 1,
+ clock_delay);
+ host->ifmode = value;
break;
};
return 0;
}
+#define PCI_PMOS0_CONTROL 0xae
+#define PMOS0_ENABLE 0x01
+#define PMOS0_OVERCURRENT_LEVEL_2_4V 0x06
+#define PMOS0_EN_OVERCURRENT_DEBOUNCE 0x40
+#define PMOS0_SW_LED_POLARITY_ENABLE 0x80
+#define PMOS0_ACTIVE_BITS (PMOS0_ENABLE | PMOS0_EN_OVERCURRENT_DEBOUNCE | \
+ PMOS0_OVERCURRENT_LEVEL_2_4V)
+#define PCI_PMOS1_CONTROL 0xbd
+#define PMOS1_ACTIVE_BITS 0x4a
+#define PCI_CLOCK_CTL 0xb9
+
+static int jmb38x_ms_pmos(struct pci_dev *pdev, int flag)
+{
+ unsigned char val;
+
+ pci_read_config_byte(pdev, PCI_PMOS0_CONTROL, &val);
+ if (flag)
+ val |= PMOS0_ACTIVE_BITS;
+ else
+ val &= ~PMOS0_ACTIVE_BITS;
+ pci_write_config_byte(pdev, PCI_PMOS0_CONTROL, val);
+ dev_dbg(&pdev->dev, "JMB38x: set PMOS0 val 0x%x\n", val);
+
+ if (pci_resource_flags(pdev, 1)) {
+ pci_read_config_byte(pdev, PCI_PMOS1_CONTROL, &val);
+ if (flag)
+ val |= PMOS1_ACTIVE_BITS;
+ else
+ val &= ~PMOS1_ACTIVE_BITS;
+ pci_write_config_byte(pdev, PCI_PMOS1_CONTROL, val);
+ dev_dbg(&pdev->dev, "JMB38x: set PMOS1 val 0x%x\n", val);
+ }
+
+ pci_read_config_byte(pdev, PCI_CLOCK_CTL, &val);
+ pci_write_config_byte(pdev, PCI_CLOCK_CTL, val & ~0x0f);
+ pci_write_config_byte(pdev, PCI_CLOCK_CTL, val | 0x01);
+ dev_dbg(&pdev->dev, "Clock Control by PCI config is disabled!\n");
+
+ return 0;
+}
+
#ifdef CONFIG_PM
static int jmb38x_ms_suspend(struct pci_dev *dev, pm_message_t state)
@@ -784,8 +831,7 @@
return rc;
pci_set_master(dev);
- pci_read_config_dword(dev, 0xac, &rc);
- pci_write_config_dword(dev, 0xac, rc | 0x00470000);
+ jmb38x_ms_pmos(dev, 1);
for (rc = 0; rc < jm->host_cnt; ++rc) {
if (!jm->hosts[rc])
@@ -894,8 +940,7 @@
goto err_out;
}
- pci_read_config_dword(pdev, 0xac, &rc);
- pci_write_config_dword(pdev, 0xac, rc | 0x00470000);
+ jmb38x_ms_pmos(pdev, 1);
cnt = jmb38x_ms_count_slots(pdev);
if (!cnt) {
@@ -976,6 +1021,8 @@
jmb38x_ms_free_host(jm->hosts[cnt]);
}
+ jmb38x_ms_pmos(dev, 0);
+
pci_set_drvdata(dev, NULL);
pci_release_regions(dev);
pci_disable_device(dev);
@@ -983,8 +1030,9 @@
}
static struct pci_device_id jmb38x_ms_id_tbl [] = {
- { PCI_VENDOR_ID_JMICRON, PCI_DEVICE_ID_JMICRON_JMB38X_MS, PCI_ANY_ID,
- PCI_ANY_ID, 0, 0, 0 },
+ { PCI_VDEVICE(JMICRON, PCI_DEVICE_ID_JMICRON_JMB38X_MS) },
+ { PCI_VDEVICE(JMICRON, PCI_DEVICE_ID_JMICRON_JMB385_MS) },
+ { PCI_VDEVICE(JMICRON, PCI_DEVICE_ID_JMICRON_JMB390_MS) },
{ }
};
diff --git a/drivers/message/fusion/lsi/mpi_log_sas.h b/drivers/message/fusion/lsi/mpi_log_sas.h
index 691620d..8b04810 100644
--- a/drivers/message/fusion/lsi/mpi_log_sas.h
+++ b/drivers/message/fusion/lsi/mpi_log_sas.h
@@ -268,7 +268,7 @@
/* Compatibility Error : IR Disabled */
#define IR_LOGINFO_COMPAT_ERROR_RAID_DISABLED (0x00010030)
-/* Compatibility Error : Inquiry Comand failed */
+/* Compatibility Error : Inquiry Command failed */
#define IR_LOGINFO_COMPAT_ERROR_INQUIRY_FAILED (0x00010031)
/* Compatibility Error : Device not direct access device */
#define IR_LOGINFO_COMPAT_ERROR_NOT_DIRECT_ACCESS (0x00010032)
diff --git a/drivers/message/fusion/mptbase.c b/drivers/message/fusion/mptbase.c
index 3e57b61..3358c0a 100644
--- a/drivers/message/fusion/mptbase.c
+++ b/drivers/message/fusion/mptbase.c
@@ -7977,7 +7977,7 @@
NULL, /* 2Eh */
NULL, /* 2Fh */
"Compatibility Error: IR Disabled", /* 30h */
- "Compatibility Error: Inquiry Comand Failed", /* 31h */
+ "Compatibility Error: Inquiry Command Failed", /* 31h */
"Compatibility Error: Device not Direct Access "
"Device ", /* 32h */
"Compatibility Error: Removable Device Found", /* 33h */
diff --git a/drivers/message/fusion/mptsas.c b/drivers/message/fusion/mptsas.c
index d48c2c6..8aefb18 100644
--- a/drivers/message/fusion/mptsas.c
+++ b/drivers/message/fusion/mptsas.c
@@ -1146,7 +1146,7 @@
*
* This function will delete scheduled target reset from the list and
* try to send next target reset. This will be called from completion
- * context of any Task managment command.
+ * context of any Task management command.
*/
void
diff --git a/drivers/message/i2o/i2o_block.c b/drivers/message/i2o/i2o_block.c
index f87a9d4..ae7cad1 100644
--- a/drivers/message/i2o/i2o_block.c
+++ b/drivers/message/i2o/i2o_block.c
@@ -309,7 +309,7 @@
* @ireq: I2O block request
* @mptr: message body pointer
*
- * Builds the SG list and map it to be accessable by the controller.
+ * Builds the SG list and map it to be accessible by the controller.
*
* Returns 0 on failure or 1 on success.
*/
diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig
index 4d073f1..1e1a4be 100644
--- a/drivers/misc/Kconfig
+++ b/drivers/misc/Kconfig
@@ -402,7 +402,7 @@
DAC7512 16-bit digital-to-analog converter.
This driver can also be built as a module. If so, the module
- will be calles ti_dac7512.
+ will be called ti_dac7512.
config VMWARE_BALLOON
tristate "VMware Balloon Driver"
diff --git a/drivers/misc/arm-charlcd.c b/drivers/misc/arm-charlcd.c
index 9e3879e..fe8616a 100644
--- a/drivers/misc/arm-charlcd.c
+++ b/drivers/misc/arm-charlcd.c
@@ -313,7 +313,7 @@
INIT_DELAYED_WORK(&lcd->init_work, charlcd_init_work);
schedule_delayed_work(&lcd->init_work, 0);
- dev_info(&pdev->dev, "initalized ARM character LCD at %08x\n",
+ dev_info(&pdev->dev, "initialized ARM character LCD at %08x\n",
lcd->phybase);
return 0;
diff --git a/drivers/misc/vmw_balloon.c b/drivers/misc/vmw_balloon.c
index 2a1e804..4d2ea8e 100644
--- a/drivers/misc/vmw_balloon.c
+++ b/drivers/misc/vmw_balloon.c
@@ -45,7 +45,7 @@
MODULE_AUTHOR("VMware, Inc.");
MODULE_DESCRIPTION("VMware Memory Control (Balloon) Driver");
-MODULE_VERSION("1.2.1.1-k");
+MODULE_VERSION("1.2.1.2-k");
MODULE_ALIAS("dmi:*:svnVMware*:*");
MODULE_ALIAS("vmware_vmmemctl");
MODULE_LICENSE("GPL");
@@ -315,7 +315,8 @@
* fear that guest will need it. Host may reject some pages, we need to
* check the return value and maybe submit a different page.
*/
-static bool vmballoon_send_lock_page(struct vmballoon *b, unsigned long pfn)
+static bool vmballoon_send_lock_page(struct vmballoon *b, unsigned long pfn,
+ unsigned int *hv_status)
{
unsigned long status, dummy;
u32 pfn32;
@@ -326,7 +327,7 @@
STATS_INC(b->stats.lock);
- status = VMWARE_BALLOON_CMD(LOCK, pfn, dummy);
+ *hv_status = status = VMWARE_BALLOON_CMD(LOCK, pfn, dummy);
if (vmballoon_check_status(b, status))
return true;
@@ -410,6 +411,7 @@
{
struct page *page;
gfp_t flags;
+ unsigned int hv_status;
bool locked = false;
do {
@@ -429,11 +431,12 @@
}
/* inform monitor */
- locked = vmballoon_send_lock_page(b, page_to_pfn(page));
+ locked = vmballoon_send_lock_page(b, page_to_pfn(page), &hv_status);
if (!locked) {
STATS_INC(b->stats.refused_alloc);
- if (b->reset_required) {
+ if (hv_status == VMW_BALLOON_ERROR_RESET ||
+ hv_status == VMW_BALLOON_ERROR_PPN_NOTNEEDED) {
__free_page(page);
return -EIO;
}
diff --git a/drivers/mmc/card/block.c b/drivers/mmc/card/block.c
index 217f820..bfc8a8a 100644
--- a/drivers/mmc/card/block.c
+++ b/drivers/mmc/card/block.c
@@ -257,7 +257,7 @@
cmd.flags = MMC_RSP_SPI_R2 | MMC_RSP_R1 | MMC_CMD_AC;
err = mmc_wait_for_cmd(card->host, &cmd, 0);
if (err)
- printk(KERN_ERR "%s: error %d sending status comand",
+ printk(KERN_ERR "%s: error %d sending status command",
req->rq_disk->disk_name, err);
return cmd.resp[0];
}
diff --git a/drivers/mmc/host/Kconfig b/drivers/mmc/host/Kconfig
index c22a4c0..afe8c6f 100644
--- a/drivers/mmc/host/Kconfig
+++ b/drivers/mmc/host/Kconfig
@@ -501,7 +501,7 @@
tristate "SuperH Internal MMCIF support"
depends on MMC_BLOCK && (SUPERH || ARCH_SHMOBILE)
help
- This selects the MMC Host Interface controler (MMCIF).
+ This selects the MMC Host Interface controller (MMCIF).
This driver supports MMCIF in sh7724/sh7757/sh7372.
diff --git a/drivers/mmc/host/au1xmmc.c b/drivers/mmc/host/au1xmmc.c
index 41e5a60..ef72e87 100644
--- a/drivers/mmc/host/au1xmmc.c
+++ b/drivers/mmc/host/au1xmmc.c
@@ -192,7 +192,7 @@
au_writel(config2 | SD_CONFIG2_DF, HOST_CONFIG2(host));
au_sync();
- /* Send the stop commmand */
+ /* Send the stop command */
au_writel(STOP_CMD, HOST_CMD(host));
}
diff --git a/drivers/mmc/host/sdricoh_cs.c b/drivers/mmc/host/sdricoh_cs.c
index f472c27..bbc298f 100644
--- a/drivers/mmc/host/sdricoh_cs.c
+++ b/drivers/mmc/host/sdricoh_cs.c
@@ -446,7 +446,7 @@
mmc->max_seg_size = 1024 * 512;
mmc->max_blk_size = 512;
- /* reset the controler */
+ /* reset the controller */
if (sdricoh_reset(host)) {
dev_dbg(dev, "could not reset\n");
result = -EIO;
@@ -478,7 +478,7 @@
dev_info(&pcmcia_dev->dev, "Searching MMC controller for pcmcia device"
" %s %s ...\n", pcmcia_dev->prod_id[0], pcmcia_dev->prod_id[1]);
- /* search pci cardbus bridge that contains the mmc controler */
+ /* search pci cardbus bridge that contains the mmc controller */
/* the io region is already claimed by yenta_socket... */
while ((pci_dev =
pci_get_device(PCI_VENDOR_ID_RICOH, PCI_DEVICE_ID_RICOH_RL5C476,
diff --git a/drivers/mtd/mtdoops.c b/drivers/mtd/mtdoops.c
index 1ee72f3..c948150 100644
--- a/drivers/mtd/mtdoops.c
+++ b/drivers/mtd/mtdoops.c
@@ -307,6 +307,11 @@
unsigned long l1_cpy, l2_cpy;
char *dst;
+ if (reason != KMSG_DUMP_OOPS &&
+ reason != KMSG_DUMP_PANIC &&
+ reason != KMSG_DUMP_KEXEC)
+ return;
+
/* Only dump oopses if dump_oops is set */
if (reason == KMSG_DUMP_OOPS && !dump_oops)
return;
diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c
index 1f75a1b..31bf376 100644
--- a/drivers/mtd/nand/nand_base.c
+++ b/drivers/mtd/nand/nand_base.c
@@ -821,7 +821,7 @@
*
* Wait for command done. This is a helper function for nand_wait used when
* we are in interrupt context. May happen when in panic and trying to write
- * an oops trough mtdoops.
+ * an oops through mtdoops.
*/
static void panic_nand_wait(struct mtd_info *mtd, struct nand_chip *chip,
unsigned long timeo)
diff --git a/drivers/net/Kconfig b/drivers/net/Kconfig
index ff652c7..4c8bfc9 100644
--- a/drivers/net/Kconfig
+++ b/drivers/net/Kconfig
@@ -2963,6 +2963,7 @@
config XEN_NETDEV_FRONTEND
tristate "Xen network device frontend driver"
depends on XEN
+ select XEN_XENBUS_FRONTEND
default y
help
The network device frontend driver allows the kernel to
diff --git a/drivers/net/bnx2x/bnx2x.h b/drivers/net/bnx2x/bnx2x.h
index 6a858a2..a6cd335 100644
--- a/drivers/net/bnx2x/bnx2x.h
+++ b/drivers/net/bnx2x/bnx2x.h
@@ -1415,12 +1415,12 @@
else
/* skip rx queue
- * if FCOE l2 support is diabled and this is the fcoe L2 queue
+ * if FCOE l2 support is disabled and this is the fcoe L2 queue
*/
#define skip_rx_queue(bp, idx) (NO_FCOE(bp) && IS_FCOE_IDX(idx))
/* skip tx queue
- * if FCOE l2 support is diabled and this is the fcoe L2 queue
+ * if FCOE l2 support is disabled and this is the fcoe L2 queue
*/
#define skip_tx_queue(bp, idx) (NO_FCOE(bp) && IS_FCOE_IDX(idx))
diff --git a/drivers/net/bnx2x/bnx2x_main.c b/drivers/net/bnx2x/bnx2x_main.c
index 84e1af4..8cdcf5b 100644
--- a/drivers/net/bnx2x/bnx2x_main.c
+++ b/drivers/net/bnx2x/bnx2x_main.c
@@ -5037,7 +5037,7 @@
memset(&ilt_cli, 0, sizeof(struct ilt_client_info));
memset(&ilt, 0, sizeof(struct bnx2x_ilt));
- /* initalize dummy TM client */
+ /* initialize dummy TM client */
ilt_cli.start = 0;
ilt_cli.end = ILT_NUM_PAGE_ENTRIES - 1;
ilt_cli.client_num = ILT_CLIENT_TM;
diff --git a/drivers/net/bnx2x/bnx2x_reg.h b/drivers/net/bnx2x/bnx2x_reg.h
index 38ef7ca..c939683 100644
--- a/drivers/net/bnx2x/bnx2x_reg.h
+++ b/drivers/net/bnx2x/bnx2x_reg.h
@@ -1633,7 +1633,7 @@
(~misc_registers_sw_timer_cfg_4.sw_timer_cfg_4[1] ) is set */
#define MISC_REG_SW_TIMER_RELOAD_VAL_4 0xa2fc
/* [RW 32] the value of the counter for sw timers1-8. there are 8 addresses
- in this register. addres 0 - timer 1; address 1 - timer 2, ... address 7 -
+ in this register. address 0 - timer 1; address 1 - timer 2, ... address 7 -
timer 8 */
#define MISC_REG_SW_TIMER_VAL 0xa5c0
/* [RW 1] Set by the MCP to remember if one or more of the drivers is/are
diff --git a/drivers/net/bonding/bond_3ad.c b/drivers/net/bonding/bond_3ad.c
index 48cf24f..171782e 100644
--- a/drivers/net/bonding/bond_3ad.c
+++ b/drivers/net/bonding/bond_3ad.c
@@ -840,7 +840,7 @@
lacpdu_header = (struct lacpdu_header *)skb_put(skb, length);
memcpy(lacpdu_header->hdr.h_dest, lacpdu_mcast_addr, ETH_ALEN);
- /* Note: source addres is set to be the member's PERMANENT address,
+ /* Note: source address is set to be the member's PERMANENT address,
because we use it to identify loopback lacpdus in receive. */
memcpy(lacpdu_header->hdr.h_source, slave->perm_hwaddr, ETH_ALEN);
lacpdu_header->hdr.h_proto = PKT_TYPE_LACPDU;
@@ -881,7 +881,7 @@
marker_header = (struct bond_marker_header *)skb_put(skb, length);
memcpy(marker_header->hdr.h_dest, lacpdu_mcast_addr, ETH_ALEN);
- /* Note: source addres is set to be the member's PERMANENT address,
+ /* Note: source address is set to be the member's PERMANENT address,
because we use it to identify loopback MARKERs in receive. */
memcpy(marker_header->hdr.h_source, slave->perm_hwaddr, ETH_ALEN);
marker_header->hdr.h_proto = PKT_TYPE_LACPDU;
@@ -1916,7 +1916,7 @@
return -1;
}
- //check that the slave has not been intialized yet.
+ //check that the slave has not been initialized yet.
if (SLAVE_AD_INFO(slave).port.slave != slave) {
// port initialization
diff --git a/drivers/net/chelsio/subr.c b/drivers/net/chelsio/subr.c
index 63ebf76..8a43c7e 100644
--- a/drivers/net/chelsio/subr.c
+++ b/drivers/net/chelsio/subr.c
@@ -556,7 +556,7 @@
#define EEPROM_MAX_POLL 4
/*
- * Read SEEPROM. A zero is written to the flag register when the addres is
+ * Read SEEPROM. A zero is written to the flag register when the address is
* written to the Control register. The hardware device will set the flag to a
* one when 4B have been transferred to the Data register.
*/
diff --git a/drivers/net/cxgb3/mc5.c b/drivers/net/cxgb3/mc5.c
index a8766fb..e13b7fe 100644
--- a/drivers/net/cxgb3/mc5.c
+++ b/drivers/net/cxgb3/mc5.c
@@ -318,7 +318,7 @@
/*
* Initialization that requires the OS and protocol layers to already
- * be intialized goes here.
+ * be initialized goes here.
*/
int t3_mc5_init(struct mc5 *mc5, unsigned int nservers, unsigned int nfilters,
unsigned int nroutes)
diff --git a/drivers/net/cxgb3/t3_hw.c b/drivers/net/cxgb3/t3_hw.c
index ec8579a..d55db6b 100644
--- a/drivers/net/cxgb3/t3_hw.c
+++ b/drivers/net/cxgb3/t3_hw.c
@@ -607,7 +607,7 @@
*
* Read a 32-bit word from a location in VPD EEPROM using the card's PCI
* VPD ROM capability. A zero is written to the flag bit when the
- * addres is written to the control register. The hardware device will
+ * address is written to the control register. The hardware device will
* set the flag to 1 when 4 bytes have been read into the data register.
*/
int t3_seeprom_read(struct adapter *adapter, u32 addr, __le32 *data)
diff --git a/drivers/net/e1000/e1000_hw.h b/drivers/net/e1000/e1000_hw.h
index f5514a0..196eeda 100644
--- a/drivers/net/e1000/e1000_hw.h
+++ b/drivers/net/e1000/e1000_hw.h
@@ -41,7 +41,7 @@
struct e1000_hw_stats;
/* Enumerated types specific to the e1000 hardware */
-/* Media Access Controlers */
+/* Media Access Controllers */
typedef enum {
e1000_undefined = 0,
e1000_82542_rev2_0,
diff --git a/drivers/net/e1000/e1000_main.c b/drivers/net/e1000/e1000_main.c
index 4ff88a6..de69c54 100644
--- a/drivers/net/e1000/e1000_main.c
+++ b/drivers/net/e1000/e1000_main.c
@@ -2233,7 +2233,7 @@
* addresses take precedence to avoid disabling unicast filtering
* when possible.
*
- * RAR 0 is used for the station MAC adddress
+ * RAR 0 is used for the station MAC address
* if there are not 14 addresses, go ahead and clear the filters
*/
i = 1;
diff --git a/drivers/net/e1000e/82571.c b/drivers/net/e1000e/82571.c
index cb6c7b1..1397da1 100644
--- a/drivers/net/e1000e/82571.c
+++ b/drivers/net/e1000e/82571.c
@@ -328,7 +328,7 @@
/*
* Ensure that the inter-port SWSM.SMBI lock bit is clear before
- * first NVM or PHY acess. This should be done for single-port
+ * first NVM or PHY access. This should be done for single-port
* devices, and for one port only on dual-port devices so that
* for those devices we can still use the SMBI lock to synchronize
* inter-port accesses to the PHY & NVM.
diff --git a/drivers/net/e1000e/ich8lan.c b/drivers/net/e1000e/ich8lan.c
index 5328a292..5bb65b7 100644
--- a/drivers/net/e1000e/ich8lan.c
+++ b/drivers/net/e1000e/ich8lan.c
@@ -321,7 +321,7 @@
}
/*
- * Reset the PHY before any acccess to it. Doing so, ensures that
+ * Reset the PHY before any access to it. Doing so, ensures that
* the PHY is in a known good state before we read/write PHY registers.
* The generic reset is sufficient here, because we haven't determined
* the PHY type yet.
diff --git a/drivers/net/e1000e/phy.c b/drivers/net/e1000e/phy.c
index a640f1c..00f89e8 100644
--- a/drivers/net/e1000e/phy.c
+++ b/drivers/net/e1000e/phy.c
@@ -2986,7 +2986,7 @@
}
/**
- * e1000_get_phy_addr_for_hv_page - Get PHY adrress based on page
+ * e1000_get_phy_addr_for_hv_page - Get PHY address based on page
* @page: page to be accessed
**/
static u32 e1000_get_phy_addr_for_hv_page(u32 page)
diff --git a/drivers/net/eepro.c b/drivers/net/eepro.c
index 4fa8d2a..eb35951 100644
--- a/drivers/net/eepro.c
+++ b/drivers/net/eepro.c
@@ -1761,7 +1761,7 @@
module_param_array(irq, int, NULL, 0);
module_param_array(mem, int, NULL, 0);
module_param(autodetect, int, 0);
-MODULE_PARM_DESC(io, "EtherExpress Pro/10 I/O base addres(es)");
+MODULE_PARM_DESC(io, "EtherExpress Pro/10 I/O base address(es)");
MODULE_PARM_DESC(irq, "EtherExpress Pro/10 IRQ number(s)");
MODULE_PARM_DESC(mem, "EtherExpress Pro/10 Rx buffer size(es) in kB (3-29)");
MODULE_PARM_DESC(autodetect, "EtherExpress Pro/10 force board(s) detection (0-1)");
diff --git a/drivers/net/irda/donauboe.h b/drivers/net/irda/donauboe.h
index 4dc39e5..77fcf44 100644
--- a/drivers/net/irda/donauboe.h
+++ b/drivers/net/irda/donauboe.h
@@ -30,7 +30,7 @@
* or the type-DO IR port.
*
* IrDA chip set list from Toshiba Computer Engineering Corp.
- * model method maker controler Version
+ * model method maker controller Version
* Portege 320CT FIR,SIR Toshiba Oboe(Triangle)
* Portege 3010CT FIR,SIR Toshiba Oboe(Sydney)
* Portege 3015CT FIR,SIR Toshiba Oboe(Sydney)
diff --git a/drivers/net/ixgbe/ixgbe_82599.c b/drivers/net/ixgbe/ixgbe_82599.c
index 8d316d9..a21f581 100644
--- a/drivers/net/ixgbe/ixgbe_82599.c
+++ b/drivers/net/ixgbe/ixgbe_82599.c
@@ -1079,7 +1079,7 @@
/*
* The defaults in the HW for RX PB 1-7 are not zero and so should be
- * intialized to zero for non DCB mode otherwise actual total RX PB
+ * initialized to zero for non DCB mode otherwise actual total RX PB
* would be bigger than programmed and filter space would run into
* the PB 0 region.
*/
@@ -1167,7 +1167,7 @@
/*
* The defaults in the HW for RX PB 1-7 are not zero and so should be
- * intialized to zero for non DCB mode otherwise actual total RX PB
+ * initialized to zero for non DCB mode otherwise actual total RX PB
* would be bigger than programmed and filter space would run into
* the PB 0 region.
*/
diff --git a/drivers/net/ll_temac_main.c b/drivers/net/ll_temac_main.c
index 183765c..f35554d 100644
--- a/drivers/net/ll_temac_main.c
+++ b/drivers/net/ll_temac_main.c
@@ -238,7 +238,7 @@
goto out;
}
/* allocate the tx and rx ring buffer descriptors. */
- /* returns a virtual addres and a physical address. */
+ /* returns a virtual address and a physical address. */
lp->tx_bd_v = dma_alloc_coherent(ndev->dev.parent,
sizeof(*lp->tx_bd_v) * TX_BD_NUM,
&lp->tx_bd_p, GFP_KERNEL);
diff --git a/drivers/net/sis900.c b/drivers/net/sis900.c
index 5818368..5976d1d 100644
--- a/drivers/net/sis900.c
+++ b/drivers/net/sis900.c
@@ -36,7 +36,7 @@
Rev 1.07.06 Nov. 7 2000 Jeff Garzik <jgarzik@pobox.com> some bug fix and cleaning
Rev 1.07.05 Nov. 6 2000 metapirat<metapirat@gmx.de> contribute media type select by ifconfig
Rev 1.07.04 Sep. 6 2000 Lei-Chun Chang added ICS1893 PHY support
- Rev 1.07.03 Aug. 24 2000 Lei-Chun Chang (lcchang@sis.com.tw) modified 630E eqaulizer workaround rule
+ Rev 1.07.03 Aug. 24 2000 Lei-Chun Chang (lcchang@sis.com.tw) modified 630E equalizer workaround rule
Rev 1.07.01 Aug. 08 2000 Ollie Lho minor update for SiS 630E and SiS 630E A1
Rev 1.07 Mar. 07 2000 Ollie Lho bug fix in Rx buffer ring
Rev 1.06.04 Feb. 11 2000 Jeff Garzik <jgarzik@pobox.com> softnet and init for kernel 2.4
diff --git a/drivers/net/tehuti.c b/drivers/net/tehuti.c
index 296000b..3397618 100644
--- a/drivers/net/tehuti.c
+++ b/drivers/net/tehuti.c
@@ -12,7 +12,7 @@
/*
* RX HW/SW interaction overview
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- * There are 2 types of RX communication channels betwean driver and NIC.
+ * There are 2 types of RX communication channels between driver and NIC.
* 1) RX Free Fifo - RXF - holds descriptors of empty buffers to accept incoming
* traffic. This Fifo is filled by SW and is readen by HW. Each descriptor holds
* info about buffer's location, size and ID. An ID field is used to identify a
@@ -821,7 +821,7 @@
}
/* use PMF to accept first MAC_MCST_NUM (15) addresses */
- /* TBD: sort addreses and write them in ascending order
+ /* TBD: sort addresses and write them in ascending order
* into RX_MAC_MCST regs. we skip this phase now and accept ALL
* multicast frames throu IMF */
/* accept the rest of addresses throu IMF */
@@ -1346,7 +1346,7 @@
/*
* TX HW/SW interaction overview
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- * There are 2 types of TX communication channels betwean driver and NIC.
+ * There are 2 types of TX communication channels between driver and NIC.
* 1) TX Free Fifo - TXF - holds ack descriptors for sent packets
* 2) TX Data Fifo - TXD - holds descriptors of full buffers.
*
diff --git a/drivers/net/tun.c b/drivers/net/tun.c
index 7599c45..b100bd5 100644
--- a/drivers/net/tun.c
+++ b/drivers/net/tun.c
@@ -1309,7 +1309,7 @@
break;
case SIOCGIFHWADDR:
- /* Get hw addres */
+ /* Get hw address */
memcpy(ifr.ifr_hwaddr.sa_data, tun->dev->dev_addr, ETH_ALEN);
ifr.ifr_hwaddr.sa_family = tun->dev->type;
if (copy_to_user(argp, &ifr, ifreq_len))
diff --git a/drivers/net/via-velocity.c b/drivers/net/via-velocity.c
index cab96ad..09cac70 100644
--- a/drivers/net/via-velocity.c
+++ b/drivers/net/via-velocity.c
@@ -898,7 +898,7 @@
set_mii_flow_control(vptr);
/*
- Check if new status is consisent with current status
+ Check if new status is consistent with current status
if (((mii_status & curr_status) & VELOCITY_AUTONEG_ENABLE) ||
(mii_status==curr_status)) {
vptr->mii_status=mii_check_media_mode(vptr->mac_regs);
diff --git a/drivers/net/vxge/vxge-traffic.h b/drivers/net/vxge/vxge-traffic.h
index 8c3103f..d48486d 100644
--- a/drivers/net/vxge/vxge-traffic.h
+++ b/drivers/net/vxge/vxge-traffic.h
@@ -1695,7 +1695,7 @@
* struct vxge_hw_device_stats - Contains HW per-device statistics,
* including hw.
* @devh: HW device handle.
- * @dma_addr: DMA addres of the %hw_info. Given to device to fill-in the stats.
+ * @dma_addr: DMA address of the %hw_info. Given to device to fill-in the stats.
* @hw_info_dmah: DMA handle used to map hw statistics onto the device memory
* space.
* @hw_info_dma_acch: One more DMA handle used subsequently to free the
diff --git a/drivers/net/wan/dscc4.c b/drivers/net/wan/dscc4.c
index 34cff6c..4578e5b 100644
--- a/drivers/net/wan/dscc4.c
+++ b/drivers/net/wan/dscc4.c
@@ -125,7 +125,7 @@
/* Module parameters */
MODULE_AUTHOR("Maintainer: Francois Romieu <romieu@cogenit.fr>");
-MODULE_DESCRIPTION("Siemens PEB20534 PCI Controler");
+MODULE_DESCRIPTION("Siemens PEB20534 PCI Controller");
MODULE_LICENSE("GPL");
module_param(debug, int, 0);
MODULE_PARM_DESC(debug,"Enable/disable extra messages");
diff --git a/drivers/net/wimax/i2400m/driver.c b/drivers/net/wimax/i2400m/driver.c
index f060332..65bc334 100644
--- a/drivers/net/wimax/i2400m/driver.c
+++ b/drivers/net/wimax/i2400m/driver.c
@@ -232,7 +232,7 @@
result);
goto error;
}
- /* Extract MAC addresss */
+ /* Extract MAC address */
ddi = (void *) skb->data;
BUILD_BUG_ON(ETH_ALEN != sizeof(ddi->mac_address));
d_printf(2, dev, "GET DEVICE INFO: mac addr %pM\n",
diff --git a/drivers/net/wimax/i2400m/i2400m.h b/drivers/net/wimax/i2400m/i2400m.h
index 17ecaa4..030cbfd 100644
--- a/drivers/net/wimax/i2400m/i2400m.h
+++ b/drivers/net/wimax/i2400m/i2400m.h
@@ -186,7 +186,7 @@
* struct i2400m_poke_table - Hardware poke table for the Intel 2400m
*
* This structure will be used to create a device specific poke table
- * to put the device in a consistant state at boot time.
+ * to put the device in a consistent state at boot time.
*
* @address: The device address to poke
*
@@ -703,7 +703,7 @@
* @I2400M_BRI_MAC_REINIT: We need to reinitialize the boot
* rom after reading the MAC address. This is quite a dirty hack,
* if you ask me -- the device requires the bootrom to be
- * intialized after reading the MAC address.
+ * initialized after reading the MAC address.
*/
enum i2400m_bri {
I2400M_BRI_SOFT = 1 << 1,
diff --git a/drivers/net/wireless/ath/ath5k/reg.h b/drivers/net/wireless/ath/ath5k/reg.h
index 7ad05d4..fd14b91 100644
--- a/drivers/net/wireless/ath/ath5k/reg.h
+++ b/drivers/net/wireless/ath/ath5k/reg.h
@@ -1064,7 +1064,7 @@
/*
* EEPROM command register
*/
-#define AR5K_EEPROM_CMD 0x6008 /* Register Addres */
+#define AR5K_EEPROM_CMD 0x6008 /* Register Address */
#define AR5K_EEPROM_CMD_READ 0x00000001 /* EEPROM read */
#define AR5K_EEPROM_CMD_WRITE 0x00000002 /* EEPROM write */
#define AR5K_EEPROM_CMD_RESET 0x00000004 /* EEPROM reset */
@@ -1084,7 +1084,7 @@
/*
* EEPROM config register
*/
-#define AR5K_EEPROM_CFG 0x6010 /* Register Addres */
+#define AR5K_EEPROM_CFG 0x6010 /* Register Address */
#define AR5K_EEPROM_CFG_SIZE 0x00000003 /* Size determination override */
#define AR5K_EEPROM_CFG_SIZE_AUTO 0
#define AR5K_EEPROM_CFG_SIZE_4KBIT 1
@@ -1126,7 +1126,7 @@
* Second station id register (Upper 16 bits of MAC address + PCU settings)
*/
#define AR5K_STA_ID1 0x8004 /* Register Address */
-#define AR5K_STA_ID1_ADDR_U16 0x0000ffff /* Upper 16 bits of MAC addres */
+#define AR5K_STA_ID1_ADDR_U16 0x0000ffff /* Upper 16 bits of MAC address */
#define AR5K_STA_ID1_AP 0x00010000 /* Set AP mode */
#define AR5K_STA_ID1_ADHOC 0x00020000 /* Set Ad-Hoc mode */
#define AR5K_STA_ID1_PWR_SV 0x00040000 /* Power save reporting */
diff --git a/drivers/net/wireless/b43/phy_g.c b/drivers/net/wireless/b43/phy_g.c
index 0dc33b6..be48281 100644
--- a/drivers/net/wireless/b43/phy_g.c
+++ b/drivers/net/wireless/b43/phy_g.c
@@ -1919,7 +1919,7 @@
b43_hf_write(dev, b43_hf_read(dev) | B43_HF_HWPCTL);
}
-/* Intialize B/G PHY power control */
+/* Initialize B/G PHY power control */
static void b43_phy_init_pctl(struct b43_wldev *dev)
{
struct ssb_bus *bus = dev->dev->bus;
diff --git a/drivers/net/wireless/b43legacy/phy.c b/drivers/net/wireless/b43legacy/phy.c
index 35033dd..28e477d 100644
--- a/drivers/net/wireless/b43legacy/phy.c
+++ b/drivers/net/wireless/b43legacy/phy.c
@@ -153,7 +153,7 @@
phy->calibrated = 1;
}
-/* intialize B PHY power control
+/* initialize B PHY power control
* as described in http://bcm-specs.sipsolutions.net/InitPowerControl
*/
static void b43legacy_phy_init_pctl(struct b43legacy_wldev *dev)
diff --git a/drivers/net/wireless/iwlwifi/iwl-agn-ict.c b/drivers/net/wireless/iwlwifi/iwl-agn-ict.c
index a5dbfea..b5cb3be 100644
--- a/drivers/net/wireless/iwlwifi/iwl-agn-ict.c
+++ b/drivers/net/wireless/iwlwifi/iwl-agn-ict.c
@@ -197,7 +197,7 @@
none:
/* re-enable interrupts here since we don't have anything to service. */
- /* only Re-enable if diabled by irq and no schedules tasklet. */
+ /* only Re-enable if disabled by irq and no schedules tasklet. */
if (test_bit(STATUS_INT_ENABLED, &priv->status) && !priv->_agn.inta)
iwl_enable_interrupts(priv);
diff --git a/drivers/net/wireless/iwlwifi/iwl-agn.c b/drivers/net/wireless/iwlwifi/iwl-agn.c
index f13a83a..36335b1 100644
--- a/drivers/net/wireless/iwlwifi/iwl-agn.c
+++ b/drivers/net/wireless/iwlwifi/iwl-agn.c
@@ -1154,7 +1154,7 @@
}
/* Re-enable all interrupts */
- /* only Re-enable if diabled by irq */
+ /* only Re-enable if disabled by irq */
if (test_bit(STATUS_INT_ENABLED, &priv->status))
iwl_enable_interrupts(priv);
@@ -1368,7 +1368,7 @@
}
/* Re-enable all interrupts */
- /* only Re-enable if diabled by irq */
+ /* only Re-enable if disabled by irq */
if (test_bit(STATUS_INT_ENABLED, &priv->status))
iwl_enable_interrupts(priv);
}
diff --git a/drivers/net/wireless/iwlwifi/iwl-legacy.c b/drivers/net/wireless/iwlwifi/iwl-legacy.c
index a08b4e5..bb1a742 100644
--- a/drivers/net/wireless/iwlwifi/iwl-legacy.c
+++ b/drivers/net/wireless/iwlwifi/iwl-legacy.c
@@ -619,7 +619,7 @@
none:
/* re-enable interrupts here since we don't have anything to service. */
- /* only Re-enable if diabled by irq */
+ /* only Re-enable if disabled by irq */
if (test_bit(STATUS_INT_ENABLED, &priv->status))
iwl_enable_interrupts(priv);
spin_unlock_irqrestore(&priv->lock, flags);
diff --git a/drivers/net/wireless/iwlwifi/iwl-sta.c b/drivers/net/wireless/iwlwifi/iwl-sta.c
index 4776323..49493d1 100644
--- a/drivers/net/wireless/iwlwifi/iwl-sta.c
+++ b/drivers/net/wireless/iwlwifi/iwl-sta.c
@@ -107,7 +107,7 @@
/*
* XXX: The MAC address in the command buffer is often changed from
* the original sent to the device. That is, the MAC address
- * written to the command buffer often is not the same MAC adress
+ * written to the command buffer often is not the same MAC address
* read from the command buffer when the command returns. This
* issue has not yet been resolved and this debugging is left to
* observe the problem.
diff --git a/drivers/net/wireless/prism54/islpci_dev.c b/drivers/net/wireless/prism54/islpci_dev.c
index 2c8cc95..ec2c75d 100644
--- a/drivers/net/wireless/prism54/islpci_dev.c
+++ b/drivers/net/wireless/prism54/islpci_dev.c
@@ -630,7 +630,7 @@
printk(KERN_DEBUG "islpci_alloc_memory\n");
#endif
- /* remap the PCI device base address to accessable */
+ /* remap the PCI device base address to accessible */
if (!(priv->device_base =
ioremap(pci_resource_start(priv->pdev, 0),
ISL38XX_PCI_MEM_SIZE))) {
@@ -709,7 +709,7 @@
PCI_DMA_FROMDEVICE);
if (!priv->pci_map_rx_address[counter]) {
/* error mapping the buffer to device
- accessable memory address */
+ accessible memory address */
printk(KERN_ERR "failed to map skb DMA'able\n");
goto out_free;
}
@@ -773,7 +773,7 @@
priv->data_low_rx[counter] = NULL;
}
- /* Free the acces control list and the WPA list */
+ /* Free the access control list and the WPA list */
prism54_acl_clean(&priv->acl);
prism54_wpa_bss_ie_clean(priv);
mgt_clean(priv);
diff --git a/drivers/net/wireless/prism54/islpci_eth.c b/drivers/net/wireless/prism54/islpci_eth.c
index 2fc52bc..d44f8e2 100644
--- a/drivers/net/wireless/prism54/islpci_eth.c
+++ b/drivers/net/wireless/prism54/islpci_eth.c
@@ -450,7 +450,7 @@
MAX_FRAGMENT_SIZE_RX + 2,
PCI_DMA_FROMDEVICE);
if (unlikely(!priv->pci_map_rx_address[index])) {
- /* error mapping the buffer to device accessable memory address */
+ /* error mapping the buffer to device accessible memory address */
DEBUG(SHOW_ERROR_MESSAGES,
"Error mapping DMA address\n");
diff --git a/drivers/net/wireless/rt2x00/rt2x00mac.c b/drivers/net/wireless/rt2x00/rt2x00mac.c
index 658542d..f3da051 100644
--- a/drivers/net/wireless/rt2x00/rt2x00mac.c
+++ b/drivers/net/wireless/rt2x00/rt2x00mac.c
@@ -273,7 +273,7 @@
intf->beacon = entry;
/*
- * The MAC adddress must be configured after the device
+ * The MAC address must be configured after the device
* has been initialized. Otherwise the device can reset
* the MAC registers.
* The BSSID address must only be configured in AP mode,
diff --git a/drivers/net/wireless/wl1251/acx.h b/drivers/net/wireless/wl1251/acx.h
index e54b21a..efcc3aa 100644
--- a/drivers/net/wireless/wl1251/acx.h
+++ b/drivers/net/wireless/wl1251/acx.h
@@ -1272,10 +1272,10 @@
/* OBSOLETE */
#define WL1251_ACX_INTR_WAKE_ON_HOST BIT(6)
-/* Trace meassge on MBOX #A */
+/* Trace message on MBOX #A */
#define WL1251_ACX_INTR_TRACE_A BIT(7)
-/* Trace meassge on MBOX #B */
+/* Trace message on MBOX #B */
#define WL1251_ACX_INTR_TRACE_B BIT(8)
/* Command processing completion */
diff --git a/drivers/net/wireless/wl1251/wl1251.h b/drivers/net/wireless/wl1251/wl1251.h
index 13fbeec..c0ce2c8 100644
--- a/drivers/net/wireless/wl1251/wl1251.h
+++ b/drivers/net/wireless/wl1251/wl1251.h
@@ -419,7 +419,7 @@
#define WL1251_FW_NAME "wl1251-fw.bin"
#define WL1251_NVS_NAME "wl1251-nvs.bin"
-#define WL1251_POWER_ON_SLEEP 10 /* in miliseconds */
+#define WL1251_POWER_ON_SLEEP 10 /* in milliseconds */
#define WL1251_PART_DOWN_MEM_START 0x0
#define WL1251_PART_DOWN_MEM_SIZE 0x16800
diff --git a/drivers/net/wireless/wl12xx/acx.h b/drivers/net/wireless/wl12xx/acx.h
index 9cbc3f4..7bd8e4d 100644
--- a/drivers/net/wireless/wl12xx/acx.h
+++ b/drivers/net/wireless/wl12xx/acx.h
@@ -47,9 +47,9 @@
#define WL1271_ACX_INTR_HW_AVAILABLE BIT(5)
/* The MISC bit is used for aggregation of RX, TxComplete and TX rate update */
#define WL1271_ACX_INTR_DATA BIT(6)
-/* Trace meassge on MBOX #A */
+/* Trace message on MBOX #A */
#define WL1271_ACX_INTR_TRACE_A BIT(7)
-/* Trace meassge on MBOX #B */
+/* Trace message on MBOX #B */
#define WL1271_ACX_INTR_TRACE_B BIT(8)
#define WL1271_ACX_INTR_ALL 0xFFFFFFFF
diff --git a/drivers/net/wireless/wl12xx/wl12xx.h b/drivers/net/wireless/wl12xx/wl12xx.h
index ce3d31f..9050dd9 100644
--- a/drivers/net/wireless/wl12xx/wl12xx.h
+++ b/drivers/net/wireless/wl12xx/wl12xx.h
@@ -416,8 +416,8 @@
/* WL1271 needs a 200ms sleep after power on, and a 20ms sleep before power
on in case is has been shut down shortly before */
-#define WL1271_PRE_POWER_ON_SLEEP 20 /* in miliseconds */
-#define WL1271_POWER_ON_SLEEP 200 /* in miliseconds */
+#define WL1271_PRE_POWER_ON_SLEEP 20 /* in milliseconds */
+#define WL1271_POWER_ON_SLEEP 200 /* in milliseconds */
/* Macros to handle wl1271.sta_rate_set */
#define HW_BG_RATES_MASK 0xffff
diff --git a/drivers/net/wireless/wl3501_cs.c b/drivers/net/wireless/wl3501_cs.c
index ee82df6..3e5befe4 100644
--- a/drivers/net/wireless/wl3501_cs.c
+++ b/drivers/net/wireless/wl3501_cs.c
@@ -192,7 +192,7 @@
}
/*
- * Get Ethernet MAC addresss.
+ * Get Ethernet MAC address.
*
* WARNING: We switch to FPAGE0 and switc back again.
* Making sure there is no other WL function beening called by ISR.
diff --git a/drivers/nfc/Kconfig b/drivers/nfc/Kconfig
new file mode 100644
index 0000000..ffedfd4
--- /dev/null
+++ b/drivers/nfc/Kconfig
@@ -0,0 +1,30 @@
+#
+# Near Field Communication (NFC) devices
+#
+
+menuconfig NFC_DEVICES
+ bool "NFC devices"
+ default n
+ ---help---
+ You'll have to say Y if your computer contains an NFC device that
+ you want to use under Linux.
+
+ You can say N here if you don't have any Near Field Communication
+ devices connected to your computer.
+
+if NFC_DEVICES
+
+config PN544_NFC
+ tristate "PN544 NFC driver"
+ depends on I2C
+ select CRC_CCITT
+ default n
+ ---help---
+ Say yes if you want PN544 Near Field Communication driver.
+ This is for i2c connected version. If unsure, say N here.
+
+ To compile this driver as a module, choose m here. The module will
+ be called pn544.
+
+
+endif # NFC_DEVICES
diff --git a/drivers/nfc/Makefile b/drivers/nfc/Makefile
new file mode 100644
index 0000000..a4efb16
--- /dev/null
+++ b/drivers/nfc/Makefile
@@ -0,0 +1,5 @@
+#
+# Makefile for nfc devices
+#
+
+obj-$(CONFIG_PN544_NFC) += pn544.o
diff --git a/drivers/nfc/pn544.c b/drivers/nfc/pn544.c
new file mode 100644
index 0000000..401c44b
--- /dev/null
+++ b/drivers/nfc/pn544.c
@@ -0,0 +1,891 @@
+/*
+ * Driver for the PN544 NFC chip.
+ *
+ * Copyright (C) Nokia Corporation
+ *
+ * Author: Jari Vanhala <ext-jari.vanhala@nokia.com>
+ * Contact: Matti Aaltonen <matti.j.aaltonen@nokia.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * 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
+ */
+
+#include <linux/completion.h>
+#include <linux/crc-ccitt.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/kernel.h>
+#include <linux/miscdevice.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/nfc/pn544.h>
+#include <linux/poll.h>
+#include <linux/regulator/consumer.h>
+#include <linux/serial_core.h> /* for TCGETS */
+#include <linux/slab.h>
+
+#define DRIVER_CARD "PN544 NFC"
+#define DRIVER_DESC "NFC driver for PN544"
+
+static struct i2c_device_id pn544_id_table[] = {
+ { PN544_DRIVER_NAME, 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, pn544_id_table);
+
+#define HCI_MODE 0
+#define FW_MODE 1
+
+enum pn544_state {
+ PN544_ST_COLD,
+ PN544_ST_FW_READY,
+ PN544_ST_READY,
+};
+
+enum pn544_irq {
+ PN544_NONE,
+ PN544_INT,
+};
+
+struct pn544_info {
+ struct miscdevice miscdev;
+ struct i2c_client *i2c_dev;
+ struct regulator_bulk_data regs[2];
+
+ enum pn544_state state;
+ wait_queue_head_t read_wait;
+ loff_t read_offset;
+ enum pn544_irq read_irq;
+ struct mutex read_mutex; /* Serialize read_irq access */
+ struct mutex mutex; /* Serialize info struct access */
+ u8 *buf;
+ unsigned int buflen;
+};
+
+static const char reg_vdd_io[] = "Vdd_IO";
+static const char reg_vbat[] = "VBat";
+
+/* sysfs interface */
+static ssize_t pn544_test(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct pn544_info *info = dev_get_drvdata(dev);
+ struct i2c_client *client = info->i2c_dev;
+ struct pn544_nfc_platform_data *pdata = client->dev.platform_data;
+
+ return snprintf(buf, PAGE_SIZE, "%d\n", pdata->test());
+}
+
+static int pn544_enable(struct pn544_info *info, int mode)
+{
+ struct pn544_nfc_platform_data *pdata;
+ struct i2c_client *client = info->i2c_dev;
+
+ int r;
+
+ r = regulator_bulk_enable(ARRAY_SIZE(info->regs), info->regs);
+ if (r < 0)
+ return r;
+
+ pdata = client->dev.platform_data;
+ info->read_irq = PN544_NONE;
+ if (pdata->enable)
+ pdata->enable(mode);
+
+ if (mode) {
+ info->state = PN544_ST_FW_READY;
+ dev_dbg(&client->dev, "now in FW-mode\n");
+ } else {
+ info->state = PN544_ST_READY;
+ dev_dbg(&client->dev, "now in HCI-mode\n");
+ }
+
+ usleep_range(10000, 15000);
+
+ return 0;
+}
+
+static void pn544_disable(struct pn544_info *info)
+{
+ struct pn544_nfc_platform_data *pdata;
+ struct i2c_client *client = info->i2c_dev;
+
+ pdata = client->dev.platform_data;
+ if (pdata->disable)
+ pdata->disable();
+
+ info->state = PN544_ST_COLD;
+
+ dev_dbg(&client->dev, "Now in OFF-mode\n");
+
+ msleep(PN544_RESETVEN_TIME);
+
+ info->read_irq = PN544_NONE;
+ regulator_bulk_disable(ARRAY_SIZE(info->regs), info->regs);
+}
+
+static int check_crc(u8 *buf, int buflen)
+{
+ u8 len;
+ u16 crc;
+
+ len = buf[0] + 1;
+ if (len < 4 || len != buflen || len > PN544_MSG_MAX_SIZE) {
+ pr_err(PN544_DRIVER_NAME
+ ": CRC; corrupt packet len %u (%d)\n", len, buflen);
+ print_hex_dump(KERN_DEBUG, "crc: ", DUMP_PREFIX_NONE,
+ 16, 2, buf, buflen, false);
+ return -EPERM;
+ }
+ crc = crc_ccitt(0xffff, buf, len - 2);
+ crc = ~crc;
+
+ if (buf[len-2] != (crc & 0xff) || buf[len-1] != (crc >> 8)) {
+ pr_err(PN544_DRIVER_NAME ": CRC error 0x%x != 0x%x 0x%x\n",
+ crc, buf[len-1], buf[len-2]);
+
+ print_hex_dump(KERN_DEBUG, "crc: ", DUMP_PREFIX_NONE,
+ 16, 2, buf, buflen, false);
+ return -EPERM;
+ }
+ return 0;
+}
+
+static int pn544_i2c_write(struct i2c_client *client, u8 *buf, int len)
+{
+ int r;
+
+ if (len < 4 || len != (buf[0] + 1)) {
+ dev_err(&client->dev, "%s: Illegal message length: %d\n",
+ __func__, len);
+ return -EINVAL;
+ }
+
+ if (check_crc(buf, len))
+ return -EINVAL;
+
+ usleep_range(3000, 6000);
+
+ r = i2c_master_send(client, buf, len);
+ dev_dbg(&client->dev, "send: %d\n", r);
+
+ if (r == -EREMOTEIO) { /* Retry, chip was in standby */
+ usleep_range(6000, 10000);
+ r = i2c_master_send(client, buf, len);
+ dev_dbg(&client->dev, "send2: %d\n", r);
+ }
+
+ if (r != len)
+ return -EREMOTEIO;
+
+ return r;
+}
+
+static int pn544_i2c_read(struct i2c_client *client, u8 *buf, int buflen)
+{
+ int r;
+ u8 len;
+
+ /*
+ * You could read a packet in one go, but then you'd need to read
+ * max size and rest would be 0xff fill, so we do split reads.
+ */
+ r = i2c_master_recv(client, &len, 1);
+ dev_dbg(&client->dev, "recv1: %d\n", r);
+
+ if (r != 1)
+ return -EREMOTEIO;
+
+ if (len < PN544_LLC_HCI_OVERHEAD)
+ len = PN544_LLC_HCI_OVERHEAD;
+ else if (len > (PN544_MSG_MAX_SIZE - 1))
+ len = PN544_MSG_MAX_SIZE - 1;
+
+ if (1 + len > buflen) /* len+(data+crc16) */
+ return -EMSGSIZE;
+
+ buf[0] = len;
+
+ r = i2c_master_recv(client, buf + 1, len);
+ dev_dbg(&client->dev, "recv2: %d\n", r);
+
+ if (r != len)
+ return -EREMOTEIO;
+
+ usleep_range(3000, 6000);
+
+ return r + 1;
+}
+
+static int pn544_fw_write(struct i2c_client *client, u8 *buf, int len)
+{
+ int r;
+
+ dev_dbg(&client->dev, "%s\n", __func__);
+
+ if (len < PN544_FW_HEADER_SIZE ||
+ (PN544_FW_HEADER_SIZE + (buf[1] << 8) + buf[2]) != len)
+ return -EINVAL;
+
+ r = i2c_master_send(client, buf, len);
+ dev_dbg(&client->dev, "fw send: %d\n", r);
+
+ if (r == -EREMOTEIO) { /* Retry, chip was in standby */
+ usleep_range(6000, 10000);
+ r = i2c_master_send(client, buf, len);
+ dev_dbg(&client->dev, "fw send2: %d\n", r);
+ }
+
+ if (r != len)
+ return -EREMOTEIO;
+
+ return r;
+}
+
+static int pn544_fw_read(struct i2c_client *client, u8 *buf, int buflen)
+{
+ int r, len;
+
+ if (buflen < PN544_FW_HEADER_SIZE)
+ return -EINVAL;
+
+ r = i2c_master_recv(client, buf, PN544_FW_HEADER_SIZE);
+ dev_dbg(&client->dev, "FW recv1: %d\n", r);
+
+ if (r < 0)
+ return r;
+
+ if (r < PN544_FW_HEADER_SIZE)
+ return -EINVAL;
+
+ len = (buf[1] << 8) + buf[2];
+ if (len == 0) /* just header, no additional data */
+ return r;
+
+ if (len > buflen - PN544_FW_HEADER_SIZE)
+ return -EMSGSIZE;
+
+ r = i2c_master_recv(client, buf + PN544_FW_HEADER_SIZE, len);
+ dev_dbg(&client->dev, "fw recv2: %d\n", r);
+
+ if (r != len)
+ return -EINVAL;
+
+ return r + PN544_FW_HEADER_SIZE;
+}
+
+static irqreturn_t pn544_irq_thread_fn(int irq, void *dev_id)
+{
+ struct pn544_info *info = dev_id;
+ struct i2c_client *client = info->i2c_dev;
+
+ BUG_ON(!info);
+ BUG_ON(irq != info->i2c_dev->irq);
+
+ dev_dbg(&client->dev, "IRQ\n");
+
+ mutex_lock(&info->read_mutex);
+ info->read_irq = PN544_INT;
+ mutex_unlock(&info->read_mutex);
+
+ wake_up_interruptible(&info->read_wait);
+
+ return IRQ_HANDLED;
+}
+
+static enum pn544_irq pn544_irq_state(struct pn544_info *info)
+{
+ enum pn544_irq irq;
+
+ mutex_lock(&info->read_mutex);
+ irq = info->read_irq;
+ mutex_unlock(&info->read_mutex);
+ /*
+ * XXX: should we check GPIO-line status directly?
+ * return pdata->irq_status() ? PN544_INT : PN544_NONE;
+ */
+
+ return irq;
+}
+
+static ssize_t pn544_read(struct file *file, char __user *buf,
+ size_t count, loff_t *offset)
+{
+ struct pn544_info *info = container_of(file->private_data,
+ struct pn544_info, miscdev);
+ struct i2c_client *client = info->i2c_dev;
+ enum pn544_irq irq;
+ size_t len;
+ int r = 0;
+
+ dev_dbg(&client->dev, "%s: info: %p, count: %zu\n", __func__,
+ info, count);
+
+ mutex_lock(&info->mutex);
+
+ if (info->state == PN544_ST_COLD) {
+ r = -ENODEV;
+ goto out;
+ }
+
+ irq = pn544_irq_state(info);
+ if (irq == PN544_NONE) {
+ if (file->f_flags & O_NONBLOCK) {
+ r = -EAGAIN;
+ goto out;
+ }
+
+ if (wait_event_interruptible(info->read_wait,
+ (info->read_irq == PN544_INT))) {
+ r = -ERESTARTSYS;
+ goto out;
+ }
+ }
+
+ if (info->state == PN544_ST_FW_READY) {
+ len = min(count, info->buflen);
+
+ mutex_lock(&info->read_mutex);
+ r = pn544_fw_read(info->i2c_dev, info->buf, len);
+ info->read_irq = PN544_NONE;
+ mutex_unlock(&info->read_mutex);
+
+ if (r < 0) {
+ dev_err(&info->i2c_dev->dev, "FW read failed: %d\n", r);
+ goto out;
+ }
+
+ print_hex_dump(KERN_DEBUG, "FW read: ", DUMP_PREFIX_NONE,
+ 16, 2, info->buf, r, false);
+
+ *offset += r;
+ if (copy_to_user(buf, info->buf, r)) {
+ r = -EFAULT;
+ goto out;
+ }
+ } else {
+ len = min(count, info->buflen);
+
+ mutex_lock(&info->read_mutex);
+ r = pn544_i2c_read(info->i2c_dev, info->buf, len);
+ info->read_irq = PN544_NONE;
+ mutex_unlock(&info->read_mutex);
+
+ if (r < 0) {
+ dev_err(&info->i2c_dev->dev, "read failed (%d)\n", r);
+ goto out;
+ }
+ print_hex_dump(KERN_DEBUG, "read: ", DUMP_PREFIX_NONE,
+ 16, 2, info->buf, r, false);
+
+ *offset += r;
+ if (copy_to_user(buf, info->buf, r)) {
+ r = -EFAULT;
+ goto out;
+ }
+ }
+
+out:
+ mutex_unlock(&info->mutex);
+
+ return r;
+}
+
+static unsigned int pn544_poll(struct file *file, poll_table *wait)
+{
+ struct pn544_info *info = container_of(file->private_data,
+ struct pn544_info, miscdev);
+ struct i2c_client *client = info->i2c_dev;
+ int r = 0;
+
+ dev_dbg(&client->dev, "%s: info: %p\n", __func__, info);
+
+ mutex_lock(&info->mutex);
+
+ if (info->state == PN544_ST_COLD) {
+ r = -ENODEV;
+ goto out;
+ }
+
+ poll_wait(file, &info->read_wait, wait);
+
+ if (pn544_irq_state(info) == PN544_INT) {
+ r = POLLIN | POLLRDNORM;
+ goto out;
+ }
+out:
+ mutex_unlock(&info->mutex);
+
+ return r;
+}
+
+static ssize_t pn544_write(struct file *file, const char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ struct pn544_info *info = container_of(file->private_data,
+ struct pn544_info, miscdev);
+ struct i2c_client *client = info->i2c_dev;
+ ssize_t len;
+ int r;
+
+ dev_dbg(&client->dev, "%s: info: %p, count %zu\n", __func__,
+ info, count);
+
+ mutex_lock(&info->mutex);
+
+ if (info->state == PN544_ST_COLD) {
+ r = -ENODEV;
+ goto out;
+ }
+
+ /*
+ * XXX: should we detect rset-writes and clean possible
+ * read_irq state
+ */
+ if (info->state == PN544_ST_FW_READY) {
+ size_t fw_len;
+
+ if (count < PN544_FW_HEADER_SIZE) {
+ r = -EINVAL;
+ goto out;
+ }
+
+ len = min(count, info->buflen);
+ if (copy_from_user(info->buf, buf, len)) {
+ r = -EFAULT;
+ goto out;
+ }
+
+ print_hex_dump(KERN_DEBUG, "FW write: ", DUMP_PREFIX_NONE,
+ 16, 2, info->buf, len, false);
+
+ fw_len = PN544_FW_HEADER_SIZE + (info->buf[1] << 8) +
+ info->buf[2];
+
+ if (len > fw_len) /* 1 msg at a time */
+ len = fw_len;
+
+ r = pn544_fw_write(info->i2c_dev, info->buf, len);
+ } else {
+ if (count < PN544_LLC_MIN_SIZE) {
+ r = -EINVAL;
+ goto out;
+ }
+
+ len = min(count, info->buflen);
+ if (copy_from_user(info->buf, buf, len)) {
+ r = -EFAULT;
+ goto out;
+ }
+
+ print_hex_dump(KERN_DEBUG, "write: ", DUMP_PREFIX_NONE,
+ 16, 2, info->buf, len, false);
+
+ if (len > (info->buf[0] + 1)) /* 1 msg at a time */
+ len = info->buf[0] + 1;
+
+ r = pn544_i2c_write(info->i2c_dev, info->buf, len);
+ }
+out:
+ mutex_unlock(&info->mutex);
+
+ return r;
+
+}
+
+static long pn544_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
+{
+ struct pn544_info *info = container_of(file->private_data,
+ struct pn544_info, miscdev);
+ struct i2c_client *client = info->i2c_dev;
+ struct pn544_nfc_platform_data *pdata;
+ unsigned int val;
+ int r = 0;
+
+ dev_dbg(&client->dev, "%s: info: %p, cmd: 0x%x\n", __func__, info, cmd);
+
+ mutex_lock(&info->mutex);
+
+ if (info->state == PN544_ST_COLD) {
+ r = -ENODEV;
+ goto out;
+ }
+
+ pdata = info->i2c_dev->dev.platform_data;
+ switch (cmd) {
+ case PN544_GET_FW_MODE:
+ dev_dbg(&client->dev, "%s: PN544_GET_FW_MODE\n", __func__);
+
+ val = (info->state == PN544_ST_FW_READY);
+ if (copy_to_user((void __user *)arg, &val, sizeof(val))) {
+ r = -EFAULT;
+ goto out;
+ }
+
+ break;
+
+ case PN544_SET_FW_MODE:
+ dev_dbg(&client->dev, "%s: PN544_SET_FW_MODE\n", __func__);
+
+ if (copy_from_user(&val, (void __user *)arg, sizeof(val))) {
+ r = -EFAULT;
+ goto out;
+ }
+
+ if (val) {
+ if (info->state == PN544_ST_FW_READY)
+ break;
+
+ pn544_disable(info);
+ r = pn544_enable(info, FW_MODE);
+ if (r < 0)
+ goto out;
+ } else {
+ if (info->state == PN544_ST_READY)
+ break;
+ pn544_disable(info);
+ r = pn544_enable(info, HCI_MODE);
+ if (r < 0)
+ goto out;
+ }
+ file->f_pos = info->read_offset;
+ break;
+
+ case TCGETS:
+ dev_dbg(&client->dev, "%s: TCGETS\n", __func__);
+
+ r = -ENOIOCTLCMD;
+ break;
+
+ default:
+ dev_err(&client->dev, "Unknown ioctl 0x%x\n", cmd);
+ r = -ENOIOCTLCMD;
+ break;
+ }
+
+out:
+ mutex_unlock(&info->mutex);
+
+ return r;
+}
+
+static int pn544_open(struct inode *inode, struct file *file)
+{
+ struct pn544_info *info = container_of(file->private_data,
+ struct pn544_info, miscdev);
+ struct i2c_client *client = info->i2c_dev;
+ int r = 0;
+
+ dev_dbg(&client->dev, "%s: info: %p, client %p\n", __func__,
+ info, info->i2c_dev);
+
+ mutex_lock(&info->mutex);
+
+ /*
+ * Only 1 at a time.
+ * XXX: maybe user (counter) would work better
+ */
+ if (info->state != PN544_ST_COLD) {
+ r = -EBUSY;
+ goto out;
+ }
+
+ file->f_pos = info->read_offset;
+ r = pn544_enable(info, HCI_MODE);
+
+out:
+ mutex_unlock(&info->mutex);
+ return r;
+}
+
+static int pn544_close(struct inode *inode, struct file *file)
+{
+ struct pn544_info *info = container_of(file->private_data,
+ struct pn544_info, miscdev);
+ struct i2c_client *client = info->i2c_dev;
+
+ dev_dbg(&client->dev, "%s: info: %p, client %p\n",
+ __func__, info, info->i2c_dev);
+
+ mutex_lock(&info->mutex);
+ pn544_disable(info);
+ mutex_unlock(&info->mutex);
+
+ return 0;
+}
+
+static const struct file_operations pn544_fops = {
+ .owner = THIS_MODULE,
+ .llseek = no_llseek,
+ .read = pn544_read,
+ .write = pn544_write,
+ .poll = pn544_poll,
+ .open = pn544_open,
+ .release = pn544_close,
+ .unlocked_ioctl = pn544_ioctl,
+};
+
+#ifdef CONFIG_PM
+static int pn544_suspend(struct device *dev)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct pn544_info *info;
+ int r = 0;
+
+ dev_info(&client->dev, "***\n%s: client %p\n***\n", __func__, client);
+
+ info = i2c_get_clientdata(client);
+ dev_info(&client->dev, "%s: info: %p, client %p\n", __func__,
+ info, client);
+
+ mutex_lock(&info->mutex);
+
+ switch (info->state) {
+ case PN544_ST_FW_READY:
+ /* Do not suspend while upgrading FW, please! */
+ r = -EPERM;
+ break;
+
+ case PN544_ST_READY:
+ /*
+ * CHECK: Device should be in standby-mode. No way to check?
+ * Allowing low power mode for the regulator is potentially
+ * dangerous if pn544 does not go to suspension.
+ */
+ break;
+
+ case PN544_ST_COLD:
+ break;
+ };
+
+ mutex_unlock(&info->mutex);
+ return r;
+}
+
+static int pn544_resume(struct device *dev)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct pn544_info *info = i2c_get_clientdata(client);
+ int r = 0;
+
+ dev_dbg(&client->dev, "%s: info: %p, client %p\n", __func__,
+ info, client);
+
+ mutex_lock(&info->mutex);
+
+ switch (info->state) {
+ case PN544_ST_READY:
+ /*
+ * CHECK: If regulator low power mode is allowed in
+ * pn544_suspend, we should go back to normal mode
+ * here.
+ */
+ break;
+
+ case PN544_ST_COLD:
+ break;
+
+ case PN544_ST_FW_READY:
+ break;
+ };
+
+ mutex_unlock(&info->mutex);
+
+ return r;
+}
+
+static SIMPLE_DEV_PM_OPS(pn544_pm_ops, pn544_suspend, pn544_resume);
+#endif
+
+static struct device_attribute pn544_attr =
+ __ATTR(nfc_test, S_IRUGO, pn544_test, NULL);
+
+static int __devinit pn544_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct pn544_info *info;
+ struct pn544_nfc_platform_data *pdata;
+ int r = 0;
+
+ dev_dbg(&client->dev, "%s\n", __func__);
+ dev_dbg(&client->dev, "IRQ: %d\n", client->irq);
+
+ /* private data allocation */
+ info = kzalloc(sizeof(struct pn544_info), GFP_KERNEL);
+ if (!info) {
+ dev_err(&client->dev,
+ "Cannot allocate memory for pn544_info.\n");
+ r = -ENOMEM;
+ goto err_info_alloc;
+ }
+
+ info->buflen = max(PN544_MSG_MAX_SIZE, PN544_MAX_I2C_TRANSFER);
+ info->buf = kzalloc(info->buflen, GFP_KERNEL);
+ if (!info->buf) {
+ dev_err(&client->dev,
+ "Cannot allocate memory for pn544_info->buf.\n");
+ r = -ENOMEM;
+ goto err_buf_alloc;
+ }
+
+ info->regs[0].supply = reg_vdd_io;
+ info->regs[1].supply = reg_vbat;
+ r = regulator_bulk_get(&client->dev, ARRAY_SIZE(info->regs),
+ info->regs);
+ if (r < 0)
+ goto err_kmalloc;
+
+ info->i2c_dev = client;
+ info->state = PN544_ST_COLD;
+ info->read_irq = PN544_NONE;
+ mutex_init(&info->read_mutex);
+ mutex_init(&info->mutex);
+ init_waitqueue_head(&info->read_wait);
+ i2c_set_clientdata(client, info);
+ pdata = client->dev.platform_data;
+ if (!pdata) {
+ dev_err(&client->dev, "No platform data\n");
+ r = -EINVAL;
+ goto err_reg;
+ }
+
+ if (!pdata->request_resources) {
+ dev_err(&client->dev, "request_resources() missing\n");
+ r = -EINVAL;
+ goto err_reg;
+ }
+
+ r = pdata->request_resources(client);
+ if (r) {
+ dev_err(&client->dev, "Cannot get platform resources\n");
+ goto err_reg;
+ }
+
+ r = request_threaded_irq(client->irq, NULL, pn544_irq_thread_fn,
+ IRQF_TRIGGER_RISING, PN544_DRIVER_NAME,
+ info);
+ if (r < 0) {
+ dev_err(&client->dev, "Unable to register IRQ handler\n");
+ goto err_res;
+ }
+
+ /* If we don't have the test we don't need the sysfs file */
+ if (pdata->test) {
+ r = device_create_file(&client->dev, &pn544_attr);
+ if (r) {
+ dev_err(&client->dev,
+ "sysfs registration failed, error %d\n", r);
+ goto err_irq;
+ }
+ }
+
+ info->miscdev.minor = MISC_DYNAMIC_MINOR;
+ info->miscdev.name = PN544_DRIVER_NAME;
+ info->miscdev.fops = &pn544_fops;
+ info->miscdev.parent = &client->dev;
+ r = misc_register(&info->miscdev);
+ if (r < 0) {
+ dev_err(&client->dev, "Device registration failed\n");
+ goto err_sysfs;
+ }
+
+ dev_dbg(&client->dev, "%s: info: %p, pdata %p, client %p\n",
+ __func__, info, pdata, client);
+
+ return 0;
+
+err_sysfs:
+ if (pdata->test)
+ device_remove_file(&client->dev, &pn544_attr);
+err_irq:
+ free_irq(client->irq, info);
+err_res:
+ if (pdata->free_resources)
+ pdata->free_resources();
+err_reg:
+ regulator_bulk_free(ARRAY_SIZE(info->regs), info->regs);
+err_kmalloc:
+ kfree(info->buf);
+err_buf_alloc:
+ kfree(info);
+err_info_alloc:
+ return r;
+}
+
+static __devexit int pn544_remove(struct i2c_client *client)
+{
+ struct pn544_info *info = i2c_get_clientdata(client);
+ struct pn544_nfc_platform_data *pdata = client->dev.platform_data;
+
+ dev_dbg(&client->dev, "%s\n", __func__);
+
+ misc_deregister(&info->miscdev);
+ if (pdata->test)
+ device_remove_file(&client->dev, &pn544_attr);
+
+ if (info->state != PN544_ST_COLD) {
+ if (pdata->disable)
+ pdata->disable();
+
+ info->read_irq = PN544_NONE;
+ }
+
+ free_irq(client->irq, info);
+ if (pdata->free_resources)
+ pdata->free_resources();
+
+ regulator_bulk_free(ARRAY_SIZE(info->regs), info->regs);
+ kfree(info->buf);
+ kfree(info);
+
+ return 0;
+}
+
+static struct i2c_driver pn544_driver = {
+ .driver = {
+ .name = PN544_DRIVER_NAME,
+#ifdef CONFIG_PM
+ .pm = &pn544_pm_ops,
+#endif
+ },
+ .probe = pn544_probe,
+ .id_table = pn544_id_table,
+ .remove = __devexit_p(pn544_remove),
+};
+
+static int __init pn544_init(void)
+{
+ int r;
+
+ pr_debug(DRIVER_DESC ": %s\n", __func__);
+
+ r = i2c_add_driver(&pn544_driver);
+ if (r) {
+ pr_err(PN544_DRIVER_NAME ": driver registration failed\n");
+ return r;
+ }
+
+ return 0;
+}
+
+static void __exit pn544_exit(void)
+{
+ i2c_del_driver(&pn544_driver);
+ pr_info(DRIVER_DESC ", Exiting.\n");
+}
+
+module_init(pn544_init);
+module_exit(pn544_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION(DRIVER_DESC);
diff --git a/drivers/pci/Kconfig b/drivers/pci/Kconfig
index 5b1630e..a9523fd 100644
--- a/drivers/pci/Kconfig
+++ b/drivers/pci/Kconfig
@@ -45,6 +45,7 @@
depends on PCI && X86 && XEN
select HOTPLUG
select PCI_XEN
+ select XEN_XENBUS_FRONTEND
default y
help
The PCI device frontend driver allows the kernel to import arbitrary
diff --git a/drivers/pcmcia/m32r_cfc.h b/drivers/pcmcia/m32r_cfc.h
index 8146e3b..f558e1a 100644
--- a/drivers/pcmcia/m32r_cfc.h
+++ b/drivers/pcmcia/m32r_cfc.h
@@ -9,7 +9,7 @@
#endif
/*
- * M32R PC Card Controler
+ * M32R PC Card Controller
*/
#define M32R_PCC0_BASE 0x00ef7000
#define M32R_PCC1_BASE 0x00ef7020
diff --git a/drivers/pcmcia/m32r_pcc.h b/drivers/pcmcia/m32r_pcc.h
index e4fffe4..f95c585 100644
--- a/drivers/pcmcia/m32r_pcc.h
+++ b/drivers/pcmcia/m32r_pcc.h
@@ -5,7 +5,7 @@
#define M32R_MAX_PCC 2
/*
- * M32R PC Card Controler
+ * M32R PC Card Controller
*/
#define M32R_PCC0_BASE 0x00ef7000
#define M32R_PCC1_BASE 0x00ef7020
diff --git a/drivers/pcmcia/m8xx_pcmcia.c b/drivers/pcmcia/m8xx_pcmcia.c
index 99d4f23..0db4827 100644
--- a/drivers/pcmcia/m8xx_pcmcia.c
+++ b/drivers/pcmcia/m8xx_pcmcia.c
@@ -1198,7 +1198,7 @@
out_be32(M8XX_PGCRX(1),
M8XX_PGCRX_CXOE | (mk_int_int_mask(hwirq) << 16));
- /* intialize the fixed memory windows */
+ /* initialize the fixed memory windows */
for (i = 0; i < PCMCIA_SOCKETS_NO; i++) {
for (m = 0; m < PCMCIA_MEM_WIN_NO; m++) {
diff --git a/drivers/platform/x86/acer-wmi.c b/drivers/platform/x86/acer-wmi.c
index ee40d68..c5c4b8c 100644
--- a/drivers/platform/x86/acer-wmi.c
+++ b/drivers/platform/x86/acer-wmi.c
@@ -1021,7 +1021,7 @@
return 0;
}
-static struct backlight_ops acer_bl_ops = {
+static const struct backlight_ops acer_bl_ops = {
.get_brightness = read_brightness,
.update_status = update_bl_status,
};
diff --git a/drivers/platform/x86/asus-laptop.c b/drivers/platform/x86/asus-laptop.c
index d235f44..f3aa6a7 100644
--- a/drivers/platform/x86/asus-laptop.c
+++ b/drivers/platform/x86/asus-laptop.c
@@ -640,7 +640,7 @@
return asus_lcd_set(asus, value);
}
-static struct backlight_ops asusbl_ops = {
+static const struct backlight_ops asusbl_ops = {
.get_brightness = asus_read_brightness,
.update_status = update_bl_status,
};
diff --git a/drivers/platform/x86/asus_acpi.c b/drivers/platform/x86/asus_acpi.c
index ca05aef..4633fd8 100644
--- a/drivers/platform/x86/asus_acpi.c
+++ b/drivers/platform/x86/asus_acpi.c
@@ -1467,7 +1467,7 @@
return 0;
}
-static struct backlight_ops asus_backlight_data = {
+static const struct backlight_ops asus_backlight_data = {
.get_brightness = read_brightness,
.update_status = set_brightness_status,
};
diff --git a/drivers/platform/x86/dell-laptop.c b/drivers/platform/x86/dell-laptop.c
index cf8a89a..34657f9 100644
--- a/drivers/platform/x86/dell-laptop.c
+++ b/drivers/platform/x86/dell-laptop.c
@@ -546,7 +546,7 @@
return buffer->output[1];
}
-static struct backlight_ops dell_ops = {
+static const struct backlight_ops dell_ops = {
.get_brightness = dell_get_intensity,
.update_status = dell_send_intensity,
};
diff --git a/drivers/platform/x86/eeepc-laptop.c b/drivers/platform/x86/eeepc-laptop.c
index e9fc530..49d9ad7 100644
--- a/drivers/platform/x86/eeepc-laptop.c
+++ b/drivers/platform/x86/eeepc-laptop.c
@@ -1126,7 +1126,7 @@
return set_brightness(bd, bd->props.brightness);
}
-static struct backlight_ops eeepcbl_ops = {
+static const struct backlight_ops eeepcbl_ops = {
.get_brightness = read_brightness,
.update_status = update_bl_status,
};
diff --git a/drivers/platform/x86/fujitsu-laptop.c b/drivers/platform/x86/fujitsu-laptop.c
index ad88b2e..19e92b2 100644
--- a/drivers/platform/x86/fujitsu-laptop.c
+++ b/drivers/platform/x86/fujitsu-laptop.c
@@ -437,7 +437,7 @@
return ret;
}
-static struct backlight_ops fujitsubl_ops = {
+static const struct backlight_ops fujitsubl_ops = {
.get_brightness = bl_get_brightness,
.update_status = bl_update_status,
};
diff --git a/drivers/platform/x86/sony-laptop.c b/drivers/platform/x86/sony-laptop.c
index b4a95bb..5e83370 100644
--- a/drivers/platform/x86/sony-laptop.c
+++ b/drivers/platform/x86/sony-laptop.c
@@ -858,7 +858,7 @@
}
static struct backlight_device *sony_backlight_device;
-static struct backlight_ops sony_backlight_ops = {
+static const struct backlight_ops sony_backlight_ops = {
.update_status = sony_backlight_update_status,
.get_brightness = sony_backlight_get_brightness,
};
diff --git a/drivers/platform/x86/thinkpad_acpi.c b/drivers/platform/x86/thinkpad_acpi.c
index a974ca3..dd59958 100644
--- a/drivers/platform/x86/thinkpad_acpi.c
+++ b/drivers/platform/x86/thinkpad_acpi.c
@@ -6110,7 +6110,7 @@
BACKLIGHT_UPDATE_HOTKEY);
}
-static struct backlight_ops ibm_backlight_data = {
+static const struct backlight_ops ibm_backlight_data = {
.get_brightness = brightness_get,
.update_status = brightness_update_status,
};
@@ -7194,7 +7194,7 @@
* TPACPI_FAN_WR_ACPI_FANS (X31/X40/X41)
*
* FIRMWARE BUG: on some models, EC 0x2f might not be initialized at
- * boot. Apparently the EC does not intialize it, so unless ACPI DSDT
+ * boot. Apparently the EC does not initialize it, so unless ACPI DSDT
* does so, its initial value is meaningless (0x07).
*
* For firmware bugs, refer to:
diff --git a/drivers/platform/x86/toshiba_acpi.c b/drivers/platform/x86/toshiba_acpi.c
index 4276da7..209cced 100644
--- a/drivers/platform/x86/toshiba_acpi.c
+++ b/drivers/platform/x86/toshiba_acpi.c
@@ -841,7 +841,7 @@
remove_proc_entry("version", toshiba_proc_dir);
}
-static struct backlight_ops toshiba_backlight_data = {
+static const struct backlight_ops toshiba_backlight_data = {
.get_brightness = get_lcd,
.update_status = set_lcd_status,
};
diff --git a/drivers/power/s3c_adc_battery.c b/drivers/power/s3c_adc_battery.c
index fe16b48..4a8ae39 100644
--- a/drivers/power/s3c_adc_battery.c
+++ b/drivers/power/s3c_adc_battery.c
@@ -1,5 +1,5 @@
/*
- * iPAQ h1930/h1940/rx1950 battery controler driver
+ * iPAQ h1930/h1940/rx1950 battery controller driver
* Copyright (c) Vasily Khoruzhick
* Based on h1940_battery.c by Arnaud Patard
*
@@ -427,5 +427,5 @@
module_exit(s3c_adc_bat_exit);
MODULE_AUTHOR("Vasily Khoruzhick <anarsoul@gmail.com>");
-MODULE_DESCRIPTION("iPAQ H1930/H1940/RX1950 battery controler driver");
+MODULE_DESCRIPTION("iPAQ H1930/H1940/RX1950 battery controller driver");
MODULE_LICENSE("GPL");
diff --git a/drivers/pps/Kconfig b/drivers/pps/Kconfig
index 1afe4e0..f0d3376 100644
--- a/drivers/pps/Kconfig
+++ b/drivers/pps/Kconfig
@@ -30,6 +30,17 @@
messages to the system log. Select this if you are having a
problem with PPS support and want to see more of what is going on.
+config NTP_PPS
+ bool "PPS kernel consumer support"
+ depends on PPS && !NO_HZ
+ help
+ This option adds support for direct in-kernel time
+ syncronization using an external PPS signal.
+
+ It doesn't work on tickless systems at the moment.
+
source drivers/pps/clients/Kconfig
+source drivers/pps/generators/Kconfig
+
endmenu
diff --git a/drivers/pps/Makefile b/drivers/pps/Makefile
index 98960dd..4483eaa 100644
--- a/drivers/pps/Makefile
+++ b/drivers/pps/Makefile
@@ -3,7 +3,8 @@
#
pps_core-y := pps.o kapi.o sysfs.o
+pps_core-$(CONFIG_NTP_PPS) += kc.o
obj-$(CONFIG_PPS) := pps_core.o
-obj-y += clients/
+obj-y += clients/ generators/
ccflags-$(CONFIG_PPS_DEBUG) := -DDEBUG
diff --git a/drivers/pps/clients/Kconfig b/drivers/pps/clients/Kconfig
index 4e801bd7..8520a7f 100644
--- a/drivers/pps/clients/Kconfig
+++ b/drivers/pps/clients/Kconfig
@@ -22,4 +22,11 @@
If you say yes here you get support for a PPS source connected
with the CD (Carrier Detect) pin of your serial port.
+config PPS_CLIENT_PARPORT
+ tristate "Parallel port PPS client"
+ depends on PPS && PARPORT
+ help
+ If you say yes here you get support for a PPS source connected
+ with the interrupt pin of your parallel port.
+
endif
diff --git a/drivers/pps/clients/Makefile b/drivers/pps/clients/Makefile
index 812c9b1..42517da 100644
--- a/drivers/pps/clients/Makefile
+++ b/drivers/pps/clients/Makefile
@@ -4,6 +4,7 @@
obj-$(CONFIG_PPS_CLIENT_KTIMER) += pps-ktimer.o
obj-$(CONFIG_PPS_CLIENT_LDISC) += pps-ldisc.o
+obj-$(CONFIG_PPS_CLIENT_PARPORT) += pps_parport.o
ifeq ($(CONFIG_PPS_DEBUG),y)
EXTRA_CFLAGS += -DDEBUG
diff --git a/drivers/pps/clients/pps-ktimer.c b/drivers/pps/clients/pps-ktimer.c
index e7ef5b8..2728469 100644
--- a/drivers/pps/clients/pps-ktimer.c
+++ b/drivers/pps/clients/pps-ktimer.c
@@ -19,6 +19,7 @@
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include <linux/kernel.h>
#include <linux/module.h>
@@ -31,7 +32,7 @@
* Global variables
*/
-static int source;
+static struct pps_device *pps;
static struct timer_list ktimer;
/*
@@ -40,19 +41,14 @@
static void pps_ktimer_event(unsigned long ptr)
{
- struct timespec __ts;
- struct pps_ktime ts;
+ struct pps_event_time ts;
/* First of all we get the time stamp... */
- getnstimeofday(&__ts);
+ pps_get_ts(&ts);
- pr_info("PPS event at %lu\n", jiffies);
+ dev_info(pps->dev, "PPS event at %lu\n", jiffies);
- /* ... and translate it to PPS time data struct */
- ts.sec = __ts.tv_sec;
- ts.nsec = __ts.tv_nsec;
-
- pps_event(source, &ts, PPS_CAPTUREASSERT, NULL);
+ pps_event(pps, &ts, PPS_CAPTUREASSERT, NULL);
mod_timer(&ktimer, jiffies + HZ);
}
@@ -61,12 +57,11 @@
* The echo function
*/
-static void pps_ktimer_echo(int source, int event, void *data)
+static void pps_ktimer_echo(struct pps_device *pps, int event, void *data)
{
- pr_info("echo %s %s for source %d\n",
+ dev_info(pps->dev, "echo %s %s\n",
event & PPS_CAPTUREASSERT ? "assert" : "",
- event & PPS_CAPTURECLEAR ? "clear" : "",
- source);
+ event & PPS_CAPTURECLEAR ? "clear" : "");
}
/*
@@ -89,30 +84,27 @@
static void __exit pps_ktimer_exit(void)
{
- del_timer_sync(&ktimer);
- pps_unregister_source(source);
+ dev_info(pps->dev, "ktimer PPS source unregistered\n");
- pr_info("ktimer PPS source unregistered\n");
+ del_timer_sync(&ktimer);
+ pps_unregister_source(pps);
}
static int __init pps_ktimer_init(void)
{
- int ret;
-
- ret = pps_register_source(&pps_ktimer_info,
+ pps = pps_register_source(&pps_ktimer_info,
PPS_CAPTUREASSERT | PPS_OFFSETASSERT);
- if (ret < 0) {
- printk(KERN_ERR "cannot register ktimer source\n");
- return ret;
+ if (pps == NULL) {
+ pr_err("cannot register PPS source\n");
+ return -ENOMEM;
}
- source = ret;
setup_timer(&ktimer, pps_ktimer_event, 0);
mod_timer(&ktimer, jiffies + HZ);
- pr_info("ktimer PPS source registered at %d\n", source);
+ dev_info(pps->dev, "ktimer PPS source registered\n");
- return 0;
+ return 0;
}
module_init(pps_ktimer_init);
diff --git a/drivers/pps/clients/pps-ldisc.c b/drivers/pps/clients/pps-ldisc.c
index 8e1932d..79451f2 100644
--- a/drivers/pps/clients/pps-ldisc.c
+++ b/drivers/pps/clients/pps-ldisc.c
@@ -19,6 +19,8 @@
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
#include <linux/module.h>
#include <linux/serial_core.h>
#include <linux/tty.h>
@@ -27,30 +29,18 @@
#define PPS_TTY_MAGIC 0x0001
static void pps_tty_dcd_change(struct tty_struct *tty, unsigned int status,
- struct timespec *ts)
+ struct pps_event_time *ts)
{
- int id = (long)tty->disc_data;
- struct timespec __ts;
- struct pps_ktime pps_ts;
+ struct pps_device *pps = (struct pps_device *)tty->disc_data;
- /* First of all we get the time stamp... */
- getnstimeofday(&__ts);
-
- /* Does caller give us a timestamp? */
- if (ts) { /* Yes. Let's use it! */
- pps_ts.sec = ts->tv_sec;
- pps_ts.nsec = ts->tv_nsec;
- } else { /* No. Do it ourself! */
- pps_ts.sec = __ts.tv_sec;
- pps_ts.nsec = __ts.tv_nsec;
- }
+ BUG_ON(pps == NULL);
/* Now do the PPS event report */
- pps_event(id, &pps_ts, status ? PPS_CAPTUREASSERT : PPS_CAPTURECLEAR,
- NULL);
+ pps_event(pps, ts, status ? PPS_CAPTUREASSERT :
+ PPS_CAPTURECLEAR, NULL);
- pr_debug("PPS %s at %lu on source #%d\n",
- status ? "assert" : "clear", jiffies, id);
+ dev_dbg(pps->dev, "PPS %s at %lu\n",
+ status ? "assert" : "clear", jiffies);
}
static int (*alias_n_tty_open)(struct tty_struct *tty);
@@ -60,6 +50,7 @@
struct pps_source_info info;
struct tty_driver *drv = tty->driver;
int index = tty->index + drv->name_base;
+ struct pps_device *pps;
int ret;
info.owner = THIS_MODULE;
@@ -70,34 +61,42 @@
PPS_OFFSETASSERT | PPS_OFFSETCLEAR | \
PPS_CANWAIT | PPS_TSFMT_TSPEC;
- ret = pps_register_source(&info, PPS_CAPTUREBOTH | \
+ pps = pps_register_source(&info, PPS_CAPTUREBOTH | \
PPS_OFFSETASSERT | PPS_OFFSETCLEAR);
- if (ret < 0) {
+ if (pps == NULL) {
pr_err("cannot register PPS source \"%s\"\n", info.path);
- return ret;
+ return -ENOMEM;
}
- tty->disc_data = (void *)(long)ret;
+ tty->disc_data = pps;
/* Should open N_TTY ldisc too */
ret = alias_n_tty_open(tty);
- if (ret < 0)
- pps_unregister_source((long)tty->disc_data);
+ if (ret < 0) {
+ pr_err("cannot open tty ldisc \"%s\"\n", info.path);
+ goto err_unregister;
+ }
- pr_info("PPS source #%d \"%s\" added\n", ret, info.path);
+ dev_info(pps->dev, "source \"%s\" added\n", info.path);
return 0;
+
+err_unregister:
+ tty->disc_data = NULL;
+ pps_unregister_source(pps);
+ return ret;
}
static void (*alias_n_tty_close)(struct tty_struct *tty);
static void pps_tty_close(struct tty_struct *tty)
{
- int id = (long)tty->disc_data;
+ struct pps_device *pps = (struct pps_device *)tty->disc_data;
- pps_unregister_source(id);
alias_n_tty_close(tty);
- pr_info("PPS source #%d removed\n", id);
+ tty->disc_data = NULL;
+ dev_info(pps->dev, "removed\n");
+ pps_unregister_source(pps);
}
static struct tty_ldisc_ops pps_ldisc_ops;
diff --git a/drivers/pps/clients/pps_parport.c b/drivers/pps/clients/pps_parport.c
new file mode 100644
index 0000000..32221ef
--- /dev/null
+++ b/drivers/pps/clients/pps_parport.c
@@ -0,0 +1,258 @@
+/*
+ * pps_parport.c -- kernel parallel port PPS client
+ *
+ *
+ * Copyright (C) 2009 Alexander Gordeev <lasaine@lvk.cs.msu.su>
+ *
+ * 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.
+ */
+
+
+/*
+ * TODO:
+ * implement echo over SEL pin
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/irqnr.h>
+#include <linux/time.h>
+#include <linux/parport.h>
+#include <linux/pps_kernel.h>
+
+#define DRVDESC "parallel port PPS client"
+
+/* module parameters */
+
+#define CLEAR_WAIT_MAX 100
+#define CLEAR_WAIT_MAX_ERRORS 5
+
+static unsigned int clear_wait = 100;
+MODULE_PARM_DESC(clear_wait,
+ "Maximum number of port reads when polling for signal clear,"
+ " zero turns clear edge capture off entirely");
+module_param(clear_wait, uint, 0);
+
+
+/* internal per port structure */
+struct pps_client_pp {
+ struct pardevice *pardev; /* parport device */
+ struct pps_device *pps; /* PPS device */
+ unsigned int cw; /* port clear timeout */
+ unsigned int cw_err; /* number of timeouts */
+};
+
+static inline int signal_is_set(struct parport *port)
+{
+ return (port->ops->read_status(port) & PARPORT_STATUS_ACK) != 0;
+}
+
+/* parport interrupt handler */
+static void parport_irq(void *handle)
+{
+ struct pps_event_time ts_assert, ts_clear;
+ struct pps_client_pp *dev = handle;
+ struct parport *port = dev->pardev->port;
+ unsigned int i;
+ unsigned long flags;
+
+ /* first of all we get the time stamp... */
+ pps_get_ts(&ts_assert);
+
+ if (dev->cw == 0)
+ /* clear edge capture disabled */
+ goto out_assert;
+
+ /* try capture the clear edge */
+
+ /* We have to disable interrupts here. The idea is to prevent
+ * other interrupts on the same processor to introduce random
+ * lags while polling the port. Reading from IO port is known
+ * to take approximately 1us while other interrupt handlers can
+ * take much more potentially.
+ *
+ * Interrupts won't be disabled for a long time because the
+ * number of polls is limited by clear_wait parameter which is
+ * kept rather low. So it should never be an issue.
+ */
+ local_irq_save(flags);
+ /* check the signal (no signal means the pulse is lost this time) */
+ if (!signal_is_set(port)) {
+ local_irq_restore(flags);
+ dev_err(dev->pps->dev, "lost the signal\n");
+ goto out_assert;
+ }
+
+ /* poll the port until the signal is unset */
+ for (i = dev->cw; i; i--)
+ if (!signal_is_set(port)) {
+ pps_get_ts(&ts_clear);
+ local_irq_restore(flags);
+ dev->cw_err = 0;
+ goto out_both;
+ }
+ local_irq_restore(flags);
+
+ /* timeout */
+ dev->cw_err++;
+ if (dev->cw_err >= CLEAR_WAIT_MAX_ERRORS) {
+ dev_err(dev->pps->dev, "disabled clear edge capture after %d"
+ " timeouts\n", dev->cw_err);
+ dev->cw = 0;
+ dev->cw_err = 0;
+ }
+
+out_assert:
+ /* fire assert event */
+ pps_event(dev->pps, &ts_assert,
+ PPS_CAPTUREASSERT, NULL);
+ return;
+
+out_both:
+ /* fire assert event */
+ pps_event(dev->pps, &ts_assert,
+ PPS_CAPTUREASSERT, NULL);
+ /* fire clear event */
+ pps_event(dev->pps, &ts_clear,
+ PPS_CAPTURECLEAR, NULL);
+ return;
+}
+
+/* the PPS echo function */
+static void pps_echo(struct pps_device *pps, int event, void *data)
+{
+ dev_info(pps->dev, "echo %s %s\n",
+ event & PPS_CAPTUREASSERT ? "assert" : "",
+ event & PPS_CAPTURECLEAR ? "clear" : "");
+}
+
+static void parport_attach(struct parport *port)
+{
+ struct pps_client_pp *device;
+ struct pps_source_info info = {
+ .name = KBUILD_MODNAME,
+ .path = "",
+ .mode = PPS_CAPTUREBOTH | \
+ PPS_OFFSETASSERT | PPS_OFFSETCLEAR | \
+ PPS_ECHOASSERT | PPS_ECHOCLEAR | \
+ PPS_CANWAIT | PPS_TSFMT_TSPEC,
+ .echo = pps_echo,
+ .owner = THIS_MODULE,
+ .dev = NULL
+ };
+
+ device = kzalloc(sizeof(struct pps_client_pp), GFP_KERNEL);
+ if (!device) {
+ pr_err("memory allocation failed, not attaching\n");
+ return;
+ }
+
+ device->pardev = parport_register_device(port, KBUILD_MODNAME,
+ NULL, NULL, parport_irq, 0, device);
+ if (!device->pardev) {
+ pr_err("couldn't register with %s\n", port->name);
+ goto err_free;
+ }
+
+ if (parport_claim_or_block(device->pardev) < 0) {
+ pr_err("couldn't claim %s\n", port->name);
+ goto err_unregister_dev;
+ }
+
+ device->pps = pps_register_source(&info,
+ PPS_CAPTUREBOTH | PPS_OFFSETASSERT | PPS_OFFSETCLEAR);
+ if (device->pps == NULL) {
+ pr_err("couldn't register PPS source\n");
+ goto err_release_dev;
+ }
+
+ device->cw = clear_wait;
+
+ port->ops->enable_irq(port);
+
+ pr_info("attached to %s\n", port->name);
+
+ return;
+
+err_release_dev:
+ parport_release(device->pardev);
+err_unregister_dev:
+ parport_unregister_device(device->pardev);
+err_free:
+ kfree(device);
+}
+
+static void parport_detach(struct parport *port)
+{
+ struct pardevice *pardev = port->cad;
+ struct pps_client_pp *device;
+
+ /* FIXME: oooh, this is ugly! */
+ if (strcmp(pardev->name, KBUILD_MODNAME))
+ /* not our port */
+ return;
+
+ device = pardev->private;
+
+ port->ops->disable_irq(port);
+ pps_unregister_source(device->pps);
+ parport_release(pardev);
+ parport_unregister_device(pardev);
+ kfree(device);
+}
+
+static struct parport_driver pps_parport_driver = {
+ .name = KBUILD_MODNAME,
+ .attach = parport_attach,
+ .detach = parport_detach,
+};
+
+/* module staff */
+
+static int __init pps_parport_init(void)
+{
+ int ret;
+
+ pr_info(DRVDESC "\n");
+
+ if (clear_wait > CLEAR_WAIT_MAX) {
+ pr_err("clear_wait value should be not greater"
+ " then %d\n", CLEAR_WAIT_MAX);
+ return -EINVAL;
+ }
+
+ ret = parport_register_driver(&pps_parport_driver);
+ if (ret) {
+ pr_err("unable to register with parport\n");
+ return ret;
+ }
+
+ return 0;
+}
+
+static void __exit pps_parport_exit(void)
+{
+ parport_unregister_driver(&pps_parport_driver);
+}
+
+module_init(pps_parport_init);
+module_exit(pps_parport_exit);
+
+MODULE_AUTHOR("Alexander Gordeev <lasaine@lvk.cs.msu.su>");
+MODULE_DESCRIPTION(DRVDESC);
+MODULE_LICENSE("GPL");
diff --git a/drivers/pps/generators/Kconfig b/drivers/pps/generators/Kconfig
new file mode 100644
index 0000000..f3a73dd
--- /dev/null
+++ b/drivers/pps/generators/Kconfig
@@ -0,0 +1,13 @@
+#
+# PPS generators configuration
+#
+
+comment "PPS generators support"
+
+config PPS_GENERATOR_PARPORT
+ tristate "Parallel port PPS signal generator"
+ depends on PARPORT
+ help
+ If you say yes here you get support for a PPS signal generator which
+ utilizes STROBE pin of a parallel port to send PPS signals. It uses
+ parport abstraction layer and hrtimers to precisely control the signal.
diff --git a/drivers/pps/generators/Makefile b/drivers/pps/generators/Makefile
new file mode 100644
index 0000000..303304a6
--- /dev/null
+++ b/drivers/pps/generators/Makefile
@@ -0,0 +1,9 @@
+#
+# Makefile for PPS generators.
+#
+
+obj-$(CONFIG_PPS_GENERATOR_PARPORT) += pps_gen_parport.o
+
+ifeq ($(CONFIG_PPS_DEBUG),y)
+EXTRA_CFLAGS += -DDEBUG
+endif
diff --git a/drivers/pps/generators/pps_gen_parport.c b/drivers/pps/generators/pps_gen_parport.c
new file mode 100644
index 0000000..5c32f8d
--- /dev/null
+++ b/drivers/pps/generators/pps_gen_parport.c
@@ -0,0 +1,282 @@
+/*
+ * pps_gen_parport.c -- kernel parallel port PPS signal generator
+ *
+ *
+ * Copyright (C) 2009 Alexander Gordeev <lasaine@lvk.cs.msu.su>
+ *
+ * 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.
+ */
+
+
+/*
+ * TODO:
+ * fix issues when realtime clock is adjusted in a leap
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/time.h>
+#include <linux/hrtimer.h>
+#include <linux/parport.h>
+
+#define DRVDESC "parallel port PPS signal generator"
+
+#define SIGNAL 0
+#define NO_SIGNAL PARPORT_CONTROL_STROBE
+
+/* module parameters */
+
+#define SEND_DELAY_MAX 100000
+
+static unsigned int send_delay = 30000;
+MODULE_PARM_DESC(delay,
+ "Delay between setting and dropping the signal (ns)");
+module_param_named(delay, send_delay, uint, 0);
+
+
+#define SAFETY_INTERVAL 3000 /* set the hrtimer earlier for safety (ns) */
+
+/* internal per port structure */
+struct pps_generator_pp {
+ struct pardevice *pardev; /* parport device */
+ struct hrtimer timer;
+ long port_write_time; /* calibrated port write time (ns) */
+};
+
+static struct pps_generator_pp device = {
+ .pardev = NULL,
+};
+
+static int attached;
+
+/* calibrated time between a hrtimer event and the reaction */
+static long hrtimer_error = SAFETY_INTERVAL;
+
+/* the kernel hrtimer event */
+static enum hrtimer_restart hrtimer_event(struct hrtimer *timer)
+{
+ struct timespec expire_time, ts1, ts2, ts3, dts;
+ struct pps_generator_pp *dev;
+ struct parport *port;
+ long lim, delta;
+ unsigned long flags;
+
+ /* We have to disable interrupts here. The idea is to prevent
+ * other interrupts on the same processor to introduce random
+ * lags while polling the clock. getnstimeofday() takes <1us on
+ * most machines while other interrupt handlers can take much
+ * more potentially.
+ *
+ * NB: approx time with blocked interrupts =
+ * send_delay + 3 * SAFETY_INTERVAL
+ */
+ local_irq_save(flags);
+
+ /* first of all we get the time stamp... */
+ getnstimeofday(&ts1);
+ expire_time = ktime_to_timespec(hrtimer_get_softexpires(timer));
+ dev = container_of(timer, struct pps_generator_pp, timer);
+ lim = NSEC_PER_SEC - send_delay - dev->port_write_time;
+
+ /* check if we are late */
+ if (expire_time.tv_sec != ts1.tv_sec || ts1.tv_nsec > lim) {
+ local_irq_restore(flags);
+ pr_err("we are late this time %ld.%09ld\n",
+ ts1.tv_sec, ts1.tv_nsec);
+ goto done;
+ }
+
+ /* busy loop until the time is right for an assert edge */
+ do {
+ getnstimeofday(&ts2);
+ } while (expire_time.tv_sec == ts2.tv_sec && ts2.tv_nsec < lim);
+
+ /* set the signal */
+ port = dev->pardev->port;
+ port->ops->write_control(port, SIGNAL);
+
+ /* busy loop until the time is right for a clear edge */
+ lim = NSEC_PER_SEC - dev->port_write_time;
+ do {
+ getnstimeofday(&ts2);
+ } while (expire_time.tv_sec == ts2.tv_sec && ts2.tv_nsec < lim);
+
+ /* unset the signal */
+ port->ops->write_control(port, NO_SIGNAL);
+
+ getnstimeofday(&ts3);
+
+ local_irq_restore(flags);
+
+ /* update calibrated port write time */
+ dts = timespec_sub(ts3, ts2);
+ dev->port_write_time =
+ (dev->port_write_time + timespec_to_ns(&dts)) >> 1;
+
+done:
+ /* update calibrated hrtimer error */
+ dts = timespec_sub(ts1, expire_time);
+ delta = timespec_to_ns(&dts);
+ /* If the new error value is bigger then the old, use the new
+ * value, if not then slowly move towards the new value. This
+ * way it should be safe in bad conditions and efficient in
+ * good conditions.
+ */
+ if (delta >= hrtimer_error)
+ hrtimer_error = delta;
+ else
+ hrtimer_error = (3 * hrtimer_error + delta) >> 2;
+
+ /* update the hrtimer expire time */
+ hrtimer_set_expires(timer,
+ ktime_set(expire_time.tv_sec + 1,
+ NSEC_PER_SEC - (send_delay +
+ dev->port_write_time + SAFETY_INTERVAL +
+ 2 * hrtimer_error)));
+
+ return HRTIMER_RESTART;
+}
+
+/* calibrate port write time */
+#define PORT_NTESTS_SHIFT 5
+static void calibrate_port(struct pps_generator_pp *dev)
+{
+ struct parport *port = dev->pardev->port;
+ int i;
+ long acc = 0;
+
+ for (i = 0; i < (1 << PORT_NTESTS_SHIFT); i++) {
+ struct timespec a, b;
+ unsigned long irq_flags;
+
+ local_irq_save(irq_flags);
+ getnstimeofday(&a);
+ port->ops->write_control(port, NO_SIGNAL);
+ getnstimeofday(&b);
+ local_irq_restore(irq_flags);
+
+ b = timespec_sub(b, a);
+ acc += timespec_to_ns(&b);
+ }
+
+ dev->port_write_time = acc >> PORT_NTESTS_SHIFT;
+ pr_info("port write takes %ldns\n", dev->port_write_time);
+}
+
+static inline ktime_t next_intr_time(struct pps_generator_pp *dev)
+{
+ struct timespec ts;
+
+ getnstimeofday(&ts);
+
+ return ktime_set(ts.tv_sec +
+ ((ts.tv_nsec > 990 * NSEC_PER_MSEC) ? 1 : 0),
+ NSEC_PER_SEC - (send_delay +
+ dev->port_write_time + 3 * SAFETY_INTERVAL));
+}
+
+static void parport_attach(struct parport *port)
+{
+ if (attached) {
+ /* we already have a port */
+ return;
+ }
+
+ device.pardev = parport_register_device(port, KBUILD_MODNAME,
+ NULL, NULL, NULL, 0, &device);
+ if (!device.pardev) {
+ pr_err("couldn't register with %s\n", port->name);
+ return;
+ }
+
+ if (parport_claim_or_block(device.pardev) < 0) {
+ pr_err("couldn't claim %s\n", port->name);
+ goto err_unregister_dev;
+ }
+
+ pr_info("attached to %s\n", port->name);
+ attached = 1;
+
+ calibrate_port(&device);
+
+ hrtimer_init(&device.timer, CLOCK_REALTIME, HRTIMER_MODE_ABS);
+ device.timer.function = hrtimer_event;
+#ifdef CONFIG_PREEMPT_RT
+ /* hrtimer interrupt will run in the interrupt context with this */
+ device.timer.irqsafe = 1;
+#endif
+
+ hrtimer_start(&device.timer, next_intr_time(&device), HRTIMER_MODE_ABS);
+
+ return;
+
+err_unregister_dev:
+ parport_unregister_device(device.pardev);
+}
+
+static void parport_detach(struct parport *port)
+{
+ if (port->cad != device.pardev)
+ return; /* not our port */
+
+ hrtimer_cancel(&device.timer);
+ parport_release(device.pardev);
+ parport_unregister_device(device.pardev);
+}
+
+static struct parport_driver pps_gen_parport_driver = {
+ .name = KBUILD_MODNAME,
+ .attach = parport_attach,
+ .detach = parport_detach,
+};
+
+/* module staff */
+
+static int __init pps_gen_parport_init(void)
+{
+ int ret;
+
+ pr_info(DRVDESC "\n");
+
+ if (send_delay > SEND_DELAY_MAX) {
+ pr_err("delay value should be not greater"
+ " then %d\n", SEND_DELAY_MAX);
+ return -EINVAL;
+ }
+
+ ret = parport_register_driver(&pps_gen_parport_driver);
+ if (ret) {
+ pr_err("unable to register with parport\n");
+ return ret;
+ }
+
+ return 0;
+}
+
+static void __exit pps_gen_parport_exit(void)
+{
+ parport_unregister_driver(&pps_gen_parport_driver);
+ pr_info("hrtimer avg error is %ldns\n", hrtimer_error);
+}
+
+module_init(pps_gen_parport_init);
+module_exit(pps_gen_parport_exit);
+
+MODULE_AUTHOR("Alexander Gordeev <lasaine@lvk.cs.msu.su>");
+MODULE_DESCRIPTION(DRVDESC);
+MODULE_LICENSE("GPL");
diff --git a/drivers/pps/kapi.c b/drivers/pps/kapi.c
index 1aa02db..cba1b43 100644
--- a/drivers/pps/kapi.c
+++ b/drivers/pps/kapi.c
@@ -19,24 +19,20 @@
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/sched.h>
#include <linux/time.h>
+#include <linux/timex.h>
#include <linux/spinlock.h>
-#include <linux/idr.h>
#include <linux/fs.h>
#include <linux/pps_kernel.h>
#include <linux/slab.h>
-/*
- * Global variables
- */
-
-DEFINE_SPINLOCK(pps_idr_lock);
-DEFINE_IDR(pps_idr);
+#include "kc.h"
/*
* Local functions
@@ -60,60 +56,6 @@
* Exported functions
*/
-/* pps_get_source - find a PPS source
- * @source: the PPS source ID.
- *
- * This function is used to find an already registered PPS source into the
- * system.
- *
- * The function returns NULL if found nothing, otherwise it returns a pointer
- * to the PPS source data struct (the refcounter is incremented by 1).
- */
-
-struct pps_device *pps_get_source(int source)
-{
- struct pps_device *pps;
- unsigned long flags;
-
- spin_lock_irqsave(&pps_idr_lock, flags);
-
- pps = idr_find(&pps_idr, source);
- if (pps != NULL)
- atomic_inc(&pps->usage);
-
- spin_unlock_irqrestore(&pps_idr_lock, flags);
-
- return pps;
-}
-
-/* pps_put_source - free the PPS source data
- * @pps: a pointer to the PPS source.
- *
- * This function is used to free a PPS data struct if its refcount is 0.
- */
-
-void pps_put_source(struct pps_device *pps)
-{
- unsigned long flags;
-
- spin_lock_irqsave(&pps_idr_lock, flags);
- BUG_ON(atomic_read(&pps->usage) == 0);
-
- if (!atomic_dec_and_test(&pps->usage)) {
- pps = NULL;
- goto exit;
- }
-
- /* No more reference to the PPS source. We can safely remove the
- * PPS data struct.
- */
- idr_remove(&pps_idr, pps->id);
-
-exit:
- spin_unlock_irqrestore(&pps_idr_lock, flags);
- kfree(pps);
-}
-
/* pps_register_source - add a PPS source in the system
* @info: the PPS info struct
* @default_params: the default PPS parameters of the new source
@@ -122,31 +64,31 @@
* source is described by info's fields and it will have, as default PPS
* parameters, the ones specified into default_params.
*
- * The function returns, in case of success, the PPS source ID.
+ * The function returns, in case of success, the PPS device. Otherwise NULL.
*/
-int pps_register_source(struct pps_source_info *info, int default_params)
+struct pps_device *pps_register_source(struct pps_source_info *info,
+ int default_params)
{
struct pps_device *pps;
- int id;
int err;
/* Sanity checks */
if ((info->mode & default_params) != default_params) {
- printk(KERN_ERR "pps: %s: unsupported default parameters\n",
+ pr_err("%s: unsupported default parameters\n",
info->name);
err = -EINVAL;
goto pps_register_source_exit;
}
if ((info->mode & (PPS_ECHOASSERT | PPS_ECHOCLEAR)) != 0 &&
info->echo == NULL) {
- printk(KERN_ERR "pps: %s: echo function is not defined\n",
+ pr_err("%s: echo function is not defined\n",
info->name);
err = -EINVAL;
goto pps_register_source_exit;
}
if ((info->mode & (PPS_TSFMT_TSPEC | PPS_TSFMT_NTPFP)) == 0) {
- printk(KERN_ERR "pps: %s: unspecified time format\n",
+ pr_err("%s: unspecified time format\n",
info->name);
err = -EINVAL;
goto pps_register_source_exit;
@@ -168,94 +110,48 @@
init_waitqueue_head(&pps->queue);
spin_lock_init(&pps->lock);
- atomic_set(&pps->usage, 1);
-
- /* Get new ID for the new PPS source */
- if (idr_pre_get(&pps_idr, GFP_KERNEL) == 0) {
- err = -ENOMEM;
- goto kfree_pps;
- }
-
- spin_lock_irq(&pps_idr_lock);
-
- /* Now really allocate the PPS source.
- * After idr_get_new() calling the new source will be freely available
- * into the kernel.
- */
- err = idr_get_new(&pps_idr, pps, &id);
- if (err < 0) {
- spin_unlock_irq(&pps_idr_lock);
- goto kfree_pps;
- }
-
- id = id & MAX_ID_MASK;
- if (id >= PPS_MAX_SOURCES) {
- spin_unlock_irq(&pps_idr_lock);
-
- printk(KERN_ERR "pps: %s: too many PPS sources in the system\n",
- info->name);
- err = -EBUSY;
- goto free_idr;
- }
- pps->id = id;
-
- spin_unlock_irq(&pps_idr_lock);
/* Create the char device */
err = pps_register_cdev(pps);
if (err < 0) {
- printk(KERN_ERR "pps: %s: unable to create char device\n",
+ pr_err("%s: unable to create char device\n",
info->name);
- goto free_idr;
+ goto kfree_pps;
}
- pr_info("new PPS source %s at ID %d\n", info->name, id);
+ dev_info(pps->dev, "new PPS source %s\n", info->name);
- return id;
-
-free_idr:
- spin_lock_irq(&pps_idr_lock);
- idr_remove(&pps_idr, id);
- spin_unlock_irq(&pps_idr_lock);
+ return pps;
kfree_pps:
kfree(pps);
pps_register_source_exit:
- printk(KERN_ERR "pps: %s: unable to register source\n", info->name);
+ pr_err("%s: unable to register source\n", info->name);
- return err;
+ return NULL;
}
EXPORT_SYMBOL(pps_register_source);
/* pps_unregister_source - remove a PPS source from the system
- * @source: the PPS source ID
+ * @pps: the PPS source
*
* This function is used to remove a previously registered PPS source from
* the system.
*/
-void pps_unregister_source(int source)
+void pps_unregister_source(struct pps_device *pps)
{
- struct pps_device *pps;
-
- spin_lock_irq(&pps_idr_lock);
- pps = idr_find(&pps_idr, source);
-
- if (!pps) {
- BUG();
- spin_unlock_irq(&pps_idr_lock);
- return;
- }
- spin_unlock_irq(&pps_idr_lock);
-
+ pps_kc_remove(pps);
pps_unregister_cdev(pps);
- pps_put_source(pps);
+
+ /* don't have to kfree(pps) here because it will be done on
+ * device destruction */
}
EXPORT_SYMBOL(pps_unregister_source);
/* pps_event - register a PPS event into the system
- * @source: the PPS source ID
+ * @pps: the PPS device
* @ts: the event timestamp
* @event: the event type
* @data: userdef pointer
@@ -263,78 +159,72 @@
* This function is used by each PPS client in order to register a new
* PPS event into the system (it's usually called inside an IRQ handler).
*
- * If an echo function is associated with the PPS source it will be called
+ * If an echo function is associated with the PPS device it will be called
* as:
- * pps->info.echo(source, event, data);
+ * pps->info.echo(pps, event, data);
*/
-
-void pps_event(int source, struct pps_ktime *ts, int event, void *data)
+void pps_event(struct pps_device *pps, struct pps_event_time *ts, int event,
+ void *data)
{
- struct pps_device *pps;
unsigned long flags;
int captured = 0;
+ struct pps_ktime ts_real;
- if ((event & (PPS_CAPTUREASSERT | PPS_CAPTURECLEAR)) == 0) {
- printk(KERN_ERR "pps: unknown event (%x) for source %d\n",
- event, source);
- return;
- }
+ /* check event type */
+ BUG_ON((event & (PPS_CAPTUREASSERT | PPS_CAPTURECLEAR)) == 0);
- pps = pps_get_source(source);
- if (!pps)
- return;
+ dev_dbg(pps->dev, "PPS event at %ld.%09ld\n",
+ ts->ts_real.tv_sec, ts->ts_real.tv_nsec);
- pr_debug("PPS event on source %d at %llu.%06u\n",
- pps->id, (unsigned long long) ts->sec, ts->nsec);
+ timespec_to_pps_ktime(&ts_real, ts->ts_real);
spin_lock_irqsave(&pps->lock, flags);
/* Must call the echo function? */
if ((pps->params.mode & (PPS_ECHOASSERT | PPS_ECHOCLEAR)))
- pps->info.echo(source, event, data);
+ pps->info.echo(pps, event, data);
/* Check the event */
pps->current_mode = pps->params.mode;
- if ((event & PPS_CAPTUREASSERT) &
- (pps->params.mode & PPS_CAPTUREASSERT)) {
+ if (event & pps->params.mode & PPS_CAPTUREASSERT) {
/* We have to add an offset? */
if (pps->params.mode & PPS_OFFSETASSERT)
- pps_add_offset(ts, &pps->params.assert_off_tu);
+ pps_add_offset(&ts_real,
+ &pps->params.assert_off_tu);
/* Save the time stamp */
- pps->assert_tu = *ts;
+ pps->assert_tu = ts_real;
pps->assert_sequence++;
- pr_debug("capture assert seq #%u for source %d\n",
- pps->assert_sequence, source);
+ dev_dbg(pps->dev, "capture assert seq #%u\n",
+ pps->assert_sequence);
captured = ~0;
}
- if ((event & PPS_CAPTURECLEAR) &
- (pps->params.mode & PPS_CAPTURECLEAR)) {
+ if (event & pps->params.mode & PPS_CAPTURECLEAR) {
/* We have to add an offset? */
if (pps->params.mode & PPS_OFFSETCLEAR)
- pps_add_offset(ts, &pps->params.clear_off_tu);
+ pps_add_offset(&ts_real,
+ &pps->params.clear_off_tu);
/* Save the time stamp */
- pps->clear_tu = *ts;
+ pps->clear_tu = ts_real;
pps->clear_sequence++;
- pr_debug("capture clear seq #%u for source %d\n",
- pps->clear_sequence, source);
+ dev_dbg(pps->dev, "capture clear seq #%u\n",
+ pps->clear_sequence);
captured = ~0;
}
- /* Wake up iif captured somthing */
+ pps_kc_event(pps, ts, event);
+
+ /* Wake up if captured something */
if (captured) {
- pps->go = ~0;
- wake_up_interruptible(&pps->queue);
+ pps->last_ev++;
+ wake_up_interruptible_all(&pps->queue);
kill_fasync(&pps->async_queue, SIGIO, POLL_IN);
}
spin_unlock_irqrestore(&pps->lock, flags);
-
- /* Now we can release the PPS source for (possible) deregistration */
- pps_put_source(pps);
}
EXPORT_SYMBOL(pps_event);
diff --git a/drivers/pps/kc.c b/drivers/pps/kc.c
new file mode 100644
index 0000000..079e930
--- /dev/null
+++ b/drivers/pps/kc.c
@@ -0,0 +1,122 @@
+/*
+ * PPS kernel consumer API
+ *
+ * Copyright (C) 2009-2010 Alexander Gordeev <lasaine@lvk.cs.msu.su>
+ *
+ * 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.
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/init.h>
+#include <linux/spinlock.h>
+#include <linux/pps_kernel.h>
+
+#include "kc.h"
+
+/*
+ * Global variables
+ */
+
+/* state variables to bind kernel consumer */
+DEFINE_SPINLOCK(pps_kc_hardpps_lock);
+/* PPS API (RFC 2783): current source and mode for kernel consumer */
+struct pps_device *pps_kc_hardpps_dev; /* unique pointer to device */
+int pps_kc_hardpps_mode; /* mode bits for kernel consumer */
+
+/* pps_kc_bind - control PPS kernel consumer binding
+ * @pps: the PPS source
+ * @bind_args: kernel consumer bind parameters
+ *
+ * This function is used to bind or unbind PPS kernel consumer according to
+ * supplied parameters. Should not be called in interrupt context.
+ */
+int pps_kc_bind(struct pps_device *pps, struct pps_bind_args *bind_args)
+{
+ /* Check if another consumer is already bound */
+ spin_lock_irq(&pps_kc_hardpps_lock);
+
+ if (bind_args->edge == 0)
+ if (pps_kc_hardpps_dev == pps) {
+ pps_kc_hardpps_mode = 0;
+ pps_kc_hardpps_dev = NULL;
+ spin_unlock_irq(&pps_kc_hardpps_lock);
+ dev_info(pps->dev, "unbound kernel"
+ " consumer\n");
+ } else {
+ spin_unlock_irq(&pps_kc_hardpps_lock);
+ dev_err(pps->dev, "selected kernel consumer"
+ " is not bound\n");
+ return -EINVAL;
+ }
+ else
+ if (pps_kc_hardpps_dev == NULL ||
+ pps_kc_hardpps_dev == pps) {
+ pps_kc_hardpps_mode = bind_args->edge;
+ pps_kc_hardpps_dev = pps;
+ spin_unlock_irq(&pps_kc_hardpps_lock);
+ dev_info(pps->dev, "bound kernel consumer: "
+ "edge=0x%x\n", bind_args->edge);
+ } else {
+ spin_unlock_irq(&pps_kc_hardpps_lock);
+ dev_err(pps->dev, "another kernel consumer"
+ " is already bound\n");
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+/* pps_kc_remove - unbind kernel consumer on PPS source removal
+ * @pps: the PPS source
+ *
+ * This function is used to disable kernel consumer on PPS source removal
+ * if this source was bound to PPS kernel consumer. Can be called on any
+ * source safely. Should not be called in interrupt context.
+ */
+void pps_kc_remove(struct pps_device *pps)
+{
+ spin_lock_irq(&pps_kc_hardpps_lock);
+ if (pps == pps_kc_hardpps_dev) {
+ pps_kc_hardpps_mode = 0;
+ pps_kc_hardpps_dev = NULL;
+ spin_unlock_irq(&pps_kc_hardpps_lock);
+ dev_info(pps->dev, "unbound kernel consumer"
+ " on device removal\n");
+ } else
+ spin_unlock_irq(&pps_kc_hardpps_lock);
+}
+
+/* pps_kc_event - call hardpps() on PPS event
+ * @pps: the PPS source
+ * @ts: PPS event timestamp
+ * @event: PPS event edge
+ *
+ * This function calls hardpps() when an event from bound PPS source occurs.
+ */
+void pps_kc_event(struct pps_device *pps, struct pps_event_time *ts,
+ int event)
+{
+ unsigned long flags;
+
+ /* Pass some events to kernel consumer if activated */
+ spin_lock_irqsave(&pps_kc_hardpps_lock, flags);
+ if (pps == pps_kc_hardpps_dev && event & pps_kc_hardpps_mode)
+ hardpps(&ts->ts_real, &ts->ts_raw);
+ spin_unlock_irqrestore(&pps_kc_hardpps_lock, flags);
+}
diff --git a/drivers/pps/kc.h b/drivers/pps/kc.h
new file mode 100644
index 0000000..d296fcd
--- /dev/null
+++ b/drivers/pps/kc.h
@@ -0,0 +1,46 @@
+/*
+ * PPS kernel consumer API header
+ *
+ * Copyright (C) 2009-2010 Alexander Gordeev <lasaine@lvk.cs.msu.su>
+ *
+ * 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.
+ */
+
+#ifndef LINUX_PPS_KC_H
+#define LINUX_PPS_KC_H
+
+#include <linux/errno.h>
+#include <linux/pps_kernel.h>
+
+#ifdef CONFIG_NTP_PPS
+
+extern int pps_kc_bind(struct pps_device *pps,
+ struct pps_bind_args *bind_args);
+extern void pps_kc_remove(struct pps_device *pps);
+extern void pps_kc_event(struct pps_device *pps,
+ struct pps_event_time *ts, int event);
+
+
+#else /* CONFIG_NTP_PPS */
+
+static inline int pps_kc_bind(struct pps_device *pps,
+ struct pps_bind_args *bind_args) { return -EOPNOTSUPP; }
+static inline void pps_kc_remove(struct pps_device *pps) {}
+static inline void pps_kc_event(struct pps_device *pps,
+ struct pps_event_time *ts, int event) {}
+
+#endif /* CONFIG_NTP_PPS */
+
+#endif /* LINUX_PPS_KC_H */
diff --git a/drivers/pps/pps.c b/drivers/pps/pps.c
index ca5183b..2baadd2 100644
--- a/drivers/pps/pps.c
+++ b/drivers/pps/pps.c
@@ -19,6 +19,7 @@
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include <linux/kernel.h>
#include <linux/module.h>
@@ -26,9 +27,13 @@
#include <linux/sched.h>
#include <linux/uaccess.h>
#include <linux/idr.h>
+#include <linux/mutex.h>
#include <linux/cdev.h>
#include <linux/poll.h>
#include <linux/pps_kernel.h>
+#include <linux/slab.h>
+
+#include "kc.h"
/*
* Local variables
@@ -37,6 +42,9 @@
static dev_t pps_devt;
static struct class *pps_class;
+static DEFINE_MUTEX(pps_idr_lock);
+static DEFINE_IDR(pps_idr);
+
/*
* Char device methods
*/
@@ -61,15 +69,13 @@
{
struct pps_device *pps = file->private_data;
struct pps_kparams params;
- struct pps_fdata fdata;
- unsigned long ticks;
void __user *uarg = (void __user *) arg;
int __user *iuarg = (int __user *) arg;
int err;
switch (cmd) {
case PPS_GETPARAMS:
- pr_debug("PPS_GETPARAMS: source %d\n", pps->id);
+ dev_dbg(pps->dev, "PPS_GETPARAMS\n");
spin_lock_irq(&pps->lock);
@@ -85,7 +91,7 @@
break;
case PPS_SETPARAMS:
- pr_debug("PPS_SETPARAMS: source %d\n", pps->id);
+ dev_dbg(pps->dev, "PPS_SETPARAMS\n");
/* Check the capabilities */
if (!capable(CAP_SYS_TIME))
@@ -95,14 +101,14 @@
if (err)
return -EFAULT;
if (!(params.mode & (PPS_CAPTUREASSERT | PPS_CAPTURECLEAR))) {
- pr_debug("capture mode unspecified (%x)\n",
+ dev_dbg(pps->dev, "capture mode unspecified (%x)\n",
params.mode);
return -EINVAL;
}
/* Check for supported capabilities */
if ((params.mode & ~pps->info.mode) != 0) {
- pr_debug("unsupported capabilities (%x)\n",
+ dev_dbg(pps->dev, "unsupported capabilities (%x)\n",
params.mode);
return -EINVAL;
}
@@ -115,7 +121,7 @@
/* Restore the read only parameters */
if ((params.mode & (PPS_TSFMT_TSPEC | PPS_TSFMT_NTPFP)) == 0) {
/* section 3.3 of RFC 2783 interpreted */
- pr_debug("time format unspecified (%x)\n",
+ dev_dbg(pps->dev, "time format unspecified (%x)\n",
params.mode);
pps->params.mode |= PPS_TSFMT_TSPEC;
}
@@ -128,7 +134,7 @@
break;
case PPS_GETCAP:
- pr_debug("PPS_GETCAP: source %d\n", pps->id);
+ dev_dbg(pps->dev, "PPS_GETCAP\n");
err = put_user(pps->info.mode, iuarg);
if (err)
@@ -136,20 +142,26 @@
break;
- case PPS_FETCH:
- pr_debug("PPS_FETCH: source %d\n", pps->id);
+ case PPS_FETCH: {
+ struct pps_fdata fdata;
+ unsigned int ev;
+
+ dev_dbg(pps->dev, "PPS_FETCH\n");
err = copy_from_user(&fdata, uarg, sizeof(struct pps_fdata));
if (err)
return -EFAULT;
- pps->go = 0;
+ ev = pps->last_ev;
/* Manage the timeout */
if (fdata.timeout.flags & PPS_TIME_INVALID)
- err = wait_event_interruptible(pps->queue, pps->go);
+ err = wait_event_interruptible(pps->queue,
+ ev != pps->last_ev);
else {
- pr_debug("timeout %lld.%09d\n",
+ unsigned long ticks;
+
+ dev_dbg(pps->dev, "timeout %lld.%09d\n",
(long long) fdata.timeout.sec,
fdata.timeout.nsec);
ticks = fdata.timeout.sec * HZ;
@@ -157,7 +169,9 @@
if (ticks != 0) {
err = wait_event_interruptible_timeout(
- pps->queue, pps->go, ticks);
+ pps->queue,
+ ev != pps->last_ev,
+ ticks);
if (err == 0)
return -ETIMEDOUT;
}
@@ -165,7 +179,7 @@
/* Check for pending signals */
if (err == -ERESTARTSYS) {
- pr_debug("pending signal caught\n");
+ dev_dbg(pps->dev, "pending signal caught\n");
return -EINTR;
}
@@ -185,10 +199,44 @@
return -EFAULT;
break;
+ }
+ case PPS_KC_BIND: {
+ struct pps_bind_args bind_args;
+ dev_dbg(pps->dev, "PPS_KC_BIND\n");
+
+ /* Check the capabilities */
+ if (!capable(CAP_SYS_TIME))
+ return -EPERM;
+
+ if (copy_from_user(&bind_args, uarg,
+ sizeof(struct pps_bind_args)))
+ return -EFAULT;
+
+ /* Check for supported capabilities */
+ if ((bind_args.edge & ~pps->info.mode) != 0) {
+ dev_err(pps->dev, "unsupported capabilities (%x)\n",
+ bind_args.edge);
+ return -EINVAL;
+ }
+
+ /* Validate parameters roughly */
+ if (bind_args.tsformat != PPS_TSFMT_TSPEC ||
+ (bind_args.edge & ~PPS_CAPTUREBOTH) != 0 ||
+ bind_args.consumer != PPS_KC_HARDPPS) {
+ dev_err(pps->dev, "invalid kernel consumer bind"
+ " parameters (%x)\n", bind_args.edge);
+ return -EINVAL;
+ }
+
+ err = pps_kc_bind(pps, &bind_args);
+ if (err < 0)
+ return err;
+
+ break;
+ }
default:
return -ENOTTY;
- break;
}
return 0;
@@ -198,12 +246,6 @@
{
struct pps_device *pps = container_of(inode->i_cdev,
struct pps_device, cdev);
- int found;
-
- found = pps_get_source(pps->id) != 0;
- if (!found)
- return -ENODEV;
-
file->private_data = pps;
return 0;
@@ -211,11 +253,6 @@
static int pps_cdev_release(struct inode *inode, struct file *file)
{
- struct pps_device *pps = file->private_data;
-
- /* Free the PPS source and wake up (possible) deregistration */
- pps_put_source(pps);
-
return 0;
}
@@ -233,25 +270,67 @@
.release = pps_cdev_release,
};
+static void pps_device_destruct(struct device *dev)
+{
+ struct pps_device *pps = dev_get_drvdata(dev);
+
+ /* release id here to protect others from using it while it's
+ * still in use */
+ mutex_lock(&pps_idr_lock);
+ idr_remove(&pps_idr, pps->id);
+ mutex_unlock(&pps_idr_lock);
+
+ kfree(dev);
+ kfree(pps);
+}
+
int pps_register_cdev(struct pps_device *pps)
{
int err;
+ dev_t devt;
- pps->devno = MKDEV(MAJOR(pps_devt), pps->id);
+ mutex_lock(&pps_idr_lock);
+ /* Get new ID for the new PPS source */
+ if (idr_pre_get(&pps_idr, GFP_KERNEL) == 0) {
+ mutex_unlock(&pps_idr_lock);
+ return -ENOMEM;
+ }
+
+ /* Now really allocate the PPS source.
+ * After idr_get_new() calling the new source will be freely available
+ * into the kernel.
+ */
+ err = idr_get_new(&pps_idr, pps, &pps->id);
+ mutex_unlock(&pps_idr_lock);
+
+ if (err < 0)
+ return err;
+
+ pps->id &= MAX_ID_MASK;
+ if (pps->id >= PPS_MAX_SOURCES) {
+ pr_err("%s: too many PPS sources in the system\n",
+ pps->info.name);
+ err = -EBUSY;
+ goto free_idr;
+ }
+
+ devt = MKDEV(MAJOR(pps_devt), pps->id);
+
cdev_init(&pps->cdev, &pps_cdev_fops);
pps->cdev.owner = pps->info.owner;
- err = cdev_add(&pps->cdev, pps->devno, 1);
+ err = cdev_add(&pps->cdev, devt, 1);
if (err) {
- printk(KERN_ERR "pps: %s: failed to add char device %d:%d\n",
+ pr_err("%s: failed to add char device %d:%d\n",
pps->info.name, MAJOR(pps_devt), pps->id);
- return err;
+ goto free_idr;
}
- pps->dev = device_create(pps_class, pps->info.dev, pps->devno, NULL,
+ pps->dev = device_create(pps_class, pps->info.dev, devt, pps,
"pps%d", pps->id);
if (IS_ERR(pps->dev))
goto del_cdev;
- dev_set_drvdata(pps->dev, pps);
+
+ pps->dev->release = pps_device_destruct;
pr_debug("source %s got cdev (%d:%d)\n", pps->info.name,
MAJOR(pps_devt), pps->id);
@@ -261,12 +340,17 @@
del_cdev:
cdev_del(&pps->cdev);
+free_idr:
+ mutex_lock(&pps_idr_lock);
+ idr_remove(&pps_idr, pps->id);
+ mutex_unlock(&pps_idr_lock);
+
return err;
}
void pps_unregister_cdev(struct pps_device *pps)
{
- device_destroy(pps_class, pps->devno);
+ device_destroy(pps_class, pps->dev->devt);
cdev_del(&pps->cdev);
}
@@ -286,14 +370,14 @@
pps_class = class_create(THIS_MODULE, "pps");
if (!pps_class) {
- printk(KERN_ERR "pps: failed to allocate class\n");
+ pr_err("failed to allocate class\n");
return -ENOMEM;
}
pps_class->dev_attrs = pps_attrs;
err = alloc_chrdev_region(&pps_devt, 0, PPS_MAX_SOURCES, "pps");
if (err < 0) {
- printk(KERN_ERR "pps: failed to allocate char device region\n");
+ pr_err("failed to allocate char device region\n");
goto remove_class;
}
diff --git a/drivers/rapidio/rio-scan.c b/drivers/rapidio/rio-scan.c
index 1eb82c4..467e82b 100644
--- a/drivers/rapidio/rio-scan.c
+++ b/drivers/rapidio/rio-scan.c
@@ -46,7 +46,6 @@
DEFINE_SPINLOCK(rio_global_list_lock);
static int next_destid = 0;
-static int next_switchid = 0;
static int next_net = 0;
static int next_comptag = 1;
@@ -378,12 +377,30 @@
struct rio_dev *rdev;
struct rio_switch *rswitch = NULL;
int result, rdid;
+ size_t size;
+ u32 swpinfo = 0;
- rdev = kzalloc(sizeof(struct rio_dev), GFP_KERNEL);
+ size = sizeof(struct rio_dev);
+ if (rio_mport_read_config_32(port, destid, hopcount,
+ RIO_PEF_CAR, &result))
+ return NULL;
+
+ if (result & (RIO_PEF_SWITCH | RIO_PEF_MULTIPORT)) {
+ rio_mport_read_config_32(port, destid, hopcount,
+ RIO_SWP_INFO_CAR, &swpinfo);
+ if (result & RIO_PEF_SWITCH) {
+ size += (RIO_GET_TOTAL_PORTS(swpinfo) *
+ sizeof(rswitch->nextdev[0])) + sizeof(*rswitch);
+ }
+ }
+
+ rdev = kzalloc(size, GFP_KERNEL);
if (!rdev)
return NULL;
rdev->net = net;
+ rdev->pef = result;
+ rdev->swpinfo = swpinfo;
rio_mport_read_config_32(port, destid, hopcount, RIO_DEV_ID_CAR,
&result);
rdev->did = result >> 16;
@@ -397,8 +414,6 @@
rio_mport_read_config_32(port, destid, hopcount, RIO_ASM_INFO_CAR,
&result);
rdev->asm_rev = result >> 16;
- rio_mport_read_config_32(port, destid, hopcount, RIO_PEF_CAR,
- &rdev->pef);
if (rdev->pef & RIO_PEF_EXT_FEATURES) {
rdev->efptr = result & 0xffff;
rdev->phys_efptr = rio_mport_get_physefb(port, 0, destid,
@@ -408,11 +423,6 @@
hopcount, RIO_EFB_ERR_MGMNT);
}
- if (rdev->pef & (RIO_PEF_SWITCH | RIO_PEF_MULTIPORT)) {
- rio_mport_read_config_32(port, destid, hopcount,
- RIO_SWP_INFO_CAR, &rdev->swpinfo);
- }
-
rio_mport_read_config_32(port, destid, hopcount, RIO_SRC_OPS_CAR,
&rdev->src_ops);
rio_mport_read_config_32(port, destid, hopcount, RIO_DST_OPS_CAR,
@@ -427,6 +437,10 @@
rio_mport_write_config_32(port, destid, hopcount,
RIO_COMPONENT_TAG_CSR, next_comptag);
rdev->comp_tag = next_comptag++;
+ } else {
+ rio_mport_read_config_32(port, destid, hopcount,
+ RIO_COMPONENT_TAG_CSR,
+ &rdev->comp_tag);
}
if (rio_device_has_destid(port, rdev->src_ops, rdev->dst_ops)) {
@@ -437,21 +451,20 @@
next_destid++;
} else
rdev->destid = rio_get_device_id(port, destid, hopcount);
- } else
- /* Switch device has an associated destID */
- rdev->destid = RIO_INVALID_DESTID;
+
+ rdev->hopcount = 0xff;
+ } else {
+ /* Switch device has an associated destID which
+ * will be adjusted later
+ */
+ rdev->destid = destid;
+ rdev->hopcount = hopcount;
+ }
/* If a PE has both switch and other functions, show it as a switch */
if (rio_is_switch(rdev)) {
- rswitch = kzalloc(sizeof(*rswitch) +
- RIO_GET_TOTAL_PORTS(rdev->swpinfo) *
- sizeof(rswitch->nextdev[0]),
- GFP_KERNEL);
- if (!rswitch)
- goto cleanup;
- rswitch->switchid = next_switchid;
- rswitch->hopcount = hopcount;
- rswitch->destid = destid;
+ rswitch = rdev->rswitch;
+ rswitch->switchid = rdev->comp_tag & RIO_CTAG_UDEVID;
rswitch->port_ok = 0;
rswitch->route_table = kzalloc(sizeof(u8)*
RIO_MAX_ROUTE_ENTRIES(port->sys_size),
@@ -462,15 +475,13 @@
for (rdid = 0; rdid < RIO_MAX_ROUTE_ENTRIES(port->sys_size);
rdid++)
rswitch->route_table[rdid] = RIO_INVALID_ROUTE;
- rdev->rswitch = rswitch;
- rswitch->rdev = rdev;
dev_set_name(&rdev->dev, "%02x:s:%04x", rdev->net->id,
- rdev->rswitch->switchid);
+ rswitch->switchid);
rio_switch_init(rdev, do_enum);
- if (do_enum && rdev->rswitch->clr_table)
- rdev->rswitch->clr_table(port, destid, hopcount,
- RIO_GLOBAL_TABLE);
+ if (do_enum && rswitch->clr_table)
+ rswitch->clr_table(port, destid, hopcount,
+ RIO_GLOBAL_TABLE);
list_add_tail(&rswitch->node, &rio_switches);
@@ -506,10 +517,9 @@
return rdev;
cleanup:
- if (rswitch) {
+ if (rswitch->route_table)
kfree(rswitch->route_table);
- kfree(rswitch);
- }
+
kfree(rdev);
return NULL;
}
@@ -632,8 +642,7 @@
/**
* rio_route_add_entry- Add a route entry to a switch routing table
- * @mport: Master port to send transaction
- * @rswitch: Switch device
+ * @rdev: RIO device
* @table: Routing table ID
* @route_destid: Destination ID to be routed
* @route_port: Port number to be routed
@@ -647,31 +656,31 @@
* on failure.
*/
static int
-rio_route_add_entry(struct rio_mport *mport, struct rio_switch *rswitch,
+rio_route_add_entry(struct rio_dev *rdev,
u16 table, u16 route_destid, u8 route_port, int lock)
{
int rc;
if (lock) {
- rc = rio_lock_device(mport, rswitch->destid,
- rswitch->hopcount, 1000);
+ rc = rio_lock_device(rdev->net->hport, rdev->destid,
+ rdev->hopcount, 1000);
if (rc)
return rc;
}
- rc = rswitch->add_entry(mport, rswitch->destid,
- rswitch->hopcount, table,
- route_destid, route_port);
+ rc = rdev->rswitch->add_entry(rdev->net->hport, rdev->destid,
+ rdev->hopcount, table,
+ route_destid, route_port);
if (lock)
- rio_unlock_device(mport, rswitch->destid, rswitch->hopcount);
+ rio_unlock_device(rdev->net->hport, rdev->destid,
+ rdev->hopcount);
return rc;
}
/**
* rio_route_get_entry- Read a route entry in a switch routing table
- * @mport: Master port to send transaction
- * @rswitch: Switch device
+ * @rdev: RIO device
* @table: Routing table ID
* @route_destid: Destination ID to be routed
* @route_port: Pointer to read port number into
@@ -685,23 +694,24 @@
* on failure.
*/
static int
-rio_route_get_entry(struct rio_mport *mport, struct rio_switch *rswitch, u16 table,
+rio_route_get_entry(struct rio_dev *rdev, u16 table,
u16 route_destid, u8 *route_port, int lock)
{
int rc;
if (lock) {
- rc = rio_lock_device(mport, rswitch->destid,
- rswitch->hopcount, 1000);
+ rc = rio_lock_device(rdev->net->hport, rdev->destid,
+ rdev->hopcount, 1000);
if (rc)
return rc;
}
- rc = rswitch->get_entry(mport, rswitch->destid,
- rswitch->hopcount, table,
- route_destid, route_port);
+ rc = rdev->rswitch->get_entry(rdev->net->hport, rdev->destid,
+ rdev->hopcount, table,
+ route_destid, route_port);
if (lock)
- rio_unlock_device(mport, rswitch->destid, rswitch->hopcount);
+ rio_unlock_device(rdev->net->hport, rdev->destid,
+ rdev->hopcount);
return rc;
}
@@ -809,16 +819,15 @@
return -1;
if (rio_is_switch(rdev)) {
- next_switchid++;
sw_inport = RIO_GET_PORT_NUM(rdev->swpinfo);
- rio_route_add_entry(port, rdev->rswitch, RIO_GLOBAL_TABLE,
+ rio_route_add_entry(rdev, RIO_GLOBAL_TABLE,
port->host_deviceid, sw_inport, 0);
rdev->rswitch->route_table[port->host_deviceid] = sw_inport;
for (destid = 0; destid < next_destid; destid++) {
if (destid == port->host_deviceid)
continue;
- rio_route_add_entry(port, rdev->rswitch, RIO_GLOBAL_TABLE,
+ rio_route_add_entry(rdev, RIO_GLOBAL_TABLE,
destid, sw_inport, 0);
rdev->rswitch->route_table[destid] = sw_inport;
}
@@ -850,8 +859,7 @@
"RIO: scanning device on port %d\n",
port_num);
rdev->rswitch->port_ok |= (1 << port_num);
- rio_route_add_entry(port, rdev->rswitch,
- RIO_GLOBAL_TABLE,
+ rio_route_add_entry(rdev, RIO_GLOBAL_TABLE,
RIO_ANY_DESTID(port->sys_size),
port_num, 0);
@@ -865,7 +873,7 @@
destid < next_destid; destid++) {
if (destid == port->host_deviceid)
continue;
- rio_route_add_entry(port, rdev->rswitch,
+ rio_route_add_entry(rdev,
RIO_GLOBAL_TABLE,
destid,
port_num,
@@ -904,7 +912,7 @@
next_destid++;
}
- rdev->rswitch->destid = sw_destid;
+ rdev->destid = sw_destid;
} else
pr_debug("RIO: found %s (vid %4.4x did %4.4x)\n",
rio_name(rdev), rdev->vid, rdev->did);
@@ -941,7 +949,7 @@
*/
static int __devinit
rio_disc_peer(struct rio_net *net, struct rio_mport *port, u16 destid,
- u8 hopcount)
+ u8 hopcount, struct rio_dev *prev, int prev_port)
{
u8 port_num, route_port;
struct rio_dev *rdev;
@@ -951,14 +959,15 @@
if ((rdev = rio_setup_device(net, port, destid, hopcount, 0))) {
/* Add device to the global and bus/net specific list. */
list_add_tail(&rdev->net_list, &net->devices);
+ rdev->prev = prev;
+ if (prev && rio_is_switch(prev))
+ prev->rswitch->nextdev[prev_port] = rdev;
} else
return -1;
if (rio_is_switch(rdev)) {
- next_switchid++;
-
/* Associated destid is how we accessed this switch */
- rdev->rswitch->destid = destid;
+ rdev->destid = destid;
pr_debug(
"RIO: found %s (vid %4.4x did %4.4x) with %d ports\n",
@@ -981,7 +990,7 @@
for (ndestid = 0;
ndestid < RIO_ANY_DESTID(port->sys_size);
ndestid++) {
- rio_route_get_entry(port, rdev->rswitch,
+ rio_route_get_entry(rdev,
RIO_GLOBAL_TABLE,
ndestid,
&route_port, 0);
@@ -992,8 +1001,8 @@
if (ndestid == RIO_ANY_DESTID(port->sys_size))
continue;
rio_unlock_device(port, destid, hopcount);
- if (rio_disc_peer
- (net, port, ndestid, hopcount + 1) < 0)
+ if (rio_disc_peer(net, port, ndestid,
+ hopcount + 1, rdev, port_num) < 0)
return -1;
}
}
@@ -1069,14 +1078,14 @@
*/
static void rio_update_route_tables(struct rio_mport *port)
{
- struct rio_dev *rdev;
+ struct rio_dev *rdev, *swrdev;
struct rio_switch *rswitch;
u8 sport;
u16 destid;
list_for_each_entry(rdev, &rio_devices, global_list) {
- destid = (rio_is_switch(rdev))?rdev->rswitch->destid:rdev->destid;
+ destid = rdev->destid;
list_for_each_entry(rswitch, &rio_switches, node) {
@@ -1084,14 +1093,16 @@
continue;
if (RIO_INVALID_ROUTE == rswitch->route_table[destid]) {
+ swrdev = sw_to_rio_dev(rswitch);
+
/* Skip if destid ends in empty switch*/
- if (rswitch->destid == destid)
+ if (swrdev->destid == destid)
continue;
- sport = RIO_GET_PORT_NUM(rswitch->rdev->swpinfo);
+ sport = RIO_GET_PORT_NUM(swrdev->swpinfo);
if (rswitch->add_entry) {
- rio_route_add_entry(port, rswitch,
+ rio_route_add_entry(swrdev,
RIO_GLOBAL_TABLE, destid,
sport, 0);
rswitch->route_table[destid] = sport;
@@ -1203,21 +1214,20 @@
list_for_each_entry(rdev, &rio_devices, global_list)
if (rio_is_switch(rdev)) {
- rio_lock_device(rdev->net->hport, rdev->rswitch->destid,
- rdev->rswitch->hopcount, 1000);
+ rio_lock_device(rdev->net->hport, rdev->destid,
+ rdev->hopcount, 1000);
for (i = 0;
i < RIO_MAX_ROUTE_ENTRIES(rdev->net->hport->sys_size);
i++) {
- if (rio_route_get_entry
- (rdev->net->hport, rdev->rswitch,
- RIO_GLOBAL_TABLE, i, &sport, 0) < 0)
+ if (rio_route_get_entry(rdev,
+ RIO_GLOBAL_TABLE, i, &sport, 0) < 0)
continue;
rdev->rswitch->route_table[i] = sport;
}
rio_unlock_device(rdev->net->hport,
- rdev->rswitch->destid,
- rdev->rswitch->hopcount);
+ rdev->destid,
+ rdev->hopcount);
}
}
@@ -1284,7 +1294,7 @@
mport->host_deviceid);
if (rio_disc_peer(net, mport, RIO_ANY_DESTID(mport->sys_size),
- 0) < 0) {
+ 0, NULL, 0) < 0) {
printk(KERN_INFO
"RIO: master port %d device has failed discovery\n",
mport->id);
diff --git a/drivers/rapidio/rio-sysfs.c b/drivers/rapidio/rio-sysfs.c
index 137ed93..76b4185 100644
--- a/drivers/rapidio/rio-sysfs.c
+++ b/drivers/rapidio/rio-sysfs.c
@@ -217,7 +217,7 @@
err = device_create_bin_file(&rdev->dev, &rio_config_attr);
- if (!err && rdev->rswitch) {
+ if (!err && (rdev->pef & RIO_PEF_SWITCH)) {
err = device_create_file(&rdev->dev, &dev_attr_routes);
if (!err && rdev->rswitch->sw_sysfs)
err = rdev->rswitch->sw_sysfs(rdev, RIO_SW_SYSFS_CREATE);
@@ -239,7 +239,7 @@
void rio_remove_sysfs_dev_files(struct rio_dev *rdev)
{
device_remove_bin_file(&rdev->dev, &rio_config_attr);
- if (rdev->rswitch) {
+ if (rdev->pef & RIO_PEF_SWITCH) {
device_remove_file(&rdev->dev, &dev_attr_routes);
if (rdev->rswitch->sw_sysfs)
rdev->rswitch->sw_sysfs(rdev, RIO_SW_SYSFS_REMOVE);
diff --git a/drivers/rapidio/rio.c b/drivers/rapidio/rio.c
index 7b5080c..cc2a3b7 100644
--- a/drivers/rapidio/rio.c
+++ b/drivers/rapidio/rio.c
@@ -471,16 +471,9 @@
*/
int rio_set_port_lockout(struct rio_dev *rdev, u32 pnum, int lock)
{
- u8 hopcount = 0xff;
- u16 destid = rdev->destid;
u32 regval;
- if (rdev->rswitch) {
- destid = rdev->rswitch->destid;
- hopcount = rdev->rswitch->hopcount;
- }
-
- rio_mport_read_config_32(rdev->net->hport, destid, hopcount,
+ rio_read_config_32(rdev,
rdev->phys_efptr + RIO_PORT_N_CTL_CSR(pnum),
®val);
if (lock)
@@ -488,7 +481,7 @@
else
regval &= ~RIO_PORT_N_CTL_LOCKOUT;
- rio_mport_write_config_32(rdev->net->hport, destid, hopcount,
+ rio_write_config_32(rdev,
rdev->phys_efptr + RIO_PORT_N_CTL_CSR(pnum),
regval);
return 0;
@@ -507,7 +500,7 @@
rio_chk_dev_route(struct rio_dev *rdev, struct rio_dev **nrdev, int *npnum)
{
u32 result;
- int p_port, dstid, rc = -EIO;
+ int p_port, rc = -EIO;
struct rio_dev *prev = NULL;
/* Find switch with failed RIO link */
@@ -522,9 +515,7 @@
if (prev == NULL)
goto err_out;
- dstid = (rdev->pef & RIO_PEF_SWITCH) ?
- rdev->rswitch->destid : rdev->destid;
- p_port = prev->rswitch->route_table[dstid];
+ p_port = prev->rswitch->route_table[rdev->destid];
if (p_port != RIO_INVALID_ROUTE) {
pr_debug("RIO: link failed on [%s]-P%d\n",
@@ -567,15 +558,8 @@
*/
static int rio_chk_dev_access(struct rio_dev *rdev)
{
- u8 hopcount = 0xff;
- u16 destid = rdev->destid;
-
- if (rdev->rswitch) {
- destid = rdev->rswitch->destid;
- hopcount = rdev->rswitch->hopcount;
- }
-
- return rio_mport_chk_dev_access(rdev->net->hport, destid, hopcount);
+ return rio_mport_chk_dev_access(rdev->net->hport,
+ rdev->destid, rdev->hopcount);
}
/**
@@ -588,23 +572,20 @@
static int
rio_get_input_status(struct rio_dev *rdev, int pnum, u32 *lnkresp)
{
- struct rio_mport *mport = rdev->net->hport;
- u16 destid = rdev->rswitch->destid;
- u8 hopcount = rdev->rswitch->hopcount;
u32 regval;
int checkcount;
if (lnkresp) {
/* Read from link maintenance response register
* to clear valid bit */
- rio_mport_read_config_32(mport, destid, hopcount,
+ rio_read_config_32(rdev,
rdev->phys_efptr + RIO_PORT_N_MNT_RSP_CSR(pnum),
®val);
udelay(50);
}
/* Issue Input-status command */
- rio_mport_write_config_32(mport, destid, hopcount,
+ rio_write_config_32(rdev,
rdev->phys_efptr + RIO_PORT_N_MNT_REQ_CSR(pnum),
RIO_MNT_REQ_CMD_IS);
@@ -615,7 +596,7 @@
checkcount = 3;
while (checkcount--) {
udelay(50);
- rio_mport_read_config_32(mport, destid, hopcount,
+ rio_read_config_32(rdev,
rdev->phys_efptr + RIO_PORT_N_MNT_RSP_CSR(pnum),
®val);
if (regval & RIO_PORT_N_MNT_RSP_RVAL) {
@@ -635,15 +616,12 @@
*/
static int rio_clr_err_stopped(struct rio_dev *rdev, u32 pnum, u32 err_status)
{
- struct rio_mport *mport = rdev->net->hport;
- u16 destid = rdev->rswitch->destid;
- u8 hopcount = rdev->rswitch->hopcount;
struct rio_dev *nextdev = rdev->rswitch->nextdev[pnum];
u32 regval;
u32 far_ackid, far_linkstat, near_ackid;
if (err_status == 0)
- rio_mport_read_config_32(mport, destid, hopcount,
+ rio_read_config_32(rdev,
rdev->phys_efptr + RIO_PORT_N_ERR_STS_CSR(pnum),
&err_status);
@@ -661,7 +639,7 @@
pnum, regval);
far_ackid = (regval & RIO_PORT_N_MNT_RSP_ASTAT) >> 5;
far_linkstat = regval & RIO_PORT_N_MNT_RSP_LSTAT;
- rio_mport_read_config_32(mport, destid, hopcount,
+ rio_read_config_32(rdev,
rdev->phys_efptr + RIO_PORT_N_ACK_STS_CSR(pnum),
®val);
pr_debug("RIO_EM: SP%d_ACK_STS_CSR=0x%08x\n", pnum, regval);
@@ -679,9 +657,8 @@
/* Align near outstanding/outbound ackIDs with
* far inbound.
*/
- rio_mport_write_config_32(mport, destid,
- hopcount, rdev->phys_efptr +
- RIO_PORT_N_ACK_STS_CSR(pnum),
+ rio_write_config_32(rdev,
+ rdev->phys_efptr + RIO_PORT_N_ACK_STS_CSR(pnum),
(near_ackid << 24) |
(far_ackid << 8) | far_ackid);
/* Align far outstanding/outbound ackIDs with
@@ -698,7 +675,7 @@
pr_debug("RIO_EM: Invalid nextdev pointer (NULL)\n");
}
rd_err:
- rio_mport_read_config_32(mport, destid, hopcount,
+ rio_read_config_32(rdev,
rdev->phys_efptr + RIO_PORT_N_ERR_STS_CSR(pnum),
&err_status);
pr_debug("RIO_EM: SP%d_ERR_STS_CSR=0x%08x\n", pnum, err_status);
@@ -710,7 +687,7 @@
RIO_GET_PORT_NUM(nextdev->swpinfo), NULL);
udelay(50);
- rio_mport_read_config_32(mport, destid, hopcount,
+ rio_read_config_32(rdev,
rdev->phys_efptr + RIO_PORT_N_ERR_STS_CSR(pnum),
&err_status);
pr_debug("RIO_EM: SP%d_ERR_STS_CSR=0x%08x\n", pnum, err_status);
@@ -730,13 +707,10 @@
int rio_inb_pwrite_handler(union rio_pw_msg *pw_msg)
{
struct rio_dev *rdev;
- struct rio_mport *mport;
- u8 hopcount;
- u16 destid;
u32 err_status, em_perrdet, em_ltlerrdet;
int rc, portnum;
- rdev = rio_get_comptag(pw_msg->em.comptag, NULL);
+ rdev = rio_get_comptag((pw_msg->em.comptag & RIO_CTAG_UDEVID), NULL);
if (rdev == NULL) {
/* Device removed or enumeration error */
pr_debug("RIO: %s No matching device for CTag 0x%08x\n",
@@ -800,17 +774,13 @@
return 0;
}
- mport = rdev->net->hport;
- destid = rdev->rswitch->destid;
- hopcount = rdev->rswitch->hopcount;
-
/*
* Process the port-write notification from switch
*/
if (rdev->rswitch->em_handle)
rdev->rswitch->em_handle(rdev, portnum);
- rio_mport_read_config_32(mport, destid, hopcount,
+ rio_read_config_32(rdev,
rdev->phys_efptr + RIO_PORT_N_ERR_STS_CSR(portnum),
&err_status);
pr_debug("RIO_PW: SP%d_ERR_STS_CSR=0x%08x\n", portnum, err_status);
@@ -840,7 +810,7 @@
rdev->rswitch->port_ok &= ~(1 << portnum);
rio_set_port_lockout(rdev, portnum, 1);
- rio_mport_write_config_32(mport, destid, hopcount,
+ rio_write_config_32(rdev,
rdev->phys_efptr +
RIO_PORT_N_ACK_STS_CSR(portnum),
RIO_PORT_N_ACK_CLEAR);
@@ -851,28 +821,28 @@
}
}
- rio_mport_read_config_32(mport, destid, hopcount,
+ rio_read_config_32(rdev,
rdev->em_efptr + RIO_EM_PN_ERR_DETECT(portnum), &em_perrdet);
if (em_perrdet) {
pr_debug("RIO_PW: RIO_EM_P%d_ERR_DETECT=0x%08x\n",
portnum, em_perrdet);
/* Clear EM Port N Error Detect CSR */
- rio_mport_write_config_32(mport, destid, hopcount,
+ rio_write_config_32(rdev,
rdev->em_efptr + RIO_EM_PN_ERR_DETECT(portnum), 0);
}
- rio_mport_read_config_32(mport, destid, hopcount,
+ rio_read_config_32(rdev,
rdev->em_efptr + RIO_EM_LTL_ERR_DETECT, &em_ltlerrdet);
if (em_ltlerrdet) {
pr_debug("RIO_PW: RIO_EM_LTL_ERR_DETECT=0x%08x\n",
em_ltlerrdet);
/* Clear EM L/T Layer Error Detect CSR */
- rio_mport_write_config_32(mport, destid, hopcount,
+ rio_write_config_32(rdev,
rdev->em_efptr + RIO_EM_LTL_ERR_DETECT, 0);
}
/* Clear remaining error bits and Port-Write Pending bit */
- rio_mport_write_config_32(mport, destid, hopcount,
+ rio_write_config_32(rdev,
rdev->phys_efptr + RIO_PORT_N_ERR_STS_CSR(portnum),
err_status);
diff --git a/drivers/rapidio/switches/idt_gen2.c b/drivers/rapidio/switches/idt_gen2.c
index 0bb871c..095016a 100644
--- a/drivers/rapidio/switches/idt_gen2.c
+++ b/drivers/rapidio/switches/idt_gen2.c
@@ -209,9 +209,6 @@
static int
idtg2_em_init(struct rio_dev *rdev)
{
- struct rio_mport *mport = rdev->net->hport;
- u16 destid = rdev->rswitch->destid;
- u8 hopcount = rdev->rswitch->hopcount;
u32 regval;
int i, tmp;
@@ -220,29 +217,25 @@
* All standard EM configuration should be performed at upper level.
*/
- pr_debug("RIO: %s [%d:%d]\n", __func__, destid, hopcount);
+ pr_debug("RIO: %s [%d:%d]\n", __func__, rdev->destid, rdev->hopcount);
/* Set Port-Write info CSR: PRIO=3 and CRF=1 */
- rio_mport_write_config_32(mport, destid, hopcount,
- IDT_PW_INFO_CSR, 0x0000e000);
+ rio_write_config_32(rdev, IDT_PW_INFO_CSR, 0x0000e000);
/*
* Configure LT LAYER error reporting.
*/
/* Enable standard (RIO.p8) error reporting */
- rio_mport_write_config_32(mport, destid, hopcount,
- IDT_LT_ERR_REPORT_EN,
+ rio_write_config_32(rdev, IDT_LT_ERR_REPORT_EN,
REM_LTL_ERR_ILLTRAN | REM_LTL_ERR_UNSOLR |
REM_LTL_ERR_UNSUPTR);
/* Use Port-Writes for LT layer error reporting.
* Enable per-port reset
*/
- rio_mport_read_config_32(mport, destid, hopcount,
- IDT_DEV_CTRL_1, ®val);
- rio_mport_write_config_32(mport, destid, hopcount,
- IDT_DEV_CTRL_1,
+ rio_read_config_32(rdev, IDT_DEV_CTRL_1, ®val);
+ rio_write_config_32(rdev, IDT_DEV_CTRL_1,
regval | IDT_DEV_CTRL_1_GENPW | IDT_DEV_CTRL_1_PRSTBEH);
/*
@@ -250,45 +243,40 @@
*/
/* Report all RIO.p8 errors supported by device */
- rio_mport_write_config_32(mport, destid, hopcount,
- IDT_PORT_ERR_REPORT_EN_BC, 0x807e8037);
+ rio_write_config_32(rdev, IDT_PORT_ERR_REPORT_EN_BC, 0x807e8037);
/* Configure reporting of implementation specific errors/events */
- rio_mport_write_config_32(mport, destid, hopcount,
- IDT_PORT_ISERR_REPORT_EN_BC, IDT_PORT_INIT_TX_ACQUIRED);
+ rio_write_config_32(rdev, IDT_PORT_ISERR_REPORT_EN_BC,
+ IDT_PORT_INIT_TX_ACQUIRED);
/* Use Port-Writes for port error reporting and enable error logging */
tmp = RIO_GET_TOTAL_PORTS(rdev->swpinfo);
for (i = 0; i < tmp; i++) {
- rio_mport_read_config_32(mport, destid, hopcount,
- IDT_PORT_OPS(i), ®val);
- rio_mport_write_config_32(mport, destid, hopcount,
+ rio_read_config_32(rdev, IDT_PORT_OPS(i), ®val);
+ rio_write_config_32(rdev,
IDT_PORT_OPS(i), regval | IDT_PORT_OPS_GENPW |
IDT_PORT_OPS_PL_ELOG |
IDT_PORT_OPS_LL_ELOG |
IDT_PORT_OPS_LT_ELOG);
}
/* Overwrite error log if full */
- rio_mport_write_config_32(mport, destid, hopcount,
- IDT_ERR_CAP, IDT_ERR_CAP_LOG_OVERWR);
+ rio_write_config_32(rdev, IDT_ERR_CAP, IDT_ERR_CAP_LOG_OVERWR);
/*
* Configure LANE error reporting.
*/
/* Disable line error reporting */
- rio_mport_write_config_32(mport, destid, hopcount,
- IDT_LANE_ERR_REPORT_EN_BC, 0);
+ rio_write_config_32(rdev, IDT_LANE_ERR_REPORT_EN_BC, 0);
/* Use Port-Writes for lane error reporting (when enabled)
* (do per-lane update because lanes may have different configuration)
*/
tmp = (rdev->did == RIO_DID_IDTCPS1848) ? 48 : 16;
for (i = 0; i < tmp; i++) {
- rio_mport_read_config_32(mport, destid, hopcount,
- IDT_LANE_CTRL(i), ®val);
- rio_mport_write_config_32(mport, destid, hopcount,
- IDT_LANE_CTRL(i), regval | IDT_LANE_CTRL_GENPW);
+ rio_read_config_32(rdev, IDT_LANE_CTRL(i), ®val);
+ rio_write_config_32(rdev, IDT_LANE_CTRL(i),
+ regval | IDT_LANE_CTRL_GENPW);
}
/*
@@ -296,41 +284,32 @@
*/
/* Disable JTAG and I2C Error capture */
- rio_mport_write_config_32(mport, destid, hopcount,
- IDT_AUX_PORT_ERR_CAP_EN, 0);
+ rio_write_config_32(rdev, IDT_AUX_PORT_ERR_CAP_EN, 0);
/* Disable JTAG and I2C Error reporting/logging */
- rio_mport_write_config_32(mport, destid, hopcount,
- IDT_AUX_ERR_REPORT_EN, 0);
+ rio_write_config_32(rdev, IDT_AUX_ERR_REPORT_EN, 0);
/* Disable Port-Write notification from JTAG */
- rio_mport_write_config_32(mport, destid, hopcount,
- IDT_JTAG_CTRL, 0);
+ rio_write_config_32(rdev, IDT_JTAG_CTRL, 0);
/* Disable Port-Write notification from I2C */
- rio_mport_read_config_32(mport, destid, hopcount,
- IDT_I2C_MCTRL, ®val);
- rio_mport_write_config_32(mport, destid, hopcount,
- IDT_I2C_MCTRL,
- regval & ~IDT_I2C_MCTRL_GENPW);
+ rio_read_config_32(rdev, IDT_I2C_MCTRL, ®val);
+ rio_write_config_32(rdev, IDT_I2C_MCTRL, regval & ~IDT_I2C_MCTRL_GENPW);
/*
* Configure CFG_BLK error reporting.
*/
/* Disable Configuration Block error capture */
- rio_mport_write_config_32(mport, destid, hopcount,
- IDT_CFGBLK_ERR_CAPTURE_EN, 0);
+ rio_write_config_32(rdev, IDT_CFGBLK_ERR_CAPTURE_EN, 0);
/* Disable Port-Writes for Configuration Block error reporting */
- rio_mport_read_config_32(mport, destid, hopcount,
- IDT_CFGBLK_ERR_REPORT, ®val);
- rio_mport_write_config_32(mport, destid, hopcount,
- IDT_CFGBLK_ERR_REPORT,
- regval & ~IDT_CFGBLK_ERR_REPORT_GENPW);
+ rio_read_config_32(rdev, IDT_CFGBLK_ERR_REPORT, ®val);
+ rio_write_config_32(rdev, IDT_CFGBLK_ERR_REPORT,
+ regval & ~IDT_CFGBLK_ERR_REPORT_GENPW);
/* set TVAL = ~50us */
- rio_mport_write_config_32(mport, destid, hopcount,
+ rio_write_config_32(rdev,
rdev->phys_efptr + RIO_PORT_LINKTO_CTL_CSR, 0x8e << 8);
return 0;
@@ -339,18 +318,15 @@
static int
idtg2_em_handler(struct rio_dev *rdev, u8 portnum)
{
- struct rio_mport *mport = rdev->net->hport;
- u16 destid = rdev->rswitch->destid;
- u8 hopcount = rdev->rswitch->hopcount;
u32 regval, em_perrdet, em_ltlerrdet;
- rio_mport_read_config_32(mport, destid, hopcount,
+ rio_read_config_32(rdev,
rdev->em_efptr + RIO_EM_LTL_ERR_DETECT, &em_ltlerrdet);
if (em_ltlerrdet) {
/* Service Logical/Transport Layer Error(s) */
if (em_ltlerrdet & REM_LTL_ERR_IMPSPEC) {
/* Implementation specific error reported */
- rio_mport_read_config_32(mport, destid, hopcount,
+ rio_read_config_32(rdev,
IDT_ISLTL_ADDRESS_CAP, ®val);
pr_debug("RIO: %s Implementation Specific LTL errors" \
@@ -358,13 +334,12 @@
rio_name(rdev), em_ltlerrdet, regval);
/* Clear implementation specific address capture CSR */
- rio_mport_write_config_32(mport, destid, hopcount,
- IDT_ISLTL_ADDRESS_CAP, 0);
+ rio_write_config_32(rdev, IDT_ISLTL_ADDRESS_CAP, 0);
}
}
- rio_mport_read_config_32(mport, destid, hopcount,
+ rio_read_config_32(rdev,
rdev->em_efptr + RIO_EM_PN_ERR_DETECT(portnum), &em_perrdet);
if (em_perrdet) {
/* Service Port-Level Error(s) */
@@ -372,14 +347,14 @@
/* Implementation Specific port error reported */
/* Get IS errors reported */
- rio_mport_read_config_32(mport, destid, hopcount,
+ rio_read_config_32(rdev,
IDT_PORT_ISERR_DET(portnum), ®val);
pr_debug("RIO: %s Implementation Specific Port" \
" errors 0x%x\n", rio_name(rdev), regval);
/* Clear all implementation specific events */
- rio_mport_write_config_32(mport, destid, hopcount,
+ rio_write_config_32(rdev,
IDT_PORT_ISERR_DET(portnum), 0);
}
}
@@ -391,14 +366,10 @@
idtg2_show_errlog(struct device *dev, struct device_attribute *attr, char *buf)
{
struct rio_dev *rdev = to_rio_dev(dev);
- struct rio_mport *mport = rdev->net->hport;
- u16 destid = rdev->rswitch->destid;
- u8 hopcount = rdev->rswitch->hopcount;
ssize_t len = 0;
u32 regval;
- while (!rio_mport_read_config_32(mport, destid, hopcount,
- IDT_ERR_RD, ®val)) {
+ while (!rio_read_config_32(rdev, IDT_ERR_RD, ®val)) {
if (!regval) /* 0 = end of log */
break;
len += snprintf(buf + len, PAGE_SIZE - len,
@@ -445,3 +416,5 @@
DECLARE_RIO_SWITCH_INIT(RIO_VID_IDT, RIO_DID_IDTCPS1848, idtg2_switch_init);
DECLARE_RIO_SWITCH_INIT(RIO_VID_IDT, RIO_DID_IDTCPS1616, idtg2_switch_init);
+DECLARE_RIO_SWITCH_INIT(RIO_VID_IDT, RIO_DID_IDTVPS1616, idtg2_switch_init);
+DECLARE_RIO_SWITCH_INIT(RIO_VID_IDT, RIO_DID_IDTSPS1616, idtg2_switch_init);
diff --git a/drivers/rapidio/switches/idtcps.c b/drivers/rapidio/switches/idtcps.c
index fc9f637..3a97107 100644
--- a/drivers/rapidio/switches/idtcps.c
+++ b/drivers/rapidio/switches/idtcps.c
@@ -117,10 +117,6 @@
static int idtcps_switch_init(struct rio_dev *rdev, int do_enum)
{
- struct rio_mport *mport = rdev->net->hport;
- u16 destid = rdev->rswitch->destid;
- u8 hopcount = rdev->rswitch->hopcount;
-
pr_debug("RIO: %s for %s\n", __func__, rio_name(rdev));
rdev->rswitch->add_entry = idtcps_route_add_entry;
rdev->rswitch->get_entry = idtcps_route_get_entry;
@@ -132,7 +128,7 @@
if (do_enum) {
/* set TVAL = ~50us */
- rio_mport_write_config_32(mport, destid, hopcount,
+ rio_write_config_32(rdev,
rdev->phys_efptr + RIO_PORT_LINKTO_CTL_CSR, 0x8e << 8);
}
diff --git a/drivers/rapidio/switches/tsi568.c b/drivers/rapidio/switches/tsi568.c
index b9a389b..3994c00 100644
--- a/drivers/rapidio/switches/tsi568.c
+++ b/drivers/rapidio/switches/tsi568.c
@@ -113,22 +113,17 @@
static int
tsi568_em_init(struct rio_dev *rdev)
{
- struct rio_mport *mport = rdev->net->hport;
- u16 destid = rdev->rswitch->destid;
- u8 hopcount = rdev->rswitch->hopcount;
u32 regval;
int portnum;
- pr_debug("TSI568 %s [%d:%d]\n", __func__, destid, hopcount);
+ pr_debug("TSI568 %s [%d:%d]\n", __func__, rdev->destid, rdev->hopcount);
/* Make sure that Port-Writes are disabled (for all ports) */
for (portnum = 0;
portnum < RIO_GET_TOTAL_PORTS(rdev->swpinfo); portnum++) {
- rio_mport_read_config_32(mport, destid, hopcount,
- TSI568_SP_MODE(portnum), ®val);
- rio_mport_write_config_32(mport, destid, hopcount,
- TSI568_SP_MODE(portnum),
- regval | TSI568_SP_MODE_PW_DIS);
+ rio_read_config_32(rdev, TSI568_SP_MODE(portnum), ®val);
+ rio_write_config_32(rdev, TSI568_SP_MODE(portnum),
+ regval | TSI568_SP_MODE_PW_DIS);
}
return 0;
diff --git a/drivers/rapidio/switches/tsi57x.c b/drivers/rapidio/switches/tsi57x.c
index 2003fb6..1a62934 100644
--- a/drivers/rapidio/switches/tsi57x.c
+++ b/drivers/rapidio/switches/tsi57x.c
@@ -158,48 +158,45 @@
static int
tsi57x_em_init(struct rio_dev *rdev)
{
- struct rio_mport *mport = rdev->net->hport;
- u16 destid = rdev->rswitch->destid;
- u8 hopcount = rdev->rswitch->hopcount;
u32 regval;
int portnum;
- pr_debug("TSI578 %s [%d:%d]\n", __func__, destid, hopcount);
+ pr_debug("TSI578 %s [%d:%d]\n", __func__, rdev->destid, rdev->hopcount);
for (portnum = 0;
portnum < RIO_GET_TOTAL_PORTS(rdev->swpinfo); portnum++) {
/* Make sure that Port-Writes are enabled (for all ports) */
- rio_mport_read_config_32(mport, destid, hopcount,
+ rio_read_config_32(rdev,
TSI578_SP_MODE(portnum), ®val);
- rio_mport_write_config_32(mport, destid, hopcount,
+ rio_write_config_32(rdev,
TSI578_SP_MODE(portnum),
regval & ~TSI578_SP_MODE_PW_DIS);
/* Clear all pending interrupts */
- rio_mport_read_config_32(mport, destid, hopcount,
+ rio_read_config_32(rdev,
rdev->phys_efptr +
RIO_PORT_N_ERR_STS_CSR(portnum),
®val);
- rio_mport_write_config_32(mport, destid, hopcount,
+ rio_write_config_32(rdev,
rdev->phys_efptr +
RIO_PORT_N_ERR_STS_CSR(portnum),
regval & 0x07120214);
- rio_mport_read_config_32(mport, destid, hopcount,
+ rio_read_config_32(rdev,
TSI578_SP_INT_STATUS(portnum), ®val);
- rio_mport_write_config_32(mport, destid, hopcount,
+ rio_write_config_32(rdev,
TSI578_SP_INT_STATUS(portnum),
regval & 0x000700bd);
/* Enable all interrupts to allow ports to send a port-write */
- rio_mport_read_config_32(mport, destid, hopcount,
+ rio_read_config_32(rdev,
TSI578_SP_CTL_INDEP(portnum), ®val);
- rio_mport_write_config_32(mport, destid, hopcount,
+ rio_write_config_32(rdev,
TSI578_SP_CTL_INDEP(portnum),
regval | 0x000b0000);
/* Skip next (odd) port if the current port is in x4 mode */
- rio_mport_read_config_32(mport, destid, hopcount,
+ rio_read_config_32(rdev,
rdev->phys_efptr + RIO_PORT_N_CTL_CSR(portnum),
®val);
if ((regval & RIO_PORT_N_CTL_PWIDTH) == RIO_PORT_N_CTL_PWIDTH_4)
@@ -207,7 +204,7 @@
}
/* set TVAL = ~50us */
- rio_mport_write_config_32(mport, destid, hopcount,
+ rio_write_config_32(rdev,
rdev->phys_efptr + RIO_PORT_LINKTO_CTL_CSR, 0x9a << 8);
return 0;
@@ -217,14 +214,12 @@
tsi57x_em_handler(struct rio_dev *rdev, u8 portnum)
{
struct rio_mport *mport = rdev->net->hport;
- u16 destid = rdev->rswitch->destid;
- u8 hopcount = rdev->rswitch->hopcount;
u32 intstat, err_status;
int sendcount, checkcount;
u8 route_port;
u32 regval;
- rio_mport_read_config_32(mport, destid, hopcount,
+ rio_read_config_32(rdev,
rdev->phys_efptr + RIO_PORT_N_ERR_STS_CSR(portnum),
&err_status);
@@ -232,15 +227,15 @@
(err_status & (RIO_PORT_N_ERR_STS_PW_OUT_ES |
RIO_PORT_N_ERR_STS_PW_INP_ES))) {
/* Remove any queued packets by locking/unlocking port */
- rio_mport_read_config_32(mport, destid, hopcount,
+ rio_read_config_32(rdev,
rdev->phys_efptr + RIO_PORT_N_CTL_CSR(portnum),
®val);
if (!(regval & RIO_PORT_N_CTL_LOCKOUT)) {
- rio_mport_write_config_32(mport, destid, hopcount,
+ rio_write_config_32(rdev,
rdev->phys_efptr + RIO_PORT_N_CTL_CSR(portnum),
regval | RIO_PORT_N_CTL_LOCKOUT);
udelay(50);
- rio_mport_write_config_32(mport, destid, hopcount,
+ rio_write_config_32(rdev,
rdev->phys_efptr + RIO_PORT_N_CTL_CSR(portnum),
regval);
}
@@ -248,7 +243,7 @@
/* Read from link maintenance response register to clear
* valid bit
*/
- rio_mport_read_config_32(mport, destid, hopcount,
+ rio_read_config_32(rdev,
rdev->phys_efptr + RIO_PORT_N_MNT_RSP_CSR(portnum),
®val);
@@ -257,13 +252,12 @@
*/
sendcount = 3;
while (sendcount) {
- rio_mport_write_config_32(mport, destid, hopcount,
+ rio_write_config_32(rdev,
TSI578_SP_CS_TX(portnum), 0x40fc8000);
checkcount = 3;
while (checkcount--) {
udelay(50);
- rio_mport_read_config_32(
- mport, destid, hopcount,
+ rio_read_config_32(rdev,
rdev->phys_efptr +
RIO_PORT_N_MNT_RSP_CSR(portnum),
®val);
@@ -277,25 +271,23 @@
exit_es:
/* Clear implementation specific error status bits */
- rio_mport_read_config_32(mport, destid, hopcount,
- TSI578_SP_INT_STATUS(portnum), &intstat);
+ rio_read_config_32(rdev, TSI578_SP_INT_STATUS(portnum), &intstat);
pr_debug("TSI578[%x:%x] SP%d_INT_STATUS=0x%08x\n",
- destid, hopcount, portnum, intstat);
+ rdev->destid, rdev->hopcount, portnum, intstat);
if (intstat & 0x10000) {
- rio_mport_read_config_32(mport, destid, hopcount,
+ rio_read_config_32(rdev,
TSI578_SP_LUT_PEINF(portnum), ®val);
regval = (mport->sys_size) ? (regval >> 16) : (regval >> 24);
route_port = rdev->rswitch->route_table[regval];
pr_debug("RIO: TSI578[%s] P%d LUT Parity Error (destID=%d)\n",
rio_name(rdev), portnum, regval);
- tsi57x_route_add_entry(mport, destid, hopcount,
+ tsi57x_route_add_entry(mport, rdev->destid, rdev->hopcount,
RIO_GLOBAL_TABLE, regval, route_port);
}
- rio_mport_write_config_32(mport, destid, hopcount,
- TSI578_SP_INT_STATUS(portnum),
- intstat & 0x000700bd);
+ rio_write_config_32(rdev, TSI578_SP_INT_STATUS(portnum),
+ intstat & 0x000700bd);
return 0;
}
diff --git a/drivers/regulator/88pm8607.c b/drivers/regulator/88pm8607.c
index 2ce2eb7..dd63084 100644
--- a/drivers/regulator/88pm8607.c
+++ b/drivers/regulator/88pm8607.c
@@ -249,7 +249,7 @@
}
static int pm8607_set_voltage(struct regulator_dev *rdev,
- int min_uV, int max_uV)
+ int min_uV, int max_uV, unsigned *selector)
{
struct pm8607_regulator_info *info = rdev_get_drvdata(rdev);
uint8_t val, mask;
@@ -263,6 +263,7 @@
ret = choose_voltage(rdev, min_uV, max_uV);
if (ret < 0)
return -EINVAL;
+ *selector = ret;
val = (uint8_t)(ret << info->vol_shift);
mask = ((1 << info->vol_nbits) - 1) << info->vol_shift;
diff --git a/drivers/regulator/Kconfig b/drivers/regulator/Kconfig
index dd30e88..e1d9436 100644
--- a/drivers/regulator/Kconfig
+++ b/drivers/regulator/Kconfig
@@ -186,13 +186,25 @@
This driver provides support for the voltage regulators of the
PCAP2 PMIC.
+config REGULATOR_MC13XXX_CORE
+ tristate
+
config REGULATOR_MC13783
tristate "Support regulators on Freescale MC13783 PMIC"
depends on MFD_MC13783
+ select REGULATOR_MC13XXX_CORE
help
Say y here to support the regulators found on the Freescale MC13783
PMIC.
+config REGULATOR_MC13892
+ tristate "Support regulators on Freescale MC13892 PMIC"
+ depends on MFD_MC13XXX
+ select REGULATOR_MC13XXX_CORE
+ help
+ Say y here to support the regulators found on the Freescale MC13892
+ PMIC.
+
config REGULATOR_AB3100
tristate "ST-Ericsson AB3100 Regulator functions"
depends on AB3100_CORE
@@ -250,5 +262,15 @@
help
This driver supports TPS6586X voltage regulator chips.
+config REGULATOR_TPS6524X
+ tristate "TI TPS6524X Power regulators"
+ depends on SPI
+ help
+ This driver supports TPS6524X voltage regulator chips. TPS6524X
+ provides three step-down converters and two general-purpose LDO
+ voltage regulators. This device is interfaced using a customized
+ serial interface currently supported on the sequencer serial
+ port controller.
+
endif
diff --git a/drivers/regulator/Makefile b/drivers/regulator/Makefile
index bff8157..0b5e88c 100644
--- a/drivers/regulator/Makefile
+++ b/drivers/regulator/Makefile
@@ -30,10 +30,13 @@
obj-$(CONFIG_REGULATOR_PCF50633) += pcf50633-regulator.o
obj-$(CONFIG_REGULATOR_PCAP) += pcap-regulator.o
obj-$(CONFIG_REGULATOR_MC13783) += mc13783-regulator.o
+obj-$(CONFIG_REGULATOR_MC13892) += mc13892-regulator.o
+obj-$(CONFIG_REGULATOR_MC13XXX_CORE) += mc13xxx-regulator-core.o
obj-$(CONFIG_REGULATOR_AB3100) += ab3100.o
obj-$(CONFIG_REGULATOR_TPS65023) += tps65023-regulator.o
obj-$(CONFIG_REGULATOR_TPS6507X) += tps6507x-regulator.o
+obj-$(CONFIG_REGULATOR_TPS6524X) += tps6524x-regulator.o
obj-$(CONFIG_REGULATOR_88PM8607) += 88pm8607.o
obj-$(CONFIG_REGULATOR_ISL6271A) += isl6271a-regulator.o
obj-$(CONFIG_REGULATOR_AB8500) += ab8500.o
diff --git a/drivers/regulator/ab3100.c b/drivers/regulator/ab3100.c
index b349266..ed6feaf 100644
--- a/drivers/regulator/ab3100.c
+++ b/drivers/regulator/ab3100.c
@@ -362,7 +362,8 @@
}
static int ab3100_set_voltage_regulator(struct regulator_dev *reg,
- int min_uV, int max_uV)
+ int min_uV, int max_uV,
+ unsigned *selector)
{
struct ab3100_regulator *abreg = reg->reg_data;
u8 regval;
@@ -373,6 +374,8 @@
if (bestindex < 0)
return bestindex;
+ *selector = bestindex;
+
err = abx500_get_register_interruptible(abreg->dev, 0,
abreg->regreg, ®val);
if (err) {
diff --git a/drivers/regulator/ab8500.c b/drivers/regulator/ab8500.c
index db6b70f..d9a052c 100644
--- a/drivers/regulator/ab8500.c
+++ b/drivers/regulator/ab8500.c
@@ -3,18 +3,13 @@
*
* License Terms: GNU General Public License v2
*
- * Author: Sundar Iyer <sundar.iyer@stericsson.com> for ST-Ericsson
+ * Authors: Sundar Iyer <sundar.iyer@stericsson.com> for ST-Ericsson
+ * Bengt Jonsson <bengt.g.jonsson@stericsson.com> for ST-Ericsson
*
* AB8500 peripheral regulators
*
- * AB8500 supports the following regulators,
- * LDOs - VAUDIO, VANAMIC2/2, VDIGMIC, VINTCORE12, VTVOUT,
- * VAUX1/2/3, VANA
- *
- * for DB8500 cut 1.0 and previous versions of the silicon, all accesses
- * to registers are through the DB8500 SPI. In cut 1.1 onwards, these
- * accesses are through the DB8500 PRCMU I2C
- *
+ * AB8500 supports the following regulators:
+ * VAUX1/2/3, VINTCORE, VTVOUT, VAUDIO, VAMIC1/2, VDMIC, VANA
*/
#include <linux/init.h>
#include <linux/kernel.h>
@@ -28,38 +23,37 @@
/**
* struct ab8500_regulator_info - ab8500 regulator information
+ * @dev: device pointer
* @desc: regulator description
- * @ab8500: ab8500 parent
* @regulator_dev: regulator device
* @max_uV: maximum voltage (for variable voltage supplies)
* @min_uV: minimum voltage (for variable voltage supplies)
* @fixed_uV: typical voltage (for fixed voltage supplies)
* @update_bank: bank to control on/off
* @update_reg: register to control on/off
- * @mask: mask to enable/disable regulator
- * @enable: bits to enable the regulator in normal(high power) mode
+ * @update_mask: mask to enable/disable regulator
+ * @update_val_enable: bits to enable the regulator in normal (high power) mode
* @voltage_bank: bank to control regulator voltage
* @voltage_reg: register to control regulator voltage
* @voltage_mask: mask to control regulator voltage
- * @supported_voltages: supported voltage table
+ * @voltages: supported voltage table
* @voltages_len: number of supported voltages for the regulator
*/
struct ab8500_regulator_info {
struct device *dev;
struct regulator_desc desc;
- struct ab8500 *ab8500;
struct regulator_dev *regulator;
int max_uV;
int min_uV;
int fixed_uV;
u8 update_bank;
u8 update_reg;
- u8 mask;
- u8 enable;
+ u8 update_mask;
+ u8 update_val_enable;
u8 voltage_bank;
u8 voltage_reg;
u8 voltage_mask;
- int const *supported_voltages;
+ int const *voltages;
int voltages_len;
};
@@ -83,6 +77,17 @@
3300000,
};
+static const int ldo_vaux3_voltages[] = {
+ 1200000,
+ 1500000,
+ 1800000,
+ 2100000,
+ 2500000,
+ 2750000,
+ 2790000,
+ 2910000,
+};
+
static const int ldo_vintcore_voltages[] = {
1200000,
1225000,
@@ -95,57 +100,80 @@
static int ab8500_regulator_enable(struct regulator_dev *rdev)
{
- int regulator_id, ret;
+ int ret;
struct ab8500_regulator_info *info = rdev_get_drvdata(rdev);
- regulator_id = rdev_get_id(rdev);
- if (regulator_id >= AB8500_NUM_REGULATORS)
+ if (info == NULL) {
+ dev_err(rdev_get_dev(rdev), "regulator info null pointer\n");
return -EINVAL;
+ }
ret = abx500_mask_and_set_register_interruptible(info->dev,
- info->update_bank, info->update_reg, info->mask, info->enable);
+ info->update_bank, info->update_reg,
+ info->update_mask, info->update_val_enable);
if (ret < 0)
dev_err(rdev_get_dev(rdev),
"couldn't set enable bits for regulator\n");
+
+ dev_vdbg(rdev_get_dev(rdev),
+ "%s-enable (bank, reg, mask, value): 0x%x, 0x%x, 0x%x, 0x%x\n",
+ info->desc.name, info->update_bank, info->update_reg,
+ info->update_mask, info->update_val_enable);
+
return ret;
}
static int ab8500_regulator_disable(struct regulator_dev *rdev)
{
- int regulator_id, ret;
+ int ret;
struct ab8500_regulator_info *info = rdev_get_drvdata(rdev);
- regulator_id = rdev_get_id(rdev);
- if (regulator_id >= AB8500_NUM_REGULATORS)
+ if (info == NULL) {
+ dev_err(rdev_get_dev(rdev), "regulator info null pointer\n");
return -EINVAL;
+ }
ret = abx500_mask_and_set_register_interruptible(info->dev,
- info->update_bank, info->update_reg, info->mask, 0x0);
+ info->update_bank, info->update_reg,
+ info->update_mask, 0x0);
if (ret < 0)
dev_err(rdev_get_dev(rdev),
"couldn't set disable bits for regulator\n");
+
+ dev_vdbg(rdev_get_dev(rdev),
+ "%s-disable (bank, reg, mask, value): 0x%x, 0x%x, 0x%x, 0x%x\n",
+ info->desc.name, info->update_bank, info->update_reg,
+ info->update_mask, 0x0);
+
return ret;
}
static int ab8500_regulator_is_enabled(struct regulator_dev *rdev)
{
- int regulator_id, ret;
+ int ret;
struct ab8500_regulator_info *info = rdev_get_drvdata(rdev);
- u8 value;
+ u8 regval;
- regulator_id = rdev_get_id(rdev);
- if (regulator_id >= AB8500_NUM_REGULATORS)
+ if (info == NULL) {
+ dev_err(rdev_get_dev(rdev), "regulator info null pointer\n");
return -EINVAL;
+ }
ret = abx500_get_register_interruptible(info->dev,
- info->update_bank, info->update_reg, &value);
+ info->update_bank, info->update_reg, ®val);
if (ret < 0) {
dev_err(rdev_get_dev(rdev),
"couldn't read 0x%x register\n", info->update_reg);
return ret;
}
- if (value & info->mask)
+ dev_vdbg(rdev_get_dev(rdev),
+ "%s-is_enabled (bank, reg, mask, value): 0x%x, 0x%x, 0x%x,"
+ " 0x%x\n",
+ info->desc.name, info->update_bank, info->update_reg,
+ info->update_mask, regval);
+
+ if (regval & info->update_mask)
return true;
else
return false;
@@ -153,12 +181,12 @@
static int ab8500_list_voltage(struct regulator_dev *rdev, unsigned selector)
{
- int regulator_id;
struct ab8500_regulator_info *info = rdev_get_drvdata(rdev);
- regulator_id = rdev_get_id(rdev);
- if (regulator_id >= AB8500_NUM_REGULATORS)
+ if (info == NULL) {
+ dev_err(rdev_get_dev(rdev), "regulator info null pointer\n");
return -EINVAL;
+ }
/* return the uV for the fixed regulators */
if (info->fixed_uV)
@@ -167,33 +195,40 @@
if (selector >= info->voltages_len)
return -EINVAL;
- return info->supported_voltages[selector];
+ return info->voltages[selector];
}
static int ab8500_regulator_get_voltage(struct regulator_dev *rdev)
{
- int regulator_id, ret;
+ int ret, val;
struct ab8500_regulator_info *info = rdev_get_drvdata(rdev);
- u8 value;
+ u8 regval;
- regulator_id = rdev_get_id(rdev);
- if (regulator_id >= AB8500_NUM_REGULATORS)
+ if (info == NULL) {
+ dev_err(rdev_get_dev(rdev), "regulator info null pointer\n");
return -EINVAL;
+ }
- ret = abx500_get_register_interruptible(info->dev, info->voltage_bank,
- info->voltage_reg, &value);
+ ret = abx500_get_register_interruptible(info->dev,
+ info->voltage_bank, info->voltage_reg, ®val);
if (ret < 0) {
dev_err(rdev_get_dev(rdev),
"couldn't read voltage reg for regulator\n");
return ret;
}
+ dev_vdbg(rdev_get_dev(rdev),
+ "%s-get_voltage (bank, reg, mask, value): 0x%x, 0x%x, 0x%x,"
+ " 0x%x\n",
+ info->desc.name, info->voltage_bank, info->voltage_reg,
+ info->voltage_mask, regval);
+
/* vintcore has a different layout */
- value &= info->voltage_mask;
- if (regulator_id == AB8500_LDO_INTCORE)
- ret = info->supported_voltages[value >> 0x3];
+ val = regval & info->voltage_mask;
+ if (info->desc.id == AB8500_LDO_INTCORE)
+ ret = info->voltages[val >> 0x3];
else
- ret = info->supported_voltages[value];
+ ret = info->voltages[val];
return ret;
}
@@ -206,8 +241,8 @@
/* check the supported voltage */
for (i = 0; i < info->voltages_len; i++) {
- if ((info->supported_voltages[i] >= min_uV) &&
- (info->supported_voltages[i] <= max_uV))
+ if ((info->voltages[i] >= min_uV) &&
+ (info->voltages[i] <= max_uV))
return i;
}
@@ -215,14 +250,17 @@
}
static int ab8500_regulator_set_voltage(struct regulator_dev *rdev,
- int min_uV, int max_uV)
+ int min_uV, int max_uV,
+ unsigned *selector)
{
- int regulator_id, ret;
+ int ret;
struct ab8500_regulator_info *info = rdev_get_drvdata(rdev);
+ u8 regval;
- regulator_id = rdev_get_id(rdev);
- if (regulator_id >= AB8500_NUM_REGULATORS)
+ if (info == NULL) {
+ dev_err(rdev_get_dev(rdev), "regulator info null pointer\n");
return -EINVAL;
+ }
/* get the appropriate voltages within the range */
ret = ab8500_get_best_voltage_index(rdev, min_uV, max_uV);
@@ -232,14 +270,23 @@
return ret;
}
+ *selector = ret;
+
/* set the registers for the request */
+ regval = (u8)ret;
ret = abx500_mask_and_set_register_interruptible(info->dev,
- info->voltage_bank, info->voltage_reg,
- info->voltage_mask, (u8)ret);
+ info->voltage_bank, info->voltage_reg,
+ info->voltage_mask, regval);
if (ret < 0)
dev_err(rdev_get_dev(rdev),
"couldn't set voltage reg for regulator\n");
+ dev_vdbg(rdev_get_dev(rdev),
+ "%s-set_voltage (bank, reg, mask, value): 0x%x, 0x%x, 0x%x,"
+ " 0x%x\n",
+ info->desc.name, info->voltage_bank, info->voltage_reg,
+ info->voltage_mask, regval);
+
return ret;
}
@@ -254,17 +301,17 @@
static int ab8500_fixed_get_voltage(struct regulator_dev *rdev)
{
- int regulator_id;
struct ab8500_regulator_info *info = rdev_get_drvdata(rdev);
- regulator_id = rdev_get_id(rdev);
- if (regulator_id >= AB8500_NUM_REGULATORS)
+ if (info == NULL) {
+ dev_err(rdev_get_dev(rdev), "regulator info null pointer\n");
return -EINVAL;
+ }
return info->fixed_uV;
}
-static struct regulator_ops ab8500_ldo_fixed_ops = {
+static struct regulator_ops ab8500_regulator_fixed_ops = {
.enable = ab8500_regulator_enable,
.disable = ab8500_regulator_disable,
.is_enabled = ab8500_regulator_is_enabled,
@@ -272,89 +319,198 @@
.list_voltage = ab8500_list_voltage,
};
-#define AB8500_LDO(_id, min, max, bank, reg, reg_mask, \
- reg_enable, volt_bank, volt_reg, volt_mask, \
- voltages, len_volts) \
-{ \
- .desc = { \
- .name = "LDO-" #_id, \
- .ops = &ab8500_regulator_ops, \
- .type = REGULATOR_VOLTAGE, \
- .id = AB8500_LDO_##_id, \
- .owner = THIS_MODULE, \
- }, \
- .min_uV = (min) * 1000, \
- .max_uV = (max) * 1000, \
- .update_bank = bank, \
- .update_reg = reg, \
- .mask = reg_mask, \
- .enable = reg_enable, \
- .voltage_bank = volt_bank, \
- .voltage_reg = volt_reg, \
- .voltage_mask = volt_mask, \
- .supported_voltages = voltages, \
- .voltages_len = len_volts, \
- .fixed_uV = 0, \
-}
-
-#define AB8500_FIXED_LDO(_id, fixed, bank, reg, \
- reg_mask, reg_enable) \
-{ \
- .desc = { \
- .name = "LDO-" #_id, \
- .ops = &ab8500_ldo_fixed_ops, \
- .type = REGULATOR_VOLTAGE, \
- .id = AB8500_LDO_##_id, \
- .owner = THIS_MODULE, \
- }, \
- .fixed_uV = fixed * 1000, \
- .update_bank = bank, \
- .update_reg = reg, \
- .mask = reg_mask, \
- .enable = reg_enable, \
-}
-
-static struct ab8500_regulator_info ab8500_regulator_info[] = {
+static struct ab8500_regulator_info
+ ab8500_regulator_info[AB8500_NUM_REGULATORS] = {
/*
- * Variable Voltage LDOs
- * name, min uV, max uV, ctrl bank, ctrl reg, reg mask, enable mask,
- * volt ctrl bank, volt ctrl reg, volt ctrl mask, volt table,
- * num supported volts
+ * Variable Voltage Regulators
+ * name, min mV, max mV,
+ * update bank, reg, mask, enable val
+ * volt bank, reg, mask, table, table length
*/
- AB8500_LDO(AUX1, 1100, 3300, 0x04, 0x09, 0x3, 0x1, 0x04, 0x1f, 0xf,
- ldo_vauxn_voltages, ARRAY_SIZE(ldo_vauxn_voltages)),
- AB8500_LDO(AUX2, 1100, 3300, 0x04, 0x09, 0xc, 0x4, 0x04, 0x20, 0xf,
- ldo_vauxn_voltages, ARRAY_SIZE(ldo_vauxn_voltages)),
- AB8500_LDO(AUX3, 1100, 3300, 0x04, 0x0a, 0x3, 0x1, 0x04, 0x21, 0xf,
- ldo_vauxn_voltages, ARRAY_SIZE(ldo_vauxn_voltages)),
- AB8500_LDO(INTCORE, 1100, 3300, 0x03, 0x80, 0x4, 0x4, 0x03, 0x80, 0x38,
- ldo_vintcore_voltages, ARRAY_SIZE(ldo_vintcore_voltages)),
+ [AB8500_LDO_AUX1] = {
+ .desc = {
+ .name = "LDO-AUX1",
+ .ops = &ab8500_regulator_ops,
+ .type = REGULATOR_VOLTAGE,
+ .id = AB8500_LDO_AUX1,
+ .owner = THIS_MODULE,
+ .n_voltages = ARRAY_SIZE(ldo_vauxn_voltages),
+ },
+ .min_uV = 1100000,
+ .max_uV = 3300000,
+ .update_bank = 0x04,
+ .update_reg = 0x09,
+ .update_mask = 0x03,
+ .update_val_enable = 0x01,
+ .voltage_bank = 0x04,
+ .voltage_reg = 0x1f,
+ .voltage_mask = 0x0f,
+ .voltages = ldo_vauxn_voltages,
+ .voltages_len = ARRAY_SIZE(ldo_vauxn_voltages),
+ },
+ [AB8500_LDO_AUX2] = {
+ .desc = {
+ .name = "LDO-AUX2",
+ .ops = &ab8500_regulator_ops,
+ .type = REGULATOR_VOLTAGE,
+ .id = AB8500_LDO_AUX2,
+ .owner = THIS_MODULE,
+ .n_voltages = ARRAY_SIZE(ldo_vauxn_voltages),
+ },
+ .min_uV = 1100000,
+ .max_uV = 3300000,
+ .update_bank = 0x04,
+ .update_reg = 0x09,
+ .update_mask = 0x0c,
+ .update_val_enable = 0x04,
+ .voltage_bank = 0x04,
+ .voltage_reg = 0x20,
+ .voltage_mask = 0x0f,
+ .voltages = ldo_vauxn_voltages,
+ .voltages_len = ARRAY_SIZE(ldo_vauxn_voltages),
+ },
+ [AB8500_LDO_AUX3] = {
+ .desc = {
+ .name = "LDO-AUX3",
+ .ops = &ab8500_regulator_ops,
+ .type = REGULATOR_VOLTAGE,
+ .id = AB8500_LDO_AUX3,
+ .owner = THIS_MODULE,
+ .n_voltages = ARRAY_SIZE(ldo_vaux3_voltages),
+ },
+ .min_uV = 1100000,
+ .max_uV = 3300000,
+ .update_bank = 0x04,
+ .update_reg = 0x0a,
+ .update_mask = 0x03,
+ .update_val_enable = 0x01,
+ .voltage_bank = 0x04,
+ .voltage_reg = 0x21,
+ .voltage_mask = 0x07,
+ .voltages = ldo_vaux3_voltages,
+ .voltages_len = ARRAY_SIZE(ldo_vaux3_voltages),
+ },
+ [AB8500_LDO_INTCORE] = {
+ .desc = {
+ .name = "LDO-INTCORE",
+ .ops = &ab8500_regulator_ops,
+ .type = REGULATOR_VOLTAGE,
+ .id = AB8500_LDO_INTCORE,
+ .owner = THIS_MODULE,
+ .n_voltages = ARRAY_SIZE(ldo_vintcore_voltages),
+ },
+ .min_uV = 1100000,
+ .max_uV = 3300000,
+ .update_bank = 0x03,
+ .update_reg = 0x80,
+ .update_mask = 0x44,
+ .update_val_enable = 0x04,
+ .voltage_bank = 0x03,
+ .voltage_reg = 0x80,
+ .voltage_mask = 0x38,
+ .voltages = ldo_vintcore_voltages,
+ .voltages_len = ARRAY_SIZE(ldo_vintcore_voltages),
+ },
/*
- * Fixed Voltage LDOs
- * name, o/p uV, ctrl bank, ctrl reg, enable, disable
+ * Fixed Voltage Regulators
+ * name, fixed mV,
+ * update bank, reg, mask, enable val
*/
- AB8500_FIXED_LDO(TVOUT, 2000, 0x03, 0x80, 0x2, 0x2),
- AB8500_FIXED_LDO(AUDIO, 2000, 0x03, 0x83, 0x2, 0x2),
- AB8500_FIXED_LDO(ANAMIC1, 2050, 0x03, 0x83, 0x4, 0x4),
- AB8500_FIXED_LDO(ANAMIC2, 2050, 0x03, 0x83, 0x8, 0x8),
- AB8500_FIXED_LDO(DMIC, 1800, 0x03, 0x83, 0x10, 0x10),
- AB8500_FIXED_LDO(ANA, 1200, 0x03, 0x83, 0xc, 0x4),
+ [AB8500_LDO_TVOUT] = {
+ .desc = {
+ .name = "LDO-TVOUT",
+ .ops = &ab8500_regulator_fixed_ops,
+ .type = REGULATOR_VOLTAGE,
+ .id = AB8500_LDO_TVOUT,
+ .owner = THIS_MODULE,
+ .n_voltages = 1,
+ },
+ .fixed_uV = 2000000,
+ .update_bank = 0x03,
+ .update_reg = 0x80,
+ .update_mask = 0x82,
+ .update_val_enable = 0x02,
+ },
+ [AB8500_LDO_AUDIO] = {
+ .desc = {
+ .name = "LDO-AUDIO",
+ .ops = &ab8500_regulator_fixed_ops,
+ .type = REGULATOR_VOLTAGE,
+ .id = AB8500_LDO_AUDIO,
+ .owner = THIS_MODULE,
+ .n_voltages = 1,
+ },
+ .fixed_uV = 2000000,
+ .update_bank = 0x03,
+ .update_reg = 0x83,
+ .update_mask = 0x02,
+ .update_val_enable = 0x02,
+ },
+ [AB8500_LDO_ANAMIC1] = {
+ .desc = {
+ .name = "LDO-ANAMIC1",
+ .ops = &ab8500_regulator_fixed_ops,
+ .type = REGULATOR_VOLTAGE,
+ .id = AB8500_LDO_ANAMIC1,
+ .owner = THIS_MODULE,
+ .n_voltages = 1,
+ },
+ .fixed_uV = 2050000,
+ .update_bank = 0x03,
+ .update_reg = 0x83,
+ .update_mask = 0x08,
+ .update_val_enable = 0x08,
+ },
+ [AB8500_LDO_ANAMIC2] = {
+ .desc = {
+ .name = "LDO-ANAMIC2",
+ .ops = &ab8500_regulator_fixed_ops,
+ .type = REGULATOR_VOLTAGE,
+ .id = AB8500_LDO_ANAMIC2,
+ .owner = THIS_MODULE,
+ .n_voltages = 1,
+ },
+ .fixed_uV = 2050000,
+ .update_bank = 0x03,
+ .update_reg = 0x83,
+ .update_mask = 0x10,
+ .update_val_enable = 0x10,
+ },
+ [AB8500_LDO_DMIC] = {
+ .desc = {
+ .name = "LDO-DMIC",
+ .ops = &ab8500_regulator_fixed_ops,
+ .type = REGULATOR_VOLTAGE,
+ .id = AB8500_LDO_DMIC,
+ .owner = THIS_MODULE,
+ .n_voltages = 1,
+ },
+ .fixed_uV = 1800000,
+ .update_bank = 0x03,
+ .update_reg = 0x83,
+ .update_mask = 0x04,
+ .update_val_enable = 0x04,
+ },
+ [AB8500_LDO_ANA] = {
+ .desc = {
+ .name = "LDO-ANA",
+ .ops = &ab8500_regulator_fixed_ops,
+ .type = REGULATOR_VOLTAGE,
+ .id = AB8500_LDO_ANA,
+ .owner = THIS_MODULE,
+ .n_voltages = 1,
+ },
+ .fixed_uV = 1200000,
+ .update_bank = 0x04,
+ .update_reg = 0x06,
+ .update_mask = 0x0c,
+ .update_val_enable = 0x04,
+ },
+
+
};
-static inline struct ab8500_regulator_info *find_regulator_info(int id)
-{
- struct ab8500_regulator_info *info;
- int i;
-
- for (i = 0; i < ARRAY_SIZE(ab8500_regulator_info); i++) {
- info = &ab8500_regulator_info[i];
- if (info->desc.id == id)
- return info;
- }
- return NULL;
-}
-
static __devinit int ab8500_regulator_probe(struct platform_device *pdev)
{
struct ab8500 *ab8500 = dev_get_drvdata(pdev->dev.parent);
@@ -366,6 +522,16 @@
return -EINVAL;
}
pdata = dev_get_platdata(ab8500->dev);
+ if (!pdata) {
+ dev_err(&pdev->dev, "null pdata\n");
+ return -EINVAL;
+ }
+
+ /* make sure the platform data has the correct size */
+ if (pdata->num_regulator != ARRAY_SIZE(ab8500_regulator_info)) {
+ dev_err(&pdev->dev, "platform configuration error\n");
+ return -EINVAL;
+ }
/* register all regulators */
for (i = 0; i < ARRAY_SIZE(ab8500_regulator_info); i++) {
@@ -374,10 +540,22 @@
/* assign per-regulator data */
info = &ab8500_regulator_info[i];
info->dev = &pdev->dev;
- info->ab8500 = ab8500;
+ /* fix for hardware before ab8500v2.0 */
+ if (abx500_get_chip_id(info->dev) < 0x20) {
+ if (info->desc.id == AB8500_LDO_AUX3) {
+ info->desc.n_voltages =
+ ARRAY_SIZE(ldo_vauxn_voltages);
+ info->voltages = ldo_vauxn_voltages;
+ info->voltages_len =
+ ARRAY_SIZE(ldo_vauxn_voltages);
+ info->voltage_mask = 0xf;
+ }
+ }
+
+ /* register regulator with framework */
info->regulator = regulator_register(&info->desc, &pdev->dev,
- pdata->regulator[i], info);
+ &pdata->regulator[i], info);
if (IS_ERR(info->regulator)) {
err = PTR_ERR(info->regulator);
dev_err(&pdev->dev, "failed to register regulator %s\n",
@@ -389,6 +567,9 @@
}
return err;
}
+
+ dev_vdbg(rdev_get_dev(info->regulator),
+ "%s-probed\n", info->desc.name);
}
return 0;
@@ -401,6 +582,10 @@
for (i = 0; i < ARRAY_SIZE(ab8500_regulator_info); i++) {
struct ab8500_regulator_info *info = NULL;
info = &ab8500_regulator_info[i];
+
+ dev_vdbg(rdev_get_dev(info->regulator),
+ "%s-remove\n", info->desc.name);
+
regulator_unregister(info->regulator);
}
diff --git a/drivers/regulator/core.c b/drivers/regulator/core.c
index ba521f0..9fa2095 100644
--- a/drivers/regulator/core.c
+++ b/drivers/regulator/core.c
@@ -13,8 +13,11 @@
*
*/
+#define pr_fmt(fmt) "%s: " fmt, __func__
+
#include <linux/kernel.h>
#include <linux/init.h>
+#include <linux/debugfs.h>
#include <linux/device.h>
#include <linux/slab.h>
#include <linux/err.h>
@@ -25,16 +28,30 @@
#include <linux/regulator/driver.h>
#include <linux/regulator/machine.h>
+#define CREATE_TRACE_POINTS
+#include <trace/events/regulator.h>
+
#include "dummy.h"
-#define REGULATOR_VERSION "0.5"
+#define rdev_err(rdev, fmt, ...) \
+ pr_err("%s: " fmt, rdev_get_name(rdev), ##__VA_ARGS__)
+#define rdev_warn(rdev, fmt, ...) \
+ pr_warn("%s: " fmt, rdev_get_name(rdev), ##__VA_ARGS__)
+#define rdev_info(rdev, fmt, ...) \
+ pr_info("%s: " fmt, rdev_get_name(rdev), ##__VA_ARGS__)
+#define rdev_dbg(rdev, fmt, ...) \
+ pr_debug("%s: " fmt, rdev_get_name(rdev), ##__VA_ARGS__)
static DEFINE_MUTEX(regulator_list_mutex);
static LIST_HEAD(regulator_list);
static LIST_HEAD(regulator_map_list);
-static int has_full_constraints;
+static bool has_full_constraints;
static bool board_wants_dummy_regulator;
+#ifdef CONFIG_DEBUG_FS
+static struct dentry *debugfs_root;
+#endif
+
/*
* struct regulator_map
*
@@ -71,6 +88,8 @@
static unsigned int _regulator_get_mode(struct regulator_dev *rdev);
static void _notifier_call_chain(struct regulator_dev *rdev,
unsigned long event, void *data);
+static int _regulator_do_set_voltage(struct regulator_dev *rdev,
+ int min_uV, int max_uV);
static const char *rdev_get_name(struct regulator_dev *rdev)
{
@@ -111,13 +130,11 @@
BUG_ON(*min_uV > *max_uV);
if (!rdev->constraints) {
- printk(KERN_ERR "%s: no constraints for %s\n", __func__,
- rdev_get_name(rdev));
+ rdev_err(rdev, "no constraints\n");
return -ENODEV;
}
if (!(rdev->constraints->valid_ops_mask & REGULATOR_CHANGE_VOLTAGE)) {
- printk(KERN_ERR "%s: operation not allowed for %s\n",
- __func__, rdev_get_name(rdev));
+ rdev_err(rdev, "operation not allowed\n");
return -EPERM;
}
@@ -132,6 +149,27 @@
return 0;
}
+/* Make sure we select a voltage that suits the needs of all
+ * regulator consumers
+ */
+static int regulator_check_consumers(struct regulator_dev *rdev,
+ int *min_uV, int *max_uV)
+{
+ struct regulator *regulator;
+
+ list_for_each_entry(regulator, &rdev->consumer_list, list) {
+ if (*max_uV > regulator->max_uV)
+ *max_uV = regulator->max_uV;
+ if (*min_uV < regulator->min_uV)
+ *min_uV = regulator->min_uV;
+ }
+
+ if (*min_uV > *max_uV)
+ return -EINVAL;
+
+ return 0;
+}
+
/* current constraint check */
static int regulator_check_current_limit(struct regulator_dev *rdev,
int *min_uA, int *max_uA)
@@ -139,13 +177,11 @@
BUG_ON(*min_uA > *max_uA);
if (!rdev->constraints) {
- printk(KERN_ERR "%s: no constraints for %s\n", __func__,
- rdev_get_name(rdev));
+ rdev_err(rdev, "no constraints\n");
return -ENODEV;
}
if (!(rdev->constraints->valid_ops_mask & REGULATOR_CHANGE_CURRENT)) {
- printk(KERN_ERR "%s: operation not allowed for %s\n",
- __func__, rdev_get_name(rdev));
+ rdev_err(rdev, "operation not allowed\n");
return -EPERM;
}
@@ -174,18 +210,15 @@
}
if (!rdev->constraints) {
- printk(KERN_ERR "%s: no constraints for %s\n", __func__,
- rdev_get_name(rdev));
+ rdev_err(rdev, "no constraints\n");
return -ENODEV;
}
if (!(rdev->constraints->valid_ops_mask & REGULATOR_CHANGE_MODE)) {
- printk(KERN_ERR "%s: operation not allowed for %s\n",
- __func__, rdev_get_name(rdev));
+ rdev_err(rdev, "operation not allowed\n");
return -EPERM;
}
if (!(rdev->constraints->valid_modes_mask & mode)) {
- printk(KERN_ERR "%s: invalid mode %x for %s\n",
- __func__, mode, rdev_get_name(rdev));
+ rdev_err(rdev, "invalid mode %x\n", mode);
return -EINVAL;
}
return 0;
@@ -195,13 +228,11 @@
static int regulator_check_drms(struct regulator_dev *rdev)
{
if (!rdev->constraints) {
- printk(KERN_ERR "%s: no constraints for %s\n", __func__,
- rdev_get_name(rdev));
+ rdev_err(rdev, "no constraints\n");
return -ENODEV;
}
if (!(rdev->constraints->valid_ops_mask & REGULATOR_CHANGE_DRMS)) {
- printk(KERN_ERR "%s: operation not allowed for %s\n",
- __func__, rdev_get_name(rdev));
+ rdev_err(rdev, "operation not allowed\n");
return -EPERM;
}
return 0;
@@ -553,18 +584,21 @@
err = regulator_check_drms(rdev);
if (err < 0 || !rdev->desc->ops->get_optimum_mode ||
- !rdev->desc->ops->get_voltage || !rdev->desc->ops->set_mode)
+ (!rdev->desc->ops->get_voltage &&
+ !rdev->desc->ops->get_voltage_sel) ||
+ !rdev->desc->ops->set_mode)
return;
/* get output voltage */
- output_uV = rdev->desc->ops->get_voltage(rdev);
+ output_uV = _regulator_get_voltage(rdev);
if (output_uV <= 0)
return;
/* get input voltage */
- if (rdev->supply && rdev->supply->desc->ops->get_voltage)
- input_uV = rdev->supply->desc->ops->get_voltage(rdev->supply);
- else
+ input_uV = 0;
+ if (rdev->supply)
+ input_uV = _regulator_get_voltage(rdev);
+ if (input_uV <= 0)
input_uV = rdev->constraints->input_uV;
if (input_uV <= 0)
return;
@@ -598,20 +632,17 @@
*/
if (!rstate->enabled && !rstate->disabled) {
if (can_set_state)
- printk(KERN_WARNING "%s: No configuration for %s\n",
- __func__, rdev_get_name(rdev));
+ rdev_warn(rdev, "No configuration\n");
return 0;
}
if (rstate->enabled && rstate->disabled) {
- printk(KERN_ERR "%s: invalid configuration for %s\n",
- __func__, rdev_get_name(rdev));
+ rdev_err(rdev, "invalid configuration\n");
return -EINVAL;
}
if (!can_set_state) {
- printk(KERN_ERR "%s: no way to set suspend state\n",
- __func__);
+ rdev_err(rdev, "no way to set suspend state\n");
return -EINVAL;
}
@@ -620,15 +651,14 @@
else
ret = rdev->desc->ops->set_suspend_disable(rdev);
if (ret < 0) {
- printk(KERN_ERR "%s: failed to enabled/disable\n", __func__);
+ rdev_err(rdev, "failed to enabled/disable\n");
return ret;
}
if (rdev->desc->ops->set_suspend_voltage && rstate->uV > 0) {
ret = rdev->desc->ops->set_suspend_voltage(rdev, rstate->uV);
if (ret < 0) {
- printk(KERN_ERR "%s: failed to set voltage\n",
- __func__);
+ rdev_err(rdev, "failed to set voltage\n");
return ret;
}
}
@@ -636,7 +666,7 @@
if (rdev->desc->ops->set_suspend_mode && rstate->mode > 0) {
ret = rdev->desc->ops->set_suspend_mode(rdev, rstate->mode);
if (ret < 0) {
- printk(KERN_ERR "%s: failed to set mode\n", __func__);
+ rdev_err(rdev, "failed to set mode\n");
return ret;
}
}
@@ -714,29 +744,27 @@
if (constraints->valid_modes_mask & REGULATOR_MODE_STANDBY)
count += sprintf(buf + count, "standby");
- printk(KERN_INFO "regulator: %s: %s\n", rdev_get_name(rdev), buf);
+ rdev_info(rdev, "%s\n", buf);
}
static int machine_constraints_voltage(struct regulator_dev *rdev,
struct regulation_constraints *constraints)
{
struct regulator_ops *ops = rdev->desc->ops;
- const char *name = rdev_get_name(rdev);
int ret;
/* do we need to apply the constraint voltage */
if (rdev->constraints->apply_uV &&
- rdev->constraints->min_uV == rdev->constraints->max_uV &&
- ops->set_voltage) {
- ret = ops->set_voltage(rdev,
- rdev->constraints->min_uV, rdev->constraints->max_uV);
- if (ret < 0) {
- printk(KERN_ERR "%s: failed to apply %duV constraint to %s\n",
- __func__,
- rdev->constraints->min_uV, name);
- rdev->constraints = NULL;
- return ret;
- }
+ rdev->constraints->min_uV == rdev->constraints->max_uV) {
+ ret = _regulator_do_set_voltage(rdev,
+ rdev->constraints->min_uV,
+ rdev->constraints->max_uV);
+ if (ret < 0) {
+ rdev_err(rdev, "failed to apply %duV constraint\n",
+ rdev->constraints->min_uV);
+ rdev->constraints = NULL;
+ return ret;
+ }
}
/* constrain machine-level voltage specs to fit
@@ -765,8 +793,7 @@
/* else require explicit machine-level constraints */
if (cmin <= 0 || cmax <= 0 || cmax < cmin) {
- pr_err("%s: %s '%s' voltage constraints\n",
- __func__, "invalid", name);
+ rdev_err(rdev, "invalid voltage constraints\n");
return -EINVAL;
}
@@ -787,22 +814,19 @@
/* final: [min_uV..max_uV] valid iff constraints valid */
if (max_uV < min_uV) {
- pr_err("%s: %s '%s' voltage constraints\n",
- __func__, "unsupportable", name);
+ rdev_err(rdev, "unsupportable voltage constraints\n");
return -EINVAL;
}
/* use regulator's subset of machine constraints */
if (constraints->min_uV < min_uV) {
- pr_debug("%s: override '%s' %s, %d -> %d\n",
- __func__, name, "min_uV",
- constraints->min_uV, min_uV);
+ rdev_dbg(rdev, "override min_uV, %d -> %d\n",
+ constraints->min_uV, min_uV);
constraints->min_uV = min_uV;
}
if (constraints->max_uV > max_uV) {
- pr_debug("%s: override '%s' %s, %d -> %d\n",
- __func__, name, "max_uV",
- constraints->max_uV, max_uV);
+ rdev_dbg(rdev, "override max_uV, %d -> %d\n",
+ constraints->max_uV, max_uV);
constraints->max_uV = max_uV;
}
}
@@ -822,26 +846,25 @@
* set_mode.
*/
static int set_machine_constraints(struct regulator_dev *rdev,
- struct regulation_constraints *constraints)
+ const struct regulation_constraints *constraints)
{
int ret = 0;
- const char *name;
struct regulator_ops *ops = rdev->desc->ops;
- rdev->constraints = constraints;
+ rdev->constraints = kmemdup(constraints, sizeof(*constraints),
+ GFP_KERNEL);
+ if (!rdev->constraints)
+ return -ENOMEM;
- name = rdev_get_name(rdev);
-
- ret = machine_constraints_voltage(rdev, constraints);
+ ret = machine_constraints_voltage(rdev, rdev->constraints);
if (ret != 0)
goto out;
/* do we need to setup our suspend state */
if (constraints->initial_state) {
- ret = suspend_prepare(rdev, constraints->initial_state);
+ ret = suspend_prepare(rdev, rdev->constraints->initial_state);
if (ret < 0) {
- printk(KERN_ERR "%s: failed to set suspend state for %s\n",
- __func__, name);
+ rdev_err(rdev, "failed to set suspend state\n");
rdev->constraints = NULL;
goto out;
}
@@ -849,17 +872,14 @@
if (constraints->initial_mode) {
if (!ops->set_mode) {
- printk(KERN_ERR "%s: no set_mode operation for %s\n",
- __func__, name);
+ rdev_err(rdev, "no set_mode operation\n");
ret = -EINVAL;
goto out;
}
- ret = ops->set_mode(rdev, constraints->initial_mode);
+ ret = ops->set_mode(rdev, rdev->constraints->initial_mode);
if (ret < 0) {
- printk(KERN_ERR
- "%s: failed to set initial mode for %s: %d\n",
- __func__, name, ret);
+ rdev_err(rdev, "failed to set initial mode: %d\n", ret);
goto out;
}
}
@@ -867,11 +887,11 @@
/* If the constraints say the regulator should be on at this point
* and we have control then make sure it is enabled.
*/
- if ((constraints->always_on || constraints->boot_on) && ops->enable) {
+ if ((rdev->constraints->always_on || rdev->constraints->boot_on) &&
+ ops->enable) {
ret = ops->enable(rdev);
if (ret < 0) {
- printk(KERN_ERR "%s: failed to enable %s\n",
- __func__, name);
+ rdev_err(rdev, "failed to enable\n");
rdev->constraints = NULL;
goto out;
}
@@ -899,9 +919,8 @@
err = sysfs_create_link(&rdev->dev.kobj, &supply_rdev->dev.kobj,
"supply");
if (err) {
- printk(KERN_ERR
- "%s: could not add device link %s err %d\n",
- __func__, supply_rdev->dev.kobj.name, err);
+ rdev_err(rdev, "could not add device link %s err %d\n",
+ supply_rdev->dev.kobj.name, err);
goto out;
}
rdev->supply = supply_rdev;
@@ -957,10 +976,10 @@
continue;
dev_dbg(consumer_dev, "%s/%s is '%s' supply; fail %s/%s\n",
- dev_name(&node->regulator->dev),
- node->regulator->desc->name,
- supply,
- dev_name(&rdev->dev), rdev_get_name(rdev));
+ dev_name(&node->regulator->dev),
+ node->regulator->desc->name,
+ supply,
+ dev_name(&rdev->dev), rdev_get_name(rdev));
return -EBUSY;
}
@@ -1031,8 +1050,7 @@
regulator->dev_attr.show = device_requested_uA_show;
err = device_create_file(dev, ®ulator->dev_attr);
if (err < 0) {
- printk(KERN_WARNING "%s: could not add regulator_dev"
- " load sysfs\n", __func__);
+ rdev_warn(rdev, "could not add regulator_dev requested microamps sysfs entry\n");
goto attr_name_err;
}
@@ -1049,9 +1067,8 @@
err = sysfs_create_link(&rdev->dev.kobj, &dev->kobj,
buf);
if (err) {
- printk(KERN_WARNING
- "%s: could not add device link %s err %d\n",
- __func__, dev->kobj.name, err);
+ rdev_warn(rdev, "could not add device link %s err %d\n",
+ dev->kobj.name, err);
goto link_name_err;
}
}
@@ -1088,7 +1105,7 @@
int ret;
if (id == NULL) {
- printk(KERN_ERR "regulator: get() with no identifier\n");
+ pr_err("get() with no identifier\n");
return regulator;
}
@@ -1122,8 +1139,8 @@
* substitute in a dummy regulator so consumers can continue.
*/
if (!has_full_constraints) {
- pr_warning("%s supply %s not found, using dummy regulator\n",
- devname, id);
+ pr_warn("%s supply %s not found, using dummy regulator\n",
+ devname, id);
rdev = dummy_regulator_rdev;
goto found;
}
@@ -1274,8 +1291,7 @@
ret = _regulator_enable(rdev->supply);
mutex_unlock(&rdev->supply->mutex);
if (ret < 0) {
- printk(KERN_ERR "%s: failed to enable %s: %d\n",
- __func__, rdev_get_name(rdev), ret);
+ rdev_err(rdev, "failed to enable: %d\n", ret);
return ret;
}
}
@@ -1302,13 +1318,13 @@
if (ret >= 0) {
delay = ret;
} else {
- printk(KERN_WARNING
- "%s: enable_time() failed for %s: %d\n",
- __func__, rdev_get_name(rdev),
- ret);
+ rdev_warn(rdev, "enable_time() failed: %d\n",
+ ret);
delay = 0;
}
+ trace_regulator_enable(rdev_get_name(rdev));
+
/* Allow the regulator to ramp; it would be useful
* to extend this for bulk operations so that the
* regulators can ramp together. */
@@ -1316,6 +1332,8 @@
if (ret < 0)
return ret;
+ trace_regulator_enable_delay(rdev_get_name(rdev));
+
if (delay >= 1000) {
mdelay(delay / 1000);
udelay(delay % 1000);
@@ -1323,9 +1341,10 @@
udelay(delay);
}
+ trace_regulator_enable_complete(rdev_get_name(rdev));
+
} else if (ret < 0) {
- printk(KERN_ERR "%s: is_enabled() failed for %s: %d\n",
- __func__, rdev_get_name(rdev), ret);
+ rdev_err(rdev, "is_enabled() failed: %d\n", ret);
return ret;
}
/* Fallthrough on positive return values - already enabled */
@@ -1367,8 +1386,7 @@
*supply_rdev_ptr = NULL;
if (WARN(rdev->use_count <= 0,
- "unbalanced disables for %s\n",
- rdev_get_name(rdev)))
+ "unbalanced disables for %s\n", rdev_get_name(rdev)))
return -EIO;
/* are we the last user and permitted to disable ? */
@@ -1378,13 +1396,16 @@
/* we are last user */
if (_regulator_can_change_status(rdev) &&
rdev->desc->ops->disable) {
+ trace_regulator_disable(rdev_get_name(rdev));
+
ret = rdev->desc->ops->disable(rdev);
if (ret < 0) {
- printk(KERN_ERR "%s: failed to disable %s\n",
- __func__, rdev_get_name(rdev));
+ rdev_err(rdev, "failed to disable\n");
return ret;
}
+ trace_regulator_disable_complete(rdev_get_name(rdev));
+
_notifier_call_chain(rdev, REGULATOR_EVENT_DISABLE,
NULL);
}
@@ -1451,8 +1472,7 @@
/* ah well, who wants to live forever... */
ret = rdev->desc->ops->disable(rdev);
if (ret < 0) {
- printk(KERN_ERR "%s: failed to force disable %s\n",
- __func__, rdev_get_name(rdev));
+ rdev_err(rdev, "failed to force disable\n");
return ret;
}
/* notify other consumers that power has been forced off */
@@ -1605,6 +1625,62 @@
return 0;
}
+static int _regulator_do_set_voltage(struct regulator_dev *rdev,
+ int min_uV, int max_uV)
+{
+ int ret;
+ unsigned int selector;
+
+ trace_regulator_set_voltage(rdev_get_name(rdev), min_uV, max_uV);
+
+ if (rdev->desc->ops->set_voltage) {
+ ret = rdev->desc->ops->set_voltage(rdev, min_uV, max_uV,
+ &selector);
+
+ if (rdev->desc->ops->list_voltage)
+ selector = rdev->desc->ops->list_voltage(rdev,
+ selector);
+ else
+ selector = -1;
+ } else if (rdev->desc->ops->set_voltage_sel) {
+ int best_val = INT_MAX;
+ int i;
+
+ selector = 0;
+
+ /* Find the smallest voltage that falls within the specified
+ * range.
+ */
+ for (i = 0; i < rdev->desc->n_voltages; i++) {
+ ret = rdev->desc->ops->list_voltage(rdev, i);
+ if (ret < 0)
+ continue;
+
+ if (ret < best_val && ret >= min_uV && ret <= max_uV) {
+ best_val = ret;
+ selector = i;
+ }
+ }
+
+ if (best_val != INT_MAX) {
+ ret = rdev->desc->ops->set_voltage_sel(rdev, selector);
+ selector = best_val;
+ } else {
+ ret = -EINVAL;
+ }
+ } else {
+ ret = -EINVAL;
+ }
+
+ if (ret == 0)
+ _notifier_call_chain(rdev, REGULATOR_EVENT_VOLTAGE_CHANGE,
+ NULL);
+
+ trace_regulator_set_voltage_complete(rdev_get_name(rdev), selector);
+
+ return ret;
+}
+
/**
* regulator_set_voltage - set regulator output voltage
* @regulator: regulator source
@@ -1626,12 +1702,20 @@
int regulator_set_voltage(struct regulator *regulator, int min_uV, int max_uV)
{
struct regulator_dev *rdev = regulator->rdev;
- int ret;
+ int ret = 0;
mutex_lock(&rdev->mutex);
+ /* If we're setting the same range as last time the change
+ * should be a noop (some cpufreq implementations use the same
+ * voltage for multiple frequencies, for example).
+ */
+ if (regulator->min_uV == min_uV && regulator->max_uV == max_uV)
+ goto out;
+
/* sanity check */
- if (!rdev->desc->ops->set_voltage) {
+ if (!rdev->desc->ops->set_voltage &&
+ !rdev->desc->ops->set_voltage_sel) {
ret = -EINVAL;
goto out;
}
@@ -1642,18 +1726,76 @@
goto out;
regulator->min_uV = min_uV;
regulator->max_uV = max_uV;
- ret = rdev->desc->ops->set_voltage(rdev, min_uV, max_uV);
+
+ ret = regulator_check_consumers(rdev, &min_uV, &max_uV);
+ if (ret < 0)
+ goto out;
+
+ ret = _regulator_do_set_voltage(rdev, min_uV, max_uV);
out:
- _notifier_call_chain(rdev, REGULATOR_EVENT_VOLTAGE_CHANGE, NULL);
mutex_unlock(&rdev->mutex);
return ret;
}
EXPORT_SYMBOL_GPL(regulator_set_voltage);
+/**
+ * regulator_sync_voltage - re-apply last regulator output voltage
+ * @regulator: regulator source
+ *
+ * Re-apply the last configured voltage. This is intended to be used
+ * where some external control source the consumer is cooperating with
+ * has caused the configured voltage to change.
+ */
+int regulator_sync_voltage(struct regulator *regulator)
+{
+ struct regulator_dev *rdev = regulator->rdev;
+ int ret, min_uV, max_uV;
+
+ mutex_lock(&rdev->mutex);
+
+ if (!rdev->desc->ops->set_voltage &&
+ !rdev->desc->ops->set_voltage_sel) {
+ ret = -EINVAL;
+ goto out;
+ }
+
+ /* This is only going to work if we've had a voltage configured. */
+ if (!regulator->min_uV && !regulator->max_uV) {
+ ret = -EINVAL;
+ goto out;
+ }
+
+ min_uV = regulator->min_uV;
+ max_uV = regulator->max_uV;
+
+ /* This should be a paranoia check... */
+ ret = regulator_check_voltage(rdev, &min_uV, &max_uV);
+ if (ret < 0)
+ goto out;
+
+ ret = regulator_check_consumers(rdev, &min_uV, &max_uV);
+ if (ret < 0)
+ goto out;
+
+ ret = _regulator_do_set_voltage(rdev, min_uV, max_uV);
+
+out:
+ mutex_unlock(&rdev->mutex);
+ return ret;
+}
+EXPORT_SYMBOL_GPL(regulator_sync_voltage);
+
static int _regulator_get_voltage(struct regulator_dev *rdev)
{
- /* sanity check */
+ int sel;
+
+ if (rdev->desc->ops->get_voltage_sel) {
+ sel = rdev->desc->ops->get_voltage_sel(rdev);
+ if (sel < 0)
+ return sel;
+ return rdev->desc->ops->list_voltage(rdev, sel);
+ }
if (rdev->desc->ops->get_voltage)
return rdev->desc->ops->get_voltage(rdev);
else
@@ -1880,21 +2022,20 @@
goto out;
/* get output voltage */
- output_uV = rdev->desc->ops->get_voltage(rdev);
+ output_uV = _regulator_get_voltage(rdev);
if (output_uV <= 0) {
- printk(KERN_ERR "%s: invalid output voltage found for %s\n",
- __func__, rdev_get_name(rdev));
+ rdev_err(rdev, "invalid output voltage found\n");
goto out;
}
/* get input voltage */
- if (rdev->supply && rdev->supply->desc->ops->get_voltage)
- input_uV = rdev->supply->desc->ops->get_voltage(rdev->supply);
- else
+ input_uV = 0;
+ if (rdev->supply)
+ input_uV = _regulator_get_voltage(rdev->supply);
+ if (input_uV <= 0)
input_uV = rdev->constraints->input_uV;
if (input_uV <= 0) {
- printk(KERN_ERR "%s: invalid input voltage found for %s\n",
- __func__, rdev_get_name(rdev));
+ rdev_err(rdev, "invalid input voltage found\n");
goto out;
}
@@ -1907,16 +2048,14 @@
total_uA_load);
ret = regulator_check_mode(rdev, mode);
if (ret < 0) {
- printk(KERN_ERR "%s: failed to get optimum mode for %s @"
- " %d uA %d -> %d uV\n", __func__, rdev_get_name(rdev),
- total_uA_load, input_uV, output_uV);
+ rdev_err(rdev, "failed to get optimum mode @ %d uA %d -> %d uV\n",
+ total_uA_load, input_uV, output_uV);
goto out;
}
ret = rdev->desc->ops->set_mode(rdev, mode);
if (ret < 0) {
- printk(KERN_ERR "%s: failed to set optimum mode %x for %s\n",
- __func__, mode, rdev_get_name(rdev));
+ rdev_err(rdev, "failed to set optimum mode %x\n", mode);
goto out;
}
ret = mode;
@@ -2047,7 +2186,7 @@
return 0;
err:
- printk(KERN_ERR "Failed to enable %s: %d\n", consumers[i].supply, ret);
+ pr_err("Failed to enable %s: %d\n", consumers[i].supply, ret);
for (--i; i >= 0; --i)
regulator_disable(consumers[i].consumer);
@@ -2082,8 +2221,7 @@
return 0;
err:
- printk(KERN_ERR "Failed to disable %s: %d\n", consumers[i].supply,
- ret);
+ pr_err("Failed to disable %s: %d\n", consumers[i].supply, ret);
for (--i; i >= 0; --i)
regulator_enable(consumers[i].consumer);
@@ -2166,7 +2304,7 @@
int status = 0;
/* some attributes need specific methods to be displayed */
- if (ops->get_voltage) {
+ if (ops->get_voltage || ops->get_voltage_sel) {
status = device_create_file(dev, &dev_attr_microvolts);
if (status < 0)
return status;
@@ -2207,7 +2345,7 @@
return status;
/* constraints need specific supporting methods */
- if (ops->set_voltage) {
+ if (ops->set_voltage || ops->set_voltage_sel) {
status = device_create_file(dev, &dev_attr_min_microvolts);
if (status < 0)
return status;
@@ -2271,6 +2409,23 @@
return status;
}
+static void rdev_init_debugfs(struct regulator_dev *rdev)
+{
+#ifdef CONFIG_DEBUG_FS
+ rdev->debugfs = debugfs_create_dir(rdev_get_name(rdev), debugfs_root);
+ if (IS_ERR(rdev->debugfs) || !rdev->debugfs) {
+ rdev_warn(rdev, "Failed to create debugfs directory\n");
+ rdev->debugfs = NULL;
+ return;
+ }
+
+ debugfs_create_u32("use_count", 0444, rdev->debugfs,
+ &rdev->use_count);
+ debugfs_create_u32("open_count", 0444, rdev->debugfs,
+ &rdev->open_count);
+#endif
+}
+
/**
* regulator_register - register regulator
* @regulator_desc: regulator to register
@@ -2282,7 +2437,7 @@
* Returns 0 on success.
*/
struct regulator_dev *regulator_register(struct regulator_desc *regulator_desc,
- struct device *dev, struct regulator_init_data *init_data,
+ struct device *dev, const struct regulator_init_data *init_data,
void *driver_data)
{
static atomic_t regulator_no = ATOMIC_INIT(0);
@@ -2302,6 +2457,22 @@
if (!init_data)
return ERR_PTR(-EINVAL);
+ /* Only one of each should be implemented */
+ WARN_ON(regulator_desc->ops->get_voltage &&
+ regulator_desc->ops->get_voltage_sel);
+ WARN_ON(regulator_desc->ops->set_voltage &&
+ regulator_desc->ops->set_voltage_sel);
+
+ /* If we're using selectors we must implement list_voltage. */
+ if (regulator_desc->ops->get_voltage_sel &&
+ !regulator_desc->ops->list_voltage) {
+ return ERR_PTR(-EINVAL);
+ }
+ if (regulator_desc->ops->set_voltage_sel &&
+ !regulator_desc->ops->list_voltage) {
+ return ERR_PTR(-EINVAL);
+ }
+
rdev = kzalloc(sizeof(struct regulator_dev), GFP_KERNEL);
if (rdev == NULL)
return ERR_PTR(-ENOMEM);
@@ -2399,6 +2570,8 @@
}
list_add(&rdev->list, ®ulator_list);
+
+ rdev_init_debugfs(rdev);
out:
mutex_unlock(®ulator_list_mutex);
return rdev;
@@ -2431,12 +2604,16 @@
return;
mutex_lock(®ulator_list_mutex);
+#ifdef CONFIG_DEBUG_FS
+ debugfs_remove_recursive(rdev->debugfs);
+#endif
WARN_ON(rdev->open_count);
unset_regulator_supplies(rdev);
list_del(&rdev->list);
if (rdev->supply)
sysfs_remove_link(&rdev->dev.kobj, "supply");
device_unregister(&rdev->dev);
+ kfree(rdev->constraints);
mutex_unlock(®ulator_list_mutex);
}
EXPORT_SYMBOL_GPL(regulator_unregister);
@@ -2465,8 +2642,7 @@
mutex_unlock(&rdev->mutex);
if (ret < 0) {
- printk(KERN_ERR "%s: failed to prepare %s\n",
- __func__, rdev_get_name(rdev));
+ rdev_err(rdev, "failed to prepare\n");
goto out;
}
}
@@ -2572,10 +2748,16 @@
{
int ret;
- printk(KERN_INFO "regulator: core version %s\n", REGULATOR_VERSION);
-
ret = class_register(®ulator_class);
+#ifdef CONFIG_DEBUG_FS
+ debugfs_root = debugfs_create_dir("regulator", NULL);
+ if (IS_ERR(debugfs_root) || !debugfs_root) {
+ pr_warn("regulator: Failed to create debugfs directory\n");
+ debugfs_root = NULL;
+ }
+#endif
+
regulator_dummy_init();
return ret;
@@ -2590,7 +2772,6 @@
struct regulator_ops *ops;
struct regulation_constraints *c;
int enabled, ret;
- const char *name;
mutex_lock(®ulator_list_mutex);
@@ -2602,8 +2783,6 @@
ops = rdev->desc->ops;
c = rdev->constraints;
- name = rdev_get_name(rdev);
-
if (!ops->disable || (c && c->always_on))
continue;
@@ -2624,13 +2803,10 @@
if (has_full_constraints) {
/* We log since this may kill the system if it
* goes wrong. */
- printk(KERN_INFO "%s: disabling %s\n",
- __func__, name);
+ rdev_info(rdev, "disabling\n");
ret = ops->disable(rdev);
if (ret != 0) {
- printk(KERN_ERR
- "%s: couldn't disable %s: %d\n",
- __func__, name, ret);
+ rdev_err(rdev, "couldn't disable: %d\n", ret);
}
} else {
/* The intention is that in future we will
@@ -2638,9 +2814,7 @@
* so warn even if we aren't going to do
* anything here.
*/
- printk(KERN_WARNING
- "%s: incomplete constraints, leaving %s on\n",
- __func__, name);
+ rdev_warn(rdev, "incomplete constraints, leaving on\n");
}
unlock:
diff --git a/drivers/regulator/da903x.c b/drivers/regulator/da903x.c
index f8c4661..362e082 100644
--- a/drivers/regulator/da903x.c
+++ b/drivers/regulator/da903x.c
@@ -107,7 +107,7 @@
/* DA9030/DA9034 common operations */
static int da903x_set_ldo_voltage(struct regulator_dev *rdev,
- int min_uV, int max_uV)
+ int min_uV, int max_uV, unsigned *selector)
{
struct da903x_regulator_info *info = rdev_get_drvdata(rdev);
struct device *da9034_dev = to_da903x_dev(rdev);
@@ -119,6 +119,7 @@
}
val = (min_uV - info->min_uV + info->step_uV - 1) / info->step_uV;
+ *selector = val;
val <<= info->vol_shift;
mask = ((1 << info->vol_nbits) - 1) << info->vol_shift;
@@ -187,7 +188,8 @@
/* DA9030 specific operations */
static int da9030_set_ldo1_15_voltage(struct regulator_dev *rdev,
- int min_uV, int max_uV)
+ int min_uV, int max_uV,
+ unsigned *selector)
{
struct da903x_regulator_info *info = rdev_get_drvdata(rdev);
struct device *da903x_dev = to_da903x_dev(rdev);
@@ -200,6 +202,7 @@
}
val = (min_uV - info->min_uV + info->step_uV - 1) / info->step_uV;
+ *selector = val;
val <<= info->vol_shift;
mask = ((1 << info->vol_nbits) - 1) << info->vol_shift;
val |= DA9030_LDO_UNLOCK; /* have to set UNLOCK bits */
@@ -214,7 +217,8 @@
}
static int da9030_set_ldo14_voltage(struct regulator_dev *rdev,
- int min_uV, int max_uV)
+ int min_uV, int max_uV,
+ unsigned *selector)
{
struct da903x_regulator_info *info = rdev_get_drvdata(rdev);
struct device *da903x_dev = to_da903x_dev(rdev);
@@ -234,6 +238,7 @@
val = (min_uV - thresh + info->step_uV - 1) / info->step_uV;
}
+ *selector = val;
val <<= info->vol_shift;
mask = ((1 << info->vol_nbits) - 1) << info->vol_shift;
@@ -263,7 +268,7 @@
/* DA9034 specific operations */
static int da9034_set_dvc_voltage(struct regulator_dev *rdev,
- int min_uV, int max_uV)
+ int min_uV, int max_uV, unsigned *selector)
{
struct da903x_regulator_info *info = rdev_get_drvdata(rdev);
struct device *da9034_dev = to_da903x_dev(rdev);
@@ -276,6 +281,7 @@
}
val = (min_uV - info->min_uV + info->step_uV - 1) / info->step_uV;
+ *selector = val;
val <<= info->vol_shift;
mask = ((1 << info->vol_nbits) - 1) << info->vol_shift;
@@ -289,7 +295,7 @@
}
static int da9034_set_ldo12_voltage(struct regulator_dev *rdev,
- int min_uV, int max_uV)
+ int min_uV, int max_uV, unsigned *selector)
{
struct da903x_regulator_info *info = rdev_get_drvdata(rdev);
struct device *da9034_dev = to_da903x_dev(rdev);
@@ -302,6 +308,7 @@
val = (min_uV - info->min_uV + info->step_uV - 1) / info->step_uV;
val = (val >= 20) ? val - 12 : ((val > 7) ? 8 : val);
+ *selector = val;
val <<= info->vol_shift;
mask = ((1 << info->vol_nbits) - 1) << info->vol_shift;
diff --git a/drivers/regulator/isl6271a-regulator.c b/drivers/regulator/isl6271a-regulator.c
index b8cc638..e4b3592 100644
--- a/drivers/regulator/isl6271a-regulator.c
+++ b/drivers/regulator/isl6271a-regulator.c
@@ -58,7 +58,9 @@
return data;
}
-static int isl6271a_set_voltage(struct regulator_dev *dev, int minuV, int maxuV)
+static int isl6271a_set_voltage(struct regulator_dev *dev,
+ int minuV, int maxuV,
+ unsigned *selector)
{
struct isl_pmic *pmic = rdev_get_drvdata(dev);
int vsel, err, data;
@@ -78,6 +80,8 @@
/* Convert the microvolts to data for the chip */
data = (vsel - ISL6271A_VOLTAGE_MIN) / ISL6271A_VOLTAGE_STEP;
+ *selector = data;
+
mutex_lock(&pmic->mtx);
err = i2c_smbus_write_byte(pmic->client, data);
@@ -169,7 +173,7 @@
init_data, pmic);
if (IS_ERR(pmic->rdev[i])) {
dev_err(&i2c->dev, "failed to register %s\n", id->name);
- err = PTR_ERR(pmic->rdev);
+ err = PTR_ERR(pmic->rdev[i]);
goto error;
}
}
diff --git a/drivers/regulator/lp3971.c b/drivers/regulator/lp3971.c
index 3bb82b6..0f22ef1 100644
--- a/drivers/regulator/lp3971.c
+++ b/drivers/regulator/lp3971.c
@@ -168,7 +168,8 @@
}
static int lp3971_ldo_set_voltage(struct regulator_dev *dev,
- int min_uV, int max_uV)
+ int min_uV, int max_uV,
+ unsigned int *selector)
{
struct lp3971 *lp3971 = rdev_get_drvdata(dev);
int ldo = rdev_get_id(dev) - LP3971_LDO1;
@@ -187,6 +188,8 @@
if (val > LDO_VOL_MAX_IDX || vol_map[val] > max_vol)
return -EINVAL;
+ *selector = val;
+
return lp3971_set_bits(lp3971, LP3971_LDO_VOL_CONTR_REG(ldo),
LDO_VOL_CONTR_MASK << LDO_VOL_CONTR_SHIFT(ldo),
val << LDO_VOL_CONTR_SHIFT(ldo));
@@ -256,7 +259,8 @@
}
static int lp3971_dcdc_set_voltage(struct regulator_dev *dev,
- int min_uV, int max_uV)
+ int min_uV, int max_uV,
+ unsigned int *selector)
{
struct lp3971 *lp3971 = rdev_get_drvdata(dev);
int buck = rdev_get_id(dev) - LP3971_DCDC1;
@@ -277,6 +281,8 @@
if (val > BUCK_TARGET_VOL_MAX_IDX || vol_map[val] > max_vol)
return -EINVAL;
+ *selector = val;
+
ret = lp3971_set_bits(lp3971, LP3971_BUCK_TARGET_VOL1_REG(buck),
BUCK_TARGET_VOL_MASK, val);
if (ret)
diff --git a/drivers/regulator/lp3972.c b/drivers/regulator/lp3972.c
index e07062f..6aa1b50 100644
--- a/drivers/regulator/lp3972.c
+++ b/drivers/regulator/lp3972.c
@@ -292,7 +292,8 @@
}
static int lp3972_ldo_set_voltage(struct regulator_dev *dev,
- int min_uV, int max_uV)
+ int min_uV, int max_uV,
+ unsigned int *selector)
{
struct lp3972 *lp3972 = rdev_get_drvdata(dev);
int ldo = rdev_get_id(dev) - LP3972_LDO1;
@@ -313,6 +314,8 @@
if (val > LP3972_LDO_VOL_MAX_IDX(ldo) || vol_map[val] > max_vol)
return -EINVAL;
+ *selector = val;
+
shift = LP3972_LDO_VOL_CONTR_SHIFT(ldo);
ret = lp3972_set_bits(lp3972, LP3972_LDO_VOL_CONTR_REG(ldo),
LP3972_LDO_VOL_MASK(ldo) << shift, val << shift);
@@ -416,7 +419,8 @@
}
static int lp3972_dcdc_set_voltage(struct regulator_dev *dev,
- int min_uV, int max_uV)
+ int min_uV, int max_uV,
+ unsigned int *selector)
{
struct lp3972 *lp3972 = rdev_get_drvdata(dev);
int buck = rdev_get_id(dev) - LP3972_DCDC1;
@@ -438,6 +442,8 @@
vol_map[val] > max_vol)
return -EINVAL;
+ *selector = val;
+
ret = lp3972_set_bits(lp3972, LP3972_BUCK_VOL1_REG(buck),
LP3972_BUCK_VOL_MASK, val);
if (ret)
diff --git a/drivers/regulator/max1586.c b/drivers/regulator/max1586.c
index 559cfa2..3f49512 100644
--- a/drivers/regulator/max1586.c
+++ b/drivers/regulator/max1586.c
@@ -63,12 +63,12 @@
return max1586->min_uV + (selector * range_uV / MAX1586_V3_MAX_VSEL);
}
-static int max1586_v3_set(struct regulator_dev *rdev, int min_uV, int max_uV)
+static int max1586_v3_set(struct regulator_dev *rdev, int min_uV, int max_uV,
+ unsigned *selector)
{
struct max1586_data *max1586 = rdev_get_drvdata(rdev);
struct i2c_client *client = max1586->client;
unsigned range_uV = max1586->max_uV - max1586->min_uV;
- unsigned selector;
u8 v3_prog;
if (min_uV > max1586->max_uV || max_uV < max1586->min_uV)
@@ -76,15 +76,15 @@
if (min_uV < max1586->min_uV)
min_uV = max1586->min_uV;
- selector = ((min_uV - max1586->min_uV) * MAX1586_V3_MAX_VSEL +
+ *selector = ((min_uV - max1586->min_uV) * MAX1586_V3_MAX_VSEL +
range_uV - 1) / range_uV;
- if (max1586_v3_calc_voltage(max1586, selector) > max_uV)
+ if (max1586_v3_calc_voltage(max1586, *selector) > max_uV)
return -EINVAL;
dev_dbg(&client->dev, "changing voltage v3 to %dmv\n",
- max1586_v3_calc_voltage(max1586, selector) / 1000);
+ max1586_v3_calc_voltage(max1586, *selector) / 1000);
- v3_prog = I2C_V3_SELECT | (u8) selector;
+ v3_prog = I2C_V3_SELECT | (u8) *selector;
return i2c_smbus_write_byte(client, v3_prog);
}
@@ -110,10 +110,10 @@
return voltages_uv[selector];
}
-static int max1586_v6_set(struct regulator_dev *rdev, int min_uV, int max_uV)
+static int max1586_v6_set(struct regulator_dev *rdev, int min_uV, int max_uV,
+ unsigned int *selector)
{
struct i2c_client *client = rdev_get_drvdata(rdev);
- unsigned selector;
u8 v6_prog;
if (min_uV < MAX1586_V6_MIN_UV || min_uV > MAX1586_V6_MAX_UV)
@@ -122,21 +122,21 @@
return -EINVAL;
if (min_uV < 1800000)
- selector = 0;
+ *selector = 0;
else if (min_uV < 2500000)
- selector = 1;
+ *selector = 1;
else if (min_uV < 3000000)
- selector = 2;
+ *selector = 2;
else if (min_uV >= 3000000)
- selector = 3;
+ *selector = 3;
- if (max1586_v6_calc_voltage(selector) > max_uV)
+ if (max1586_v6_calc_voltage(*selector) > max_uV)
return -EINVAL;
dev_dbg(&client->dev, "changing voltage v6 to %dmv\n",
- max1586_v6_calc_voltage(selector) / 1000);
+ max1586_v6_calc_voltage(*selector) / 1000);
- v6_prog = I2C_V6_SELECT | (u8) selector;
+ v6_prog = I2C_V6_SELECT | (u8) *selector;
return i2c_smbus_write_byte(client, v6_prog);
}
diff --git a/drivers/regulator/max8649.c b/drivers/regulator/max8649.c
index 6b60a9c..30eb9e5 100644
--- a/drivers/regulator/max8649.c
+++ b/drivers/regulator/max8649.c
@@ -155,7 +155,7 @@
}
static int max8649_set_voltage(struct regulator_dev *rdev,
- int min_uV, int max_uV)
+ int min_uV, int max_uV, unsigned *selector)
{
struct max8649_regulator_info *info = rdev_get_drvdata(rdev);
unsigned char data, mask;
@@ -168,6 +168,7 @@
data = (min_uV - MAX8649_DCDC_VMIN + MAX8649_DCDC_STEP - 1)
/ MAX8649_DCDC_STEP;
mask = MAX8649_VOL_MASK;
+ *selector = data & mask;
return max8649_set_bits(info->i2c, info->vol_reg, mask, data);
}
diff --git a/drivers/regulator/max8660.c b/drivers/regulator/max8660.c
index c570e6e..33f5d9a 100644
--- a/drivers/regulator/max8660.c
+++ b/drivers/regulator/max8660.c
@@ -141,7 +141,8 @@
return MAX8660_DCDC_MIN_UV + selector * MAX8660_DCDC_STEP;
}
-static int max8660_dcdc_set(struct regulator_dev *rdev, int min_uV, int max_uV)
+static int max8660_dcdc_set(struct regulator_dev *rdev, int min_uV, int max_uV,
+ unsigned int *s)
{
struct max8660 *max8660 = rdev_get_drvdata(rdev);
u8 reg, selector, bits;
@@ -154,6 +155,7 @@
selector = (min_uV - (MAX8660_DCDC_MIN_UV - MAX8660_DCDC_STEP + 1))
/ MAX8660_DCDC_STEP;
+ *s = selector;
ret = max8660_dcdc_list(rdev, selector);
if (ret < 0 || ret > max_uV)
@@ -196,7 +198,8 @@
return MAX8660_LDO5_MIN_UV + selector * MAX8660_LDO5_STEP;
}
-static int max8660_ldo5_set(struct regulator_dev *rdev, int min_uV, int max_uV)
+static int max8660_ldo5_set(struct regulator_dev *rdev, int min_uV, int max_uV,
+ unsigned int *s)
{
struct max8660 *max8660 = rdev_get_drvdata(rdev);
u8 selector;
@@ -213,6 +216,8 @@
if (ret < 0 || ret > max_uV)
return -EINVAL;
+ *s = selector;
+
ret = max8660_write(max8660, MAX8660_MDTV2, 0, selector);
if (ret)
return ret;
@@ -270,7 +275,8 @@
return MAX8660_LDO67_MIN_UV + selector * MAX8660_LDO67_STEP;
}
-static int max8660_ldo67_set(struct regulator_dev *rdev, int min_uV, int max_uV)
+static int max8660_ldo67_set(struct regulator_dev *rdev, int min_uV,
+ int max_uV, unsigned int *s)
{
struct max8660 *max8660 = rdev_get_drvdata(rdev);
u8 selector;
@@ -288,6 +294,8 @@
if (ret < 0 || ret > max_uV)
return -EINVAL;
+ *s = selector;
+
if (rdev_get_id(rdev) == MAX8660_V6)
return max8660_write(max8660, MAX8660_L12VCR, 0xf0, selector);
else
diff --git a/drivers/regulator/max8925-regulator.c b/drivers/regulator/max8925-regulator.c
index 552cad8..8ae1475 100644
--- a/drivers/regulator/max8925-regulator.c
+++ b/drivers/regulator/max8925-regulator.c
@@ -55,7 +55,7 @@
}
static int max8925_set_voltage(struct regulator_dev *rdev,
- int min_uV, int max_uV)
+ int min_uV, int max_uV, unsigned int *selector)
{
struct max8925_regulator_info *info = rdev_get_drvdata(rdev);
unsigned char data, mask;
@@ -66,6 +66,7 @@
return -EINVAL;
}
data = (min_uV - info->min_uV + info->step_uV - 1) / info->step_uV;
+ *selector = data;
data <<= info->vol_shift;
mask = ((1 << info->vol_nbits) - 1) << info->vol_shift;
diff --git a/drivers/regulator/max8952.c b/drivers/regulator/max8952.c
index 0d5dda4..a8f4ecf 100644
--- a/drivers/regulator/max8952.c
+++ b/drivers/regulator/max8952.c
@@ -133,7 +133,7 @@
}
static int max8952_set_voltage(struct regulator_dev *rdev,
- int min_uV, int max_uV)
+ int min_uV, int max_uV, unsigned *selector)
{
struct max8952_data *max8952 = rdev_get_drvdata(rdev);
s8 vid = -1, i;
@@ -156,6 +156,7 @@
if (vid >= 0 && vid < MAX8952_NUM_DVS_MODE) {
max8952->vid0 = (vid % 2 == 1);
max8952->vid1 = (((vid >> 1) % 2) == 1);
+ *selector = vid;
gpio_set_value(max8952->pdata->gpio_vid0, max8952->vid0);
gpio_set_value(max8952->pdata->gpio_vid1, max8952->vid1);
} else
diff --git a/drivers/regulator/max8998.c b/drivers/regulator/max8998.c
index 5c20756..7568df6 100644
--- a/drivers/regulator/max8998.c
+++ b/drivers/regulator/max8998.c
@@ -304,7 +304,7 @@
}
static int max8998_set_voltage_ldo(struct regulator_dev *rdev,
- int min_uV, int max_uV)
+ int min_uV, int max_uV, unsigned *selector)
{
struct max8998_data *max8998 = rdev_get_drvdata(rdev);
struct i2c_client *i2c = max8998->iodev->i2c;
@@ -331,6 +331,8 @@
if (desc->min + desc->step*i > max_vol)
return -EINVAL;
+ *selector = i;
+
ret = max8998_get_voltage_register(rdev, ®, &shift, &mask);
if (ret)
return ret;
@@ -352,7 +354,7 @@
}
static int max8998_set_voltage_buck(struct regulator_dev *rdev,
- int min_uV, int max_uV)
+ int min_uV, int max_uV, unsigned *selector)
{
struct max8998_data *max8998 = rdev_get_drvdata(rdev);
struct max8998_platform_data *pdata =
@@ -384,6 +386,8 @@
if (desc->min + desc->step*i > max_vol)
return -EINVAL;
+ *selector = i;
+
ret = max8998_get_voltage_register(rdev, ®, &shift, &mask);
if (ret)
return ret;
diff --git a/drivers/regulator/mc13783-regulator.c b/drivers/regulator/mc13783-regulator.c
index ecd99f5..3e5d0c3 100644
--- a/drivers/regulator/mc13783-regulator.c
+++ b/drivers/regulator/mc13783-regulator.c
@@ -1,6 +1,7 @@
/*
* Regulator Driver for Freescale MC13783 PMIC
*
+ * Copyright 2010 Yong Shen <yong.shen@linaro.org>
* Copyright (C) 2008 Sascha Hauer, Pengutronix <s.hauer@pengutronix.de>
* Copyright 2009 Alberto Panizzo <maramaopercheseimorto@gmail.com>
*
@@ -17,6 +18,7 @@
#include <linux/slab.h>
#include <linux/init.h>
#include <linux/err.h>
+#include "mc13xxx.h"
#define MC13783_REG_SWITCHERS5 29
#define MC13783_REG_SWITCHERS5_SW3EN (1 << 20)
@@ -89,154 +91,106 @@
#define MC13783_REG_POWERMISC_PWGTSPI_M (3 << 15)
-struct mc13783_regulator {
- struct regulator_desc desc;
- int reg;
- int enable_bit;
- int vsel_reg;
- int vsel_shift;
- int vsel_mask;
- int const *voltages;
-};
-
/* Voltage Values */
-static const int const mc13783_sw3_val[] = {
+static const int mc13783_sw3_val[] = {
5000000, 5000000, 5000000, 5500000,
};
-static const int const mc13783_vaudio_val[] = {
+static const int mc13783_vaudio_val[] = {
2775000,
};
-static const int const mc13783_viohi_val[] = {
+static const int mc13783_viohi_val[] = {
2775000,
};
-static const int const mc13783_violo_val[] = {
+static const int mc13783_violo_val[] = {
1200000, 1300000, 1500000, 1800000,
};
-static const int const mc13783_vdig_val[] = {
+static const int mc13783_vdig_val[] = {
1200000, 1300000, 1500000, 1800000,
};
-static const int const mc13783_vgen_val[] = {
+static const int mc13783_vgen_val[] = {
1200000, 1300000, 1500000, 1800000,
1100000, 2000000, 2775000, 2400000,
};
-static const int const mc13783_vrfdig_val[] = {
+static const int mc13783_vrfdig_val[] = {
1200000, 1500000, 1800000, 1875000,
};
-static const int const mc13783_vrfref_val[] = {
+static const int mc13783_vrfref_val[] = {
2475000, 2600000, 2700000, 2775000,
};
-static const int const mc13783_vrfcp_val[] = {
+static const int mc13783_vrfcp_val[] = {
2700000, 2775000,
};
-static const int const mc13783_vsim_val[] = {
+static const int mc13783_vsim_val[] = {
1800000, 2900000, 3000000,
};
-static const int const mc13783_vesim_val[] = {
+static const int mc13783_vesim_val[] = {
1800000, 2900000,
};
-static const int const mc13783_vcam_val[] = {
+static const int mc13783_vcam_val[] = {
1500000, 1800000, 2500000, 2550000,
2600000, 2750000, 2800000, 3000000,
};
-static const int const mc13783_vrfbg_val[] = {
+static const int mc13783_vrfbg_val[] = {
1250000,
};
-static const int const mc13783_vvib_val[] = {
+static const int mc13783_vvib_val[] = {
1300000, 1800000, 2000000, 3000000,
};
-static const int const mc13783_vmmc_val[] = {
+static const int mc13783_vmmc_val[] = {
1600000, 1800000, 2000000, 2600000,
2700000, 2800000, 2900000, 3000000,
};
-static const int const mc13783_vrf_val[] = {
+static const int mc13783_vrf_val[] = {
1500000, 1875000, 2700000, 2775000,
};
-static const int const mc13783_gpo_val[] = {
+static const int mc13783_gpo_val[] = {
3100000,
};
-static const int const mc13783_pwgtdrv_val[] = {
+static const int mc13783_pwgtdrv_val[] = {
5500000,
};
-static struct regulator_ops mc13783_regulator_ops;
-static struct regulator_ops mc13783_fixed_regulator_ops;
static struct regulator_ops mc13783_gpo_regulator_ops;
-#define MC13783_DEFINE(prefix, _name, _reg, _vsel_reg, _voltages) \
- [MC13783_ ## prefix ## _ ## _name] = { \
- .desc = { \
- .name = #prefix "_" #_name, \
- .n_voltages = ARRAY_SIZE(_voltages), \
- .ops = &mc13783_regulator_ops, \
- .type = REGULATOR_VOLTAGE, \
- .id = MC13783_ ## prefix ## _ ## _name, \
- .owner = THIS_MODULE, \
- }, \
- .reg = MC13783_REG_ ## _reg, \
- .enable_bit = MC13783_REG_ ## _reg ## _ ## _name ## EN, \
- .vsel_reg = MC13783_REG_ ## _vsel_reg, \
- .vsel_shift = MC13783_REG_ ## _vsel_reg ## _ ## _name ## VSEL,\
- .vsel_mask = MC13783_REG_ ## _vsel_reg ## _ ## _name ## VSEL_M,\
- .voltages = _voltages, \
- }
+#define MC13783_DEFINE(prefix, name, reg, vsel_reg, voltages) \
+ MC13xxx_DEFINE(MC13783_REG_, name, reg, vsel_reg, voltages, \
+ mc13xxx_regulator_ops)
-#define MC13783_FIXED_DEFINE(prefix, _name, _reg, _voltages) \
- [MC13783_ ## prefix ## _ ## _name] = { \
- .desc = { \
- .name = #prefix "_" #_name, \
- .n_voltages = ARRAY_SIZE(_voltages), \
- .ops = &mc13783_fixed_regulator_ops, \
- .type = REGULATOR_VOLTAGE, \
- .id = MC13783_ ## prefix ## _ ## _name, \
- .owner = THIS_MODULE, \
- }, \
- .reg = MC13783_REG_ ## _reg, \
- .enable_bit = MC13783_REG_ ## _reg ## _ ## _name ## EN, \
- .voltages = _voltages, \
- }
+#define MC13783_FIXED_DEFINE(prefix, name, reg, voltages) \
+ MC13xxx_FIXED_DEFINE(MC13783_REG_, name, reg, voltages, \
+ mc13xxx_fixed_regulator_ops)
-#define MC13783_GPO_DEFINE(prefix, _name, _reg, _voltages) \
- [MC13783_ ## prefix ## _ ## _name] = { \
- .desc = { \
- .name = #prefix "_" #_name, \
- .n_voltages = ARRAY_SIZE(_voltages), \
- .ops = &mc13783_gpo_regulator_ops, \
- .type = REGULATOR_VOLTAGE, \
- .id = MC13783_ ## prefix ## _ ## _name, \
- .owner = THIS_MODULE, \
- }, \
- .reg = MC13783_REG_ ## _reg, \
- .enable_bit = MC13783_REG_ ## _reg ## _ ## _name ## EN, \
- .voltages = _voltages, \
- }
+#define MC13783_GPO_DEFINE(prefix, name, reg, voltages) \
+ MC13xxx_GPO_DEFINE(MC13783_REG_, name, reg, voltages, \
+ mc13783_gpo_regulator_ops)
#define MC13783_DEFINE_SW(_name, _reg, _vsel_reg, _voltages) \
- MC13783_DEFINE(SW, _name, _reg, _vsel_reg, _voltages)
+ MC13783_DEFINE(REG, _name, _reg, _vsel_reg, _voltages)
#define MC13783_DEFINE_REGU(_name, _reg, _vsel_reg, _voltages) \
- MC13783_DEFINE(REGU, _name, _reg, _vsel_reg, _voltages)
+ MC13783_DEFINE(REG, _name, _reg, _vsel_reg, _voltages)
-static struct mc13783_regulator mc13783_regulators[] = {
+static struct mc13xxx_regulator mc13783_regulators[] = {
MC13783_DEFINE_SW(SW3, SWITCHERS5, SWITCHERS5, mc13783_sw3_val),
- MC13783_FIXED_DEFINE(REGU, VAUDIO, REGULATORMODE0, mc13783_vaudio_val),
- MC13783_FIXED_DEFINE(REGU, VIOHI, REGULATORMODE0, mc13783_viohi_val),
+ MC13783_FIXED_DEFINE(REG, VAUDIO, REGULATORMODE0, mc13783_vaudio_val),
+ MC13783_FIXED_DEFINE(REG, VIOHI, REGULATORMODE0, mc13783_viohi_val),
MC13783_DEFINE_REGU(VIOLO, REGULATORMODE0, REGULATORSETTING0, \
mc13783_violo_val),
MC13783_DEFINE_REGU(VDIG, REGULATORMODE0, REGULATORSETTING0, \
@@ -255,7 +209,7 @@
mc13783_vesim_val),
MC13783_DEFINE_REGU(VCAM, REGULATORMODE1, REGULATORSETTING0, \
mc13783_vcam_val),
- MC13783_FIXED_DEFINE(REGU, VRFBG, REGULATORMODE1, mc13783_vrfbg_val),
+ MC13783_FIXED_DEFINE(REG, VRFBG, REGULATORMODE1, mc13783_vrfbg_val),
MC13783_DEFINE_REGU(VVIB, REGULATORMODE1, REGULATORSETTING1, \
mc13783_vvib_val),
MC13783_DEFINE_REGU(VRF1, REGULATORMODE1, REGULATORSETTING1, \
@@ -266,215 +220,24 @@
mc13783_vmmc_val),
MC13783_DEFINE_REGU(VMMC2, REGULATORMODE1, REGULATORSETTING1, \
mc13783_vmmc_val),
- MC13783_GPO_DEFINE(REGU, GPO1, POWERMISC, mc13783_gpo_val),
- MC13783_GPO_DEFINE(REGU, GPO2, POWERMISC, mc13783_gpo_val),
- MC13783_GPO_DEFINE(REGU, GPO3, POWERMISC, mc13783_gpo_val),
- MC13783_GPO_DEFINE(REGU, GPO4, POWERMISC, mc13783_gpo_val),
- MC13783_GPO_DEFINE(REGU, PWGT1SPI, POWERMISC, mc13783_pwgtdrv_val),
- MC13783_GPO_DEFINE(REGU, PWGT2SPI, POWERMISC, mc13783_pwgtdrv_val),
+ MC13783_GPO_DEFINE(REG, GPO1, POWERMISC, mc13783_gpo_val),
+ MC13783_GPO_DEFINE(REG, GPO2, POWERMISC, mc13783_gpo_val),
+ MC13783_GPO_DEFINE(REG, GPO3, POWERMISC, mc13783_gpo_val),
+ MC13783_GPO_DEFINE(REG, GPO4, POWERMISC, mc13783_gpo_val),
+ MC13783_GPO_DEFINE(REG, PWGT1SPI, POWERMISC, mc13783_pwgtdrv_val),
+ MC13783_GPO_DEFINE(REG, PWGT2SPI, POWERMISC, mc13783_pwgtdrv_val),
};
-struct mc13783_regulator_priv {
- struct mc13783 *mc13783;
- u32 powermisc_pwgt_state;
- struct regulator_dev *regulators[];
-};
-
-static int mc13783_regulator_enable(struct regulator_dev *rdev)
+static int mc13783_powermisc_rmw(struct mc13xxx_regulator_priv *priv, u32 mask,
+ u32 val)
{
- struct mc13783_regulator_priv *priv = rdev_get_drvdata(rdev);
- int id = rdev_get_id(rdev);
- int ret;
-
- dev_dbg(rdev_get_dev(rdev), "%s id: %d\n", __func__, id);
-
- mc13783_lock(priv->mc13783);
- ret = mc13783_reg_rmw(priv->mc13783, mc13783_regulators[id].reg,
- mc13783_regulators[id].enable_bit,
- mc13783_regulators[id].enable_bit);
- mc13783_unlock(priv->mc13783);
-
- return ret;
-}
-
-static int mc13783_regulator_disable(struct regulator_dev *rdev)
-{
- struct mc13783_regulator_priv *priv = rdev_get_drvdata(rdev);
- int id = rdev_get_id(rdev);
- int ret;
-
- dev_dbg(rdev_get_dev(rdev), "%s id: %d\n", __func__, id);
-
- mc13783_lock(priv->mc13783);
- ret = mc13783_reg_rmw(priv->mc13783, mc13783_regulators[id].reg,
- mc13783_regulators[id].enable_bit, 0);
- mc13783_unlock(priv->mc13783);
-
- return ret;
-}
-
-static int mc13783_regulator_is_enabled(struct regulator_dev *rdev)
-{
- struct mc13783_regulator_priv *priv = rdev_get_drvdata(rdev);
- int ret, id = rdev_get_id(rdev);
- unsigned int val;
-
- mc13783_lock(priv->mc13783);
- ret = mc13783_reg_read(priv->mc13783, mc13783_regulators[id].reg, &val);
- mc13783_unlock(priv->mc13783);
-
- if (ret)
- return ret;
-
- return (val & mc13783_regulators[id].enable_bit) != 0;
-}
-
-static int mc13783_regulator_list_voltage(struct regulator_dev *rdev,
- unsigned selector)
-{
- int id = rdev_get_id(rdev);
-
- if (selector >= mc13783_regulators[id].desc.n_voltages)
- return -EINVAL;
-
- return mc13783_regulators[id].voltages[selector];
-}
-
-static int mc13783_get_best_voltage_index(struct regulator_dev *rdev,
- int min_uV, int max_uV)
-{
- int reg_id = rdev_get_id(rdev);
- int i;
- int bestmatch;
- int bestindex;
-
- /*
- * Locate the minimum voltage fitting the criteria on
- * this regulator. The switchable voltages are not
- * in strict falling order so we need to check them
- * all for the best match.
- */
- bestmatch = INT_MAX;
- bestindex = -1;
- for (i = 0; i < mc13783_regulators[reg_id].desc.n_voltages; i++) {
- if (mc13783_regulators[reg_id].voltages[i] >= min_uV &&
- mc13783_regulators[reg_id].voltages[i] < bestmatch) {
- bestmatch = mc13783_regulators[reg_id].voltages[i];
- bestindex = i;
- }
- }
-
- if (bestindex < 0 || bestmatch > max_uV) {
- dev_warn(&rdev->dev, "no possible value for %d<=x<=%d uV\n",
- min_uV, max_uV);
- return -EINVAL;
- }
- return bestindex;
-}
-
-static int mc13783_regulator_set_voltage(struct regulator_dev *rdev,
- int min_uV, int max_uV)
-{
- struct mc13783_regulator_priv *priv = rdev_get_drvdata(rdev);
- int value, id = rdev_get_id(rdev);
- int ret;
-
- dev_dbg(rdev_get_dev(rdev), "%s id: %d min_uV: %d max_uV: %d\n",
- __func__, id, min_uV, max_uV);
-
- /* Find the best index */
- value = mc13783_get_best_voltage_index(rdev, min_uV, max_uV);
- dev_dbg(rdev_get_dev(rdev), "%s best value: %d \n", __func__, value);
- if (value < 0)
- return value;
-
- mc13783_lock(priv->mc13783);
- ret = mc13783_reg_rmw(priv->mc13783, mc13783_regulators[id].vsel_reg,
- mc13783_regulators[id].vsel_mask,
- value << mc13783_regulators[id].vsel_shift);
- mc13783_unlock(priv->mc13783);
-
- return ret;
-}
-
-static int mc13783_regulator_get_voltage(struct regulator_dev *rdev)
-{
- struct mc13783_regulator_priv *priv = rdev_get_drvdata(rdev);
- int ret, id = rdev_get_id(rdev);
- unsigned int val;
-
- dev_dbg(rdev_get_dev(rdev), "%s id: %d\n", __func__, id);
-
- mc13783_lock(priv->mc13783);
- ret = mc13783_reg_read(priv->mc13783,
- mc13783_regulators[id].vsel_reg, &val);
- mc13783_unlock(priv->mc13783);
-
- if (ret)
- return ret;
-
- val = (val & mc13783_regulators[id].vsel_mask)
- >> mc13783_regulators[id].vsel_shift;
-
- dev_dbg(rdev_get_dev(rdev), "%s id: %d val: %d\n", __func__, id, val);
-
- BUG_ON(val < 0 || val > mc13783_regulators[id].desc.n_voltages);
-
- return mc13783_regulators[id].voltages[val];
-}
-
-static struct regulator_ops mc13783_regulator_ops = {
- .enable = mc13783_regulator_enable,
- .disable = mc13783_regulator_disable,
- .is_enabled = mc13783_regulator_is_enabled,
- .list_voltage = mc13783_regulator_list_voltage,
- .set_voltage = mc13783_regulator_set_voltage,
- .get_voltage = mc13783_regulator_get_voltage,
-};
-
-static int mc13783_fixed_regulator_set_voltage(struct regulator_dev *rdev,
- int min_uV, int max_uV)
-{
- int id = rdev_get_id(rdev);
-
- dev_dbg(rdev_get_dev(rdev), "%s id: %d min_uV: %d max_uV: %d\n",
- __func__, id, min_uV, max_uV);
-
- if (min_uV >= mc13783_regulators[id].voltages[0] &&
- max_uV <= mc13783_regulators[id].voltages[0])
- return 0;
- else
- return -EINVAL;
-}
-
-static int mc13783_fixed_regulator_get_voltage(struct regulator_dev *rdev)
-{
- int id = rdev_get_id(rdev);
-
- dev_dbg(rdev_get_dev(rdev), "%s id: %d\n", __func__, id);
-
- return mc13783_regulators[id].voltages[0];
-}
-
-static struct regulator_ops mc13783_fixed_regulator_ops = {
- .enable = mc13783_regulator_enable,
- .disable = mc13783_regulator_disable,
- .is_enabled = mc13783_regulator_is_enabled,
- .list_voltage = mc13783_regulator_list_voltage,
- .set_voltage = mc13783_fixed_regulator_set_voltage,
- .get_voltage = mc13783_fixed_regulator_get_voltage,
-};
-
-static int mc13783_powermisc_rmw(struct mc13783_regulator_priv *priv, u32 mask,
- u32 val)
-{
- struct mc13783 *mc13783 = priv->mc13783;
+ struct mc13xxx *mc13783 = priv->mc13xxx;
int ret;
u32 valread;
BUG_ON(val & ~mask);
- ret = mc13783_reg_read(mc13783, MC13783_REG_POWERMISC, &valread);
+ ret = mc13xxx_reg_read(mc13783, MC13783_REG_POWERMISC, &valread);
if (ret)
return ret;
@@ -489,34 +252,36 @@
valread = (valread & ~MC13783_REG_POWERMISC_PWGTSPI_M) |
priv->powermisc_pwgt_state;
- return mc13783_reg_write(mc13783, MC13783_REG_POWERMISC, valread);
+ return mc13xxx_reg_write(mc13783, MC13783_REG_POWERMISC, valread);
}
static int mc13783_gpo_regulator_enable(struct regulator_dev *rdev)
{
- struct mc13783_regulator_priv *priv = rdev_get_drvdata(rdev);
+ struct mc13xxx_regulator_priv *priv = rdev_get_drvdata(rdev);
+ struct mc13xxx_regulator *mc13xxx_regulators = priv->mc13xxx_regulators;
int id = rdev_get_id(rdev);
int ret;
- u32 en_val = mc13783_regulators[id].enable_bit;
+ u32 en_val = mc13xxx_regulators[id].enable_bit;
dev_dbg(rdev_get_dev(rdev), "%s id: %d\n", __func__, id);
/* Power Gate enable value is 0 */
- if (id == MC13783_REGU_PWGT1SPI ||
- id == MC13783_REGU_PWGT2SPI)
+ if (id == MC13783_REG_PWGT1SPI ||
+ id == MC13783_REG_PWGT2SPI)
en_val = 0;
- mc13783_lock(priv->mc13783);
- ret = mc13783_powermisc_rmw(priv, mc13783_regulators[id].enable_bit,
+ mc13xxx_lock(priv->mc13xxx);
+ ret = mc13783_powermisc_rmw(priv, mc13xxx_regulators[id].enable_bit,
en_val);
- mc13783_unlock(priv->mc13783);
+ mc13xxx_unlock(priv->mc13xxx);
return ret;
}
static int mc13783_gpo_regulator_disable(struct regulator_dev *rdev)
{
- struct mc13783_regulator_priv *priv = rdev_get_drvdata(rdev);
+ struct mc13xxx_regulator_priv *priv = rdev_get_drvdata(rdev);
+ struct mc13xxx_regulator *mc13xxx_regulators = priv->mc13xxx_regulators;
int id = rdev_get_id(rdev);
int ret;
u32 dis_val = 0;
@@ -524,27 +289,28 @@
dev_dbg(rdev_get_dev(rdev), "%s id: %d\n", __func__, id);
/* Power Gate disable value is 1 */
- if (id == MC13783_REGU_PWGT1SPI ||
- id == MC13783_REGU_PWGT2SPI)
- dis_val = mc13783_regulators[id].enable_bit;
+ if (id == MC13783_REG_PWGT1SPI ||
+ id == MC13783_REG_PWGT2SPI)
+ dis_val = mc13xxx_regulators[id].enable_bit;
- mc13783_lock(priv->mc13783);
- ret = mc13783_powermisc_rmw(priv, mc13783_regulators[id].enable_bit,
+ mc13xxx_lock(priv->mc13xxx);
+ ret = mc13783_powermisc_rmw(priv, mc13xxx_regulators[id].enable_bit,
dis_val);
- mc13783_unlock(priv->mc13783);
+ mc13xxx_unlock(priv->mc13xxx);
return ret;
}
static int mc13783_gpo_regulator_is_enabled(struct regulator_dev *rdev)
{
- struct mc13783_regulator_priv *priv = rdev_get_drvdata(rdev);
+ struct mc13xxx_regulator_priv *priv = rdev_get_drvdata(rdev);
+ struct mc13xxx_regulator *mc13xxx_regulators = priv->mc13xxx_regulators;
int ret, id = rdev_get_id(rdev);
unsigned int val;
- mc13783_lock(priv->mc13783);
- ret = mc13783_reg_read(priv->mc13783, mc13783_regulators[id].reg, &val);
- mc13783_unlock(priv->mc13783);
+ mc13xxx_lock(priv->mc13xxx);
+ ret = mc13xxx_reg_read(priv->mc13xxx, mc13xxx_regulators[id].reg, &val);
+ mc13xxx_unlock(priv->mc13xxx);
if (ret)
return ret;
@@ -554,22 +320,22 @@
val = (val & ~MC13783_REG_POWERMISC_PWGTSPI_M) |
(priv->powermisc_pwgt_state ^ MC13783_REG_POWERMISC_PWGTSPI_M);
- return (val & mc13783_regulators[id].enable_bit) != 0;
+ return (val & mc13xxx_regulators[id].enable_bit) != 0;
}
static struct regulator_ops mc13783_gpo_regulator_ops = {
.enable = mc13783_gpo_regulator_enable,
.disable = mc13783_gpo_regulator_disable,
.is_enabled = mc13783_gpo_regulator_is_enabled,
- .list_voltage = mc13783_regulator_list_voltage,
- .set_voltage = mc13783_fixed_regulator_set_voltage,
- .get_voltage = mc13783_fixed_regulator_get_voltage,
+ .list_voltage = mc13xxx_regulator_list_voltage,
+ .set_voltage = mc13xxx_fixed_regulator_set_voltage,
+ .get_voltage = mc13xxx_fixed_regulator_get_voltage,
};
static int __devinit mc13783_regulator_probe(struct platform_device *pdev)
{
- struct mc13783_regulator_priv *priv;
- struct mc13783 *mc13783 = dev_get_drvdata(pdev->dev.parent);
+ struct mc13xxx_regulator_priv *priv;
+ struct mc13xxx *mc13783 = dev_get_drvdata(pdev->dev.parent);
struct mc13783_regulator_platform_data *pdata =
dev_get_platdata(&pdev->dev);
struct mc13783_regulator_init_data *init_data;
@@ -583,7 +349,8 @@
if (!priv)
return -ENOMEM;
- priv->mc13783 = mc13783;
+ priv->mc13xxx_regulators = mc13783_regulators;
+ priv->mc13xxx = mc13783;
for (i = 0; i < pdata->num_regulators; i++) {
init_data = &pdata->regulators[i];
@@ -613,7 +380,7 @@
static int __devexit mc13783_regulator_remove(struct platform_device *pdev)
{
- struct mc13783_regulator_priv *priv = platform_get_drvdata(pdev);
+ struct mc13xxx_regulator_priv *priv = platform_get_drvdata(pdev);
struct mc13783_regulator_platform_data *pdata =
dev_get_platdata(&pdev->dev);
int i;
diff --git a/drivers/regulator/mc13892-regulator.c b/drivers/regulator/mc13892-regulator.c
new file mode 100644
index 0000000..1b8f739
--- /dev/null
+++ b/drivers/regulator/mc13892-regulator.c
@@ -0,0 +1,635 @@
+/*
+ * Regulator Driver for Freescale MC13892 PMIC
+ *
+ * Copyright 2010 Yong Shen <yong.shen@linaro.org>
+ *
+ * Based on draft driver from Arnaud Patard <arnaud.patard@rtp-net.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/mfd/mc13892.h>
+#include <linux/regulator/machine.h>
+#include <linux/regulator/driver.h>
+#include <linux/platform_device.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/init.h>
+#include <linux/err.h>
+#include "mc13xxx.h"
+
+#define MC13892_REVISION 7
+
+#define MC13892_POWERCTL0 13
+#define MC13892_POWERCTL0_USEROFFSPI 3
+#define MC13892_POWERCTL0_VCOINCELLVSEL 20
+#define MC13892_POWERCTL0_VCOINCELLVSEL_M (7<<20)
+#define MC13892_POWERCTL0_VCOINCELLEN (1<<23)
+
+#define MC13892_SWITCHERS0_SWxHI (1<<23)
+
+#define MC13892_SWITCHERS0 24
+#define MC13892_SWITCHERS0_SW1VSEL 0
+#define MC13892_SWITCHERS0_SW1VSEL_M (0x1f<<0)
+#define MC13892_SWITCHERS0_SW1HI (1<<23)
+#define MC13892_SWITCHERS0_SW1EN 0
+
+#define MC13892_SWITCHERS1 25
+#define MC13892_SWITCHERS1_SW2VSEL 0
+#define MC13892_SWITCHERS1_SW2VSEL_M (0x1f<<0)
+#define MC13892_SWITCHERS1_SW2HI (1<<23)
+#define MC13892_SWITCHERS1_SW2EN 0
+
+#define MC13892_SWITCHERS2 26
+#define MC13892_SWITCHERS2_SW3VSEL 0
+#define MC13892_SWITCHERS2_SW3VSEL_M (0x1f<<0)
+#define MC13892_SWITCHERS2_SW3HI (1<<23)
+#define MC13892_SWITCHERS2_SW3EN 0
+
+#define MC13892_SWITCHERS3 27
+#define MC13892_SWITCHERS3_SW4VSEL 0
+#define MC13892_SWITCHERS3_SW4VSEL_M (0x1f<<0)
+#define MC13892_SWITCHERS3_SW4HI (1<<23)
+#define MC13892_SWITCHERS3_SW4EN 0
+
+#define MC13892_SWITCHERS4 28
+#define MC13892_SWITCHERS4_SW1MODE 0
+#define MC13892_SWITCHERS4_SW1MODE_AUTO (8<<0)
+#define MC13892_SWITCHERS4_SW1MODE_M (0xf<<0)
+#define MC13892_SWITCHERS4_SW2MODE 10
+#define MC13892_SWITCHERS4_SW2MODE_AUTO (8<<10)
+#define MC13892_SWITCHERS4_SW2MODE_M (0xf<<10)
+
+#define MC13892_SWITCHERS5 29
+#define MC13892_SWITCHERS5_SW3MODE 0
+#define MC13892_SWITCHERS5_SW3MODE_AUTO (8<<0)
+#define MC13892_SWITCHERS5_SW3MODE_M (0xf<<0)
+#define MC13892_SWITCHERS5_SW4MODE 8
+#define MC13892_SWITCHERS5_SW4MODE_AUTO (8<<8)
+#define MC13892_SWITCHERS5_SW4MODE_M (0xf<<8)
+#define MC13892_SWITCHERS5_SWBSTEN (1<<20)
+
+#define MC13892_REGULATORSETTING0 30
+#define MC13892_REGULATORSETTING0_VGEN1VSEL 0
+#define MC13892_REGULATORSETTING0_VDIGVSEL 4
+#define MC13892_REGULATORSETTING0_VGEN2VSEL 6
+#define MC13892_REGULATORSETTING0_VPLLVSEL 9
+#define MC13892_REGULATORSETTING0_VUSB2VSEL 11
+#define MC13892_REGULATORSETTING0_VGEN3VSEL 14
+#define MC13892_REGULATORSETTING0_VCAMVSEL 16
+
+#define MC13892_REGULATORSETTING0_VGEN1VSEL_M (3<<0)
+#define MC13892_REGULATORSETTING0_VDIGVSEL_M (3<<4)
+#define MC13892_REGULATORSETTING0_VGEN2VSEL_M (7<<6)
+#define MC13892_REGULATORSETTING0_VPLLVSEL_M (3<<9)
+#define MC13892_REGULATORSETTING0_VUSB2VSEL_M (3<<11)
+#define MC13892_REGULATORSETTING0_VGEN3VSEL_M (1<<14)
+#define MC13892_REGULATORSETTING0_VCAMVSEL_M (3<<16)
+
+#define MC13892_REGULATORSETTING1 31
+#define MC13892_REGULATORSETTING1_VVIDEOVSEL 2
+#define MC13892_REGULATORSETTING1_VAUDIOVSEL 4
+#define MC13892_REGULATORSETTING1_VSDVSEL 6
+
+#define MC13892_REGULATORSETTING1_VVIDEOVSEL_M (3<<2)
+#define MC13892_REGULATORSETTING1_VAUDIOVSEL_M (3<<4)
+#define MC13892_REGULATORSETTING1_VSDVSEL_M (7<<6)
+
+#define MC13892_REGULATORMODE0 32
+#define MC13892_REGULATORMODE0_VGEN1EN (1<<0)
+#define MC13892_REGULATORMODE0_VGEN1STDBY (1<<1)
+#define MC13892_REGULATORMODE0_VGEN1MODE (1<<2)
+#define MC13892_REGULATORMODE0_VIOHIEN (1<<3)
+#define MC13892_REGULATORMODE0_VIOHISTDBY (1<<4)
+#define MC13892_REGULATORMODE0_VIOHIMODE (1<<5)
+#define MC13892_REGULATORMODE0_VDIGEN (1<<9)
+#define MC13892_REGULATORMODE0_VDIGSTDBY (1<<10)
+#define MC13892_REGULATORMODE0_VDIGMODE (1<<11)
+#define MC13892_REGULATORMODE0_VGEN2EN (1<<12)
+#define MC13892_REGULATORMODE0_VGEN2STDBY (1<<13)
+#define MC13892_REGULATORMODE0_VGEN2MODE (1<<14)
+#define MC13892_REGULATORMODE0_VPLLEN (1<<15)
+#define MC13892_REGULATORMODE0_VPLLSTDBY (1<<16)
+#define MC13892_REGULATORMODE0_VPLLMODE (1<<17)
+#define MC13892_REGULATORMODE0_VUSB2EN (1<<18)
+#define MC13892_REGULATORMODE0_VUSB2STDBY (1<<19)
+#define MC13892_REGULATORMODE0_VUSB2MODE (1<<20)
+
+#define MC13892_REGULATORMODE1 33
+#define MC13892_REGULATORMODE1_VGEN3EN (1<<0)
+#define MC13892_REGULATORMODE1_VGEN3STDBY (1<<1)
+#define MC13892_REGULATORMODE1_VGEN3MODE (1<<2)
+#define MC13892_REGULATORMODE1_VCAMEN (1<<6)
+#define MC13892_REGULATORMODE1_VCAMSTDBY (1<<7)
+#define MC13892_REGULATORMODE1_VCAMMODE (1<<8)
+#define MC13892_REGULATORMODE1_VCAMCONFIGEN (1<<9)
+#define MC13892_REGULATORMODE1_VVIDEOEN (1<<12)
+#define MC13892_REGULATORMODE1_VVIDEOSTDBY (1<<13)
+#define MC13892_REGULATORMODE1_VVIDEOMODE (1<<14)
+#define MC13892_REGULATORMODE1_VAUDIOEN (1<<15)
+#define MC13892_REGULATORMODE1_VAUDIOSTDBY (1<<16)
+#define MC13892_REGULATORMODE1_VAUDIOMODE (1<<17)
+#define MC13892_REGULATORMODE1_VSDEN (1<<18)
+#define MC13892_REGULATORMODE1_VSDSTDBY (1<<19)
+#define MC13892_REGULATORMODE1_VSDMODE (1<<20)
+
+#define MC13892_POWERMISC 34
+#define MC13892_POWERMISC_GPO1EN (1<<6)
+#define MC13892_POWERMISC_GPO2EN (1<<8)
+#define MC13892_POWERMISC_GPO3EN (1<<10)
+#define MC13892_POWERMISC_GPO4EN (1<<12)
+#define MC13892_POWERMISC_PWGT1SPIEN (1<<15)
+#define MC13892_POWERMISC_PWGT2SPIEN (1<<16)
+#define MC13892_POWERMISC_GPO4ADINEN (1<<21)
+
+#define MC13892_POWERMISC_PWGTSPI_M (3 << 15)
+
+#define MC13892_USB1 50
+#define MC13892_USB1_VUSBEN (1<<3)
+
+static const int mc13892_vcoincell[] = {
+ 2500000, 2700000, 2800000, 2900000, 3000000, 3100000,
+ 3200000, 3300000,
+};
+
+static const int mc13892_sw1[] = {
+ 600000, 625000, 650000, 675000, 700000, 725000,
+ 750000, 775000, 800000, 825000, 850000, 875000,
+ 900000, 925000, 950000, 975000, 1000000, 1025000,
+ 1050000, 1075000, 1100000, 1125000, 1150000, 1175000,
+ 1200000, 1225000, 1250000, 1275000, 1300000, 1325000,
+ 1350000, 1375000
+};
+
+static const int mc13892_sw[] = {
+ 600000, 625000, 650000, 675000, 700000, 725000,
+ 750000, 775000, 800000, 825000, 850000, 875000,
+ 900000, 925000, 950000, 975000, 1000000, 1025000,
+ 1050000, 1075000, 1100000, 1125000, 1150000, 1175000,
+ 1200000, 1225000, 1250000, 1275000, 1300000, 1325000,
+ 1350000, 1375000, 1400000, 1425000, 1450000, 1475000,
+ 1500000, 1525000, 1550000, 1575000, 1600000, 1625000,
+ 1650000, 1675000, 1700000, 1725000, 1750000, 1775000,
+ 1800000, 1825000, 1850000, 1875000
+};
+
+static const int mc13892_swbst[] = {
+ 5000000,
+};
+
+static const int mc13892_viohi[] = {
+ 2775000,
+};
+
+static const int mc13892_vpll[] = {
+ 1050000, 1250000, 1650000, 1800000,
+};
+
+static const int mc13892_vdig[] = {
+ 1050000, 1250000, 1650000, 1800000,
+};
+
+static const int mc13892_vsd[] = {
+ 1800000, 2000000, 2600000, 2700000,
+ 2800000, 2900000, 3000000, 3150000,
+};
+
+static const int mc13892_vusb2[] = {
+ 2400000, 2600000, 2700000, 2775000,
+};
+
+static const int mc13892_vvideo[] = {
+ 2700000, 2775000, 2500000, 2600000,
+};
+
+static const int mc13892_vaudio[] = {
+ 2300000, 2500000, 2775000, 3000000,
+};
+
+static const int mc13892_vcam[] = {
+ 2500000, 2600000, 2750000, 3000000,
+};
+
+static const int mc13892_vgen1[] = {
+ 1200000, 1500000, 2775000, 3150000,
+};
+
+static const int mc13892_vgen2[] = {
+ 1200000, 1500000, 1600000, 1800000,
+ 2700000, 2800000, 3000000, 3150000,
+};
+
+static const int mc13892_vgen3[] = {
+ 1800000, 2900000,
+};
+
+static const int mc13892_vusb[] = {
+ 3300000,
+};
+
+static const int mc13892_gpo[] = {
+ 2750000,
+};
+
+static const int mc13892_pwgtdrv[] = {
+ 5000000,
+};
+
+static struct regulator_ops mc13892_gpo_regulator_ops;
+/* sw regulators need special care due to the "hi bit" */
+static struct regulator_ops mc13892_sw_regulator_ops;
+
+
+#define MC13892_FIXED_DEFINE(name, reg, voltages) \
+ MC13xxx_FIXED_DEFINE(MC13892_, name, reg, voltages, \
+ mc13xxx_fixed_regulator_ops)
+
+#define MC13892_GPO_DEFINE(name, reg, voltages) \
+ MC13xxx_GPO_DEFINE(MC13892_, name, reg, voltages, \
+ mc13892_gpo_regulator_ops)
+
+#define MC13892_SW_DEFINE(name, reg, vsel_reg, voltages) \
+ MC13xxx_DEFINE(MC13892_, name, reg, vsel_reg, voltages, \
+ mc13892_sw_regulator_ops)
+
+#define MC13892_DEFINE_REGU(name, reg, vsel_reg, voltages) \
+ MC13xxx_DEFINE(MC13892_, name, reg, vsel_reg, voltages, \
+ mc13xxx_regulator_ops)
+
+static struct mc13xxx_regulator mc13892_regulators[] = {
+ MC13892_DEFINE_REGU(VCOINCELL, POWERCTL0, POWERCTL0, mc13892_vcoincell),
+ MC13892_SW_DEFINE(SW1, SWITCHERS0, SWITCHERS0, mc13892_sw1),
+ MC13892_SW_DEFINE(SW2, SWITCHERS1, SWITCHERS1, mc13892_sw),
+ MC13892_SW_DEFINE(SW3, SWITCHERS2, SWITCHERS2, mc13892_sw),
+ MC13892_SW_DEFINE(SW4, SWITCHERS3, SWITCHERS3, mc13892_sw),
+ MC13892_FIXED_DEFINE(SWBST, SWITCHERS5, mc13892_swbst),
+ MC13892_FIXED_DEFINE(VIOHI, REGULATORMODE0, mc13892_viohi),
+ MC13892_DEFINE_REGU(VPLL, REGULATORMODE0, REGULATORSETTING0, \
+ mc13892_vpll),
+ MC13892_DEFINE_REGU(VDIG, REGULATORMODE0, REGULATORSETTING0, \
+ mc13892_vdig),
+ MC13892_DEFINE_REGU(VSD, REGULATORMODE1, REGULATORSETTING1, \
+ mc13892_vsd),
+ MC13892_DEFINE_REGU(VUSB2, REGULATORMODE0, REGULATORSETTING0, \
+ mc13892_vusb2),
+ MC13892_DEFINE_REGU(VVIDEO, REGULATORMODE1, REGULATORSETTING1, \
+ mc13892_vvideo),
+ MC13892_DEFINE_REGU(VAUDIO, REGULATORMODE1, REGULATORSETTING1, \
+ mc13892_vaudio),
+ MC13892_DEFINE_REGU(VCAM, REGULATORMODE1, REGULATORSETTING0, \
+ mc13892_vcam),
+ MC13892_DEFINE_REGU(VGEN1, REGULATORMODE0, REGULATORSETTING0, \
+ mc13892_vgen1),
+ MC13892_DEFINE_REGU(VGEN2, REGULATORMODE0, REGULATORSETTING0, \
+ mc13892_vgen2),
+ MC13892_DEFINE_REGU(VGEN3, REGULATORMODE1, REGULATORSETTING0, \
+ mc13892_vgen3),
+ MC13892_FIXED_DEFINE(VUSB, USB1, mc13892_vusb),
+ MC13892_GPO_DEFINE(GPO1, POWERMISC, mc13892_gpo),
+ MC13892_GPO_DEFINE(GPO2, POWERMISC, mc13892_gpo),
+ MC13892_GPO_DEFINE(GPO3, POWERMISC, mc13892_gpo),
+ MC13892_GPO_DEFINE(GPO4, POWERMISC, mc13892_gpo),
+ MC13892_GPO_DEFINE(PWGT1SPI, POWERMISC, mc13892_pwgtdrv),
+ MC13892_GPO_DEFINE(PWGT2SPI, POWERMISC, mc13892_pwgtdrv),
+};
+
+static int mc13892_powermisc_rmw(struct mc13xxx_regulator_priv *priv, u32 mask,
+ u32 val)
+{
+ struct mc13xxx *mc13892 = priv->mc13xxx;
+ int ret;
+ u32 valread;
+
+ BUG_ON(val & ~mask);
+
+ ret = mc13xxx_reg_read(mc13892, MC13892_POWERMISC, &valread);
+ if (ret)
+ return ret;
+
+ /* Update the stored state for Power Gates. */
+ priv->powermisc_pwgt_state =
+ (priv->powermisc_pwgt_state & ~mask) | val;
+ priv->powermisc_pwgt_state &= MC13892_POWERMISC_PWGTSPI_M;
+
+ /* Construct the new register value */
+ valread = (valread & ~mask) | val;
+ /* Overwrite the PWGTxEN with the stored version */
+ valread = (valread & ~MC13892_POWERMISC_PWGTSPI_M) |
+ priv->powermisc_pwgt_state;
+
+ return mc13xxx_reg_write(mc13892, MC13892_POWERMISC, valread);
+}
+
+static int mc13892_gpo_regulator_enable(struct regulator_dev *rdev)
+{
+ struct mc13xxx_regulator_priv *priv = rdev_get_drvdata(rdev);
+ int id = rdev_get_id(rdev);
+ int ret;
+ u32 en_val = mc13892_regulators[id].enable_bit;
+ u32 mask = mc13892_regulators[id].enable_bit;
+
+ dev_dbg(rdev_get_dev(rdev), "%s id: %d\n", __func__, id);
+
+ /* Power Gate enable value is 0 */
+ if (id == MC13892_PWGT1SPI || id == MC13892_PWGT2SPI)
+ en_val = 0;
+
+ if (id == MC13892_GPO4)
+ mask |= MC13892_POWERMISC_GPO4ADINEN;
+
+ mc13xxx_lock(priv->mc13xxx);
+ ret = mc13892_powermisc_rmw(priv, mask, en_val);
+ mc13xxx_unlock(priv->mc13xxx);
+
+ return ret;
+}
+
+static int mc13892_gpo_regulator_disable(struct regulator_dev *rdev)
+{
+ struct mc13xxx_regulator_priv *priv = rdev_get_drvdata(rdev);
+ int id = rdev_get_id(rdev);
+ int ret;
+ u32 dis_val = 0;
+
+ dev_dbg(rdev_get_dev(rdev), "%s id: %d\n", __func__, id);
+
+ /* Power Gate disable value is 1 */
+ if (id == MC13892_PWGT1SPI || id == MC13892_PWGT2SPI)
+ dis_val = mc13892_regulators[id].enable_bit;
+
+ mc13xxx_lock(priv->mc13xxx);
+ ret = mc13892_powermisc_rmw(priv, mc13892_regulators[id].enable_bit,
+ dis_val);
+ mc13xxx_unlock(priv->mc13xxx);
+
+ return ret;
+}
+
+static int mc13892_gpo_regulator_is_enabled(struct regulator_dev *rdev)
+{
+ struct mc13xxx_regulator_priv *priv = rdev_get_drvdata(rdev);
+ int ret, id = rdev_get_id(rdev);
+ unsigned int val;
+
+ mc13xxx_lock(priv->mc13xxx);
+ ret = mc13xxx_reg_read(priv->mc13xxx, mc13892_regulators[id].reg, &val);
+ mc13xxx_unlock(priv->mc13xxx);
+
+ if (ret)
+ return ret;
+
+ /* Power Gates state is stored in powermisc_pwgt_state
+ * where the meaning of bits is negated */
+ val = (val & ~MC13892_POWERMISC_PWGTSPI_M) |
+ (priv->powermisc_pwgt_state ^ MC13892_POWERMISC_PWGTSPI_M);
+
+ return (val & mc13892_regulators[id].enable_bit) != 0;
+}
+
+
+static struct regulator_ops mc13892_gpo_regulator_ops = {
+ .enable = mc13892_gpo_regulator_enable,
+ .disable = mc13892_gpo_regulator_disable,
+ .is_enabled = mc13892_gpo_regulator_is_enabled,
+ .list_voltage = mc13xxx_regulator_list_voltage,
+ .set_voltage = mc13xxx_fixed_regulator_set_voltage,
+ .get_voltage = mc13xxx_fixed_regulator_get_voltage,
+};
+
+static int mc13892_sw_regulator_get_voltage(struct regulator_dev *rdev)
+{
+ struct mc13xxx_regulator_priv *priv = rdev_get_drvdata(rdev);
+ int ret, id = rdev_get_id(rdev);
+ unsigned int val, hi;
+
+ dev_dbg(rdev_get_dev(rdev), "%s id: %d\n", __func__, id);
+
+ mc13xxx_lock(priv->mc13xxx);
+ ret = mc13xxx_reg_read(priv->mc13xxx,
+ mc13892_regulators[id].vsel_reg, &val);
+ mc13xxx_unlock(priv->mc13xxx);
+ if (ret)
+ return ret;
+
+ hi = val & MC13892_SWITCHERS0_SWxHI;
+ val = (val & mc13892_regulators[id].vsel_mask)
+ >> mc13892_regulators[id].vsel_shift;
+
+ dev_dbg(rdev_get_dev(rdev), "%s id: %d val: %d\n", __func__, id, val);
+
+ if (hi)
+ val = (25000 * val) + 1100000;
+ else
+ val = (25000 * val) + 600000;
+
+ return val;
+}
+
+static int mc13892_sw_regulator_set_voltage(struct regulator_dev *rdev,
+ int min_uV, int max_uV, unsigned *selector)
+{
+ struct mc13xxx_regulator_priv *priv = rdev_get_drvdata(rdev);
+ int hi, value, val, mask, id = rdev_get_id(rdev);
+ int ret;
+
+ dev_dbg(rdev_get_dev(rdev), "%s id: %d min_uV: %d max_uV: %d\n",
+ __func__, id, min_uV, max_uV);
+
+ /* Find the best index */
+ value = mc13xxx_get_best_voltage_index(rdev, min_uV, max_uV);
+ dev_dbg(rdev_get_dev(rdev), "%s best value: %d\n", __func__, value);
+ if (value < 0)
+ return value;
+
+ value = mc13892_regulators[id].voltages[value];
+
+ mc13xxx_lock(priv->mc13xxx);
+ ret = mc13xxx_reg_read(priv->mc13xxx,
+ mc13892_regulators[id].vsel_reg, &val);
+ if (ret)
+ goto err;
+
+ hi = val & MC13892_SWITCHERS0_SWxHI;
+ if (value > 1375)
+ hi = 1;
+ if (value < 1100)
+ hi = 0;
+
+ if (hi) {
+ value = (value - 1100000) / 25000;
+ value |= MC13892_SWITCHERS0_SWxHI;
+ } else
+ value = (value - 600000) / 25000;
+
+ mask = mc13892_regulators[id].vsel_mask | MC13892_SWITCHERS0_SWxHI;
+ ret = mc13xxx_reg_rmw(priv->mc13xxx, mc13892_regulators[id].vsel_reg,
+ mask, value << mc13892_regulators[id].vsel_shift);
+err:
+ mc13xxx_unlock(priv->mc13xxx);
+
+ return ret;
+}
+
+static struct regulator_ops mc13892_sw_regulator_ops = {
+ .is_enabled = mc13xxx_sw_regulator_is_enabled,
+ .list_voltage = mc13xxx_regulator_list_voltage,
+ .set_voltage = mc13892_sw_regulator_set_voltage,
+ .get_voltage = mc13892_sw_regulator_get_voltage,
+};
+
+static int mc13892_vcam_set_mode(struct regulator_dev *rdev, unsigned int mode)
+{
+ unsigned int en_val = 0;
+ struct mc13xxx_regulator_priv *priv = rdev_get_drvdata(rdev);
+ int ret, id = rdev_get_id(rdev);
+
+ if (mode == REGULATOR_MODE_FAST)
+ en_val = MC13892_REGULATORMODE1_VCAMCONFIGEN;
+
+ mc13xxx_lock(priv->mc13xxx);
+ ret = mc13xxx_reg_rmw(priv->mc13xxx, mc13892_regulators[id].reg,
+ MC13892_REGULATORMODE1_VCAMCONFIGEN, en_val);
+ mc13xxx_unlock(priv->mc13xxx);
+
+ return ret;
+}
+
+static unsigned int mc13892_vcam_get_mode(struct regulator_dev *rdev)
+{
+ struct mc13xxx_regulator_priv *priv = rdev_get_drvdata(rdev);
+ int ret, id = rdev_get_id(rdev);
+ unsigned int val;
+
+ mc13xxx_lock(priv->mc13xxx);
+ ret = mc13xxx_reg_read(priv->mc13xxx, mc13892_regulators[id].reg, &val);
+ mc13xxx_unlock(priv->mc13xxx);
+
+ if (ret)
+ return ret;
+
+ if (val & MC13892_REGULATORMODE1_VCAMCONFIGEN)
+ return REGULATOR_MODE_FAST;
+
+ return REGULATOR_MODE_NORMAL;
+}
+
+
+static int __devinit mc13892_regulator_probe(struct platform_device *pdev)
+{
+ struct mc13xxx_regulator_priv *priv;
+ struct mc13xxx *mc13892 = dev_get_drvdata(pdev->dev.parent);
+ struct mc13xxx_regulator_platform_data *pdata =
+ dev_get_platdata(&pdev->dev);
+ struct mc13xxx_regulator_init_data *init_data;
+ int i, ret;
+ u32 val;
+
+ priv = kzalloc(sizeof(*priv) +
+ pdata->num_regulators * sizeof(priv->regulators[0]),
+ GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ priv->mc13xxx_regulators = mc13892_regulators;
+ priv->mc13xxx = mc13892;
+
+ mc13xxx_lock(mc13892);
+ ret = mc13xxx_reg_read(mc13892, MC13892_REVISION, &val);
+ if (ret)
+ goto err_free;
+
+ /* enable switch auto mode */
+ if ((val & 0x0000FFFF) == 0x45d0) {
+ ret = mc13xxx_reg_rmw(mc13892, MC13892_SWITCHERS4,
+ MC13892_SWITCHERS4_SW1MODE_M |
+ MC13892_SWITCHERS4_SW2MODE_M,
+ MC13892_SWITCHERS4_SW1MODE_AUTO |
+ MC13892_SWITCHERS4_SW2MODE_AUTO);
+ if (ret)
+ goto err_free;
+
+ ret = mc13xxx_reg_rmw(mc13892, MC13892_SWITCHERS5,
+ MC13892_SWITCHERS5_SW3MODE_M |
+ MC13892_SWITCHERS5_SW4MODE_M,
+ MC13892_SWITCHERS5_SW3MODE_AUTO |
+ MC13892_SWITCHERS5_SW4MODE_AUTO);
+ if (ret)
+ goto err_free;
+ }
+ mc13xxx_unlock(mc13892);
+
+ mc13892_regulators[MC13892_VCAM].desc.ops->set_mode
+ = mc13892_vcam_set_mode;
+ mc13892_regulators[MC13892_VCAM].desc.ops->get_mode
+ = mc13892_vcam_get_mode;
+ for (i = 0; i < pdata->num_regulators; i++) {
+ init_data = &pdata->regulators[i];
+ priv->regulators[i] = regulator_register(
+ &mc13892_regulators[init_data->id].desc,
+ &pdev->dev, init_data->init_data, priv);
+
+ if (IS_ERR(priv->regulators[i])) {
+ dev_err(&pdev->dev, "failed to register regulator %s\n",
+ mc13892_regulators[i].desc.name);
+ ret = PTR_ERR(priv->regulators[i]);
+ goto err;
+ }
+ }
+
+ platform_set_drvdata(pdev, priv);
+
+ return 0;
+err:
+ while (--i >= 0)
+ regulator_unregister(priv->regulators[i]);
+
+err_free:
+ mc13xxx_unlock(mc13892);
+ kfree(priv);
+
+ return ret;
+}
+
+static int __devexit mc13892_regulator_remove(struct platform_device *pdev)
+{
+ struct mc13xxx_regulator_priv *priv = platform_get_drvdata(pdev);
+ struct mc13xxx_regulator_platform_data *pdata =
+ dev_get_platdata(&pdev->dev);
+ int i;
+
+ platform_set_drvdata(pdev, NULL);
+
+ for (i = 0; i < pdata->num_regulators; i++)
+ regulator_unregister(priv->regulators[i]);
+
+ kfree(priv);
+ return 0;
+}
+
+static struct platform_driver mc13892_regulator_driver = {
+ .driver = {
+ .name = "mc13892-regulator",
+ .owner = THIS_MODULE,
+ },
+ .remove = __devexit_p(mc13892_regulator_remove),
+ .probe = mc13892_regulator_probe,
+};
+
+static int __init mc13892_regulator_init(void)
+{
+ return platform_driver_register(&mc13892_regulator_driver);
+}
+subsys_initcall(mc13892_regulator_init);
+
+static void __exit mc13892_regulator_exit(void)
+{
+ platform_driver_unregister(&mc13892_regulator_driver);
+}
+module_exit(mc13892_regulator_exit);
+
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("Yong Shen <yong.shen@linaro.org>");
+MODULE_DESCRIPTION("Regulator Driver for Freescale MC13892 PMIC");
+MODULE_ALIAS("platform:mc13892-regulator");
diff --git a/drivers/regulator/mc13xxx-regulator-core.c b/drivers/regulator/mc13xxx-regulator-core.c
new file mode 100644
index 0000000..f53d31b
--- /dev/null
+++ b/drivers/regulator/mc13xxx-regulator-core.c
@@ -0,0 +1,241 @@
+/*
+ * Regulator Driver for Freescale MC13xxx PMIC
+ *
+ * Copyright 2010 Yong Shen <yong.shen@linaro.org>
+ *
+ * Based on mc13783 regulator driver :
+ * Copyright (C) 2008 Sascha Hauer, Pengutronix <s.hauer@pengutronix.de>
+ * Copyright 2009 Alberto Panizzo <maramaopercheseimorto@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Regs infos taken from mc13xxx drivers from freescale and mc13xxx.pdf file
+ * from freescale
+ */
+
+#include <linux/mfd/mc13xxx.h>
+#include <linux/regulator/machine.h>
+#include <linux/regulator/driver.h>
+#include <linux/platform_device.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/init.h>
+#include <linux/err.h>
+#include "mc13xxx.h"
+
+static int mc13xxx_regulator_enable(struct regulator_dev *rdev)
+{
+ struct mc13xxx_regulator_priv *priv = rdev_get_drvdata(rdev);
+ struct mc13xxx_regulator *mc13xxx_regulators = priv->mc13xxx_regulators;
+ int id = rdev_get_id(rdev);
+ int ret;
+
+ dev_dbg(rdev_get_dev(rdev), "%s id: %d\n", __func__, id);
+
+ mc13xxx_lock(priv->mc13xxx);
+ ret = mc13xxx_reg_rmw(priv->mc13xxx, mc13xxx_regulators[id].reg,
+ mc13xxx_regulators[id].enable_bit,
+ mc13xxx_regulators[id].enable_bit);
+ mc13xxx_unlock(priv->mc13xxx);
+
+ return ret;
+}
+
+static int mc13xxx_regulator_disable(struct regulator_dev *rdev)
+{
+ struct mc13xxx_regulator_priv *priv = rdev_get_drvdata(rdev);
+ struct mc13xxx_regulator *mc13xxx_regulators = priv->mc13xxx_regulators;
+ int id = rdev_get_id(rdev);
+ int ret;
+
+ dev_dbg(rdev_get_dev(rdev), "%s id: %d\n", __func__, id);
+
+ mc13xxx_lock(priv->mc13xxx);
+ ret = mc13xxx_reg_rmw(priv->mc13xxx, mc13xxx_regulators[id].reg,
+ mc13xxx_regulators[id].enable_bit, 0);
+ mc13xxx_unlock(priv->mc13xxx);
+
+ return ret;
+}
+
+static int mc13xxx_regulator_is_enabled(struct regulator_dev *rdev)
+{
+ struct mc13xxx_regulator_priv *priv = rdev_get_drvdata(rdev);
+ struct mc13xxx_regulator *mc13xxx_regulators = priv->mc13xxx_regulators;
+ int ret, id = rdev_get_id(rdev);
+ unsigned int val;
+
+ mc13xxx_lock(priv->mc13xxx);
+ ret = mc13xxx_reg_read(priv->mc13xxx, mc13xxx_regulators[id].reg, &val);
+ mc13xxx_unlock(priv->mc13xxx);
+
+ if (ret)
+ return ret;
+
+ return (val & mc13xxx_regulators[id].enable_bit) != 0;
+}
+
+int mc13xxx_regulator_list_voltage(struct regulator_dev *rdev,
+ unsigned selector)
+{
+ int id = rdev_get_id(rdev);
+ struct mc13xxx_regulator_priv *priv = rdev_get_drvdata(rdev);
+ struct mc13xxx_regulator *mc13xxx_regulators = priv->mc13xxx_regulators;
+
+ if (selector >= mc13xxx_regulators[id].desc.n_voltages)
+ return -EINVAL;
+
+ return mc13xxx_regulators[id].voltages[selector];
+}
+EXPORT_SYMBOL_GPL(mc13xxx_regulator_list_voltage);
+
+int mc13xxx_get_best_voltage_index(struct regulator_dev *rdev,
+ int min_uV, int max_uV)
+{
+ struct mc13xxx_regulator_priv *priv = rdev_get_drvdata(rdev);
+ struct mc13xxx_regulator *mc13xxx_regulators = priv->mc13xxx_regulators;
+ int reg_id = rdev_get_id(rdev);
+ int i;
+ int bestmatch;
+ int bestindex;
+
+ /*
+ * Locate the minimum voltage fitting the criteria on
+ * this regulator. The switchable voltages are not
+ * in strict falling order so we need to check them
+ * all for the best match.
+ */
+ bestmatch = INT_MAX;
+ bestindex = -1;
+ for (i = 0; i < mc13xxx_regulators[reg_id].desc.n_voltages; i++) {
+ if (mc13xxx_regulators[reg_id].voltages[i] >= min_uV &&
+ mc13xxx_regulators[reg_id].voltages[i] < bestmatch) {
+ bestmatch = mc13xxx_regulators[reg_id].voltages[i];
+ bestindex = i;
+ }
+ }
+
+ if (bestindex < 0 || bestmatch > max_uV) {
+ dev_warn(&rdev->dev, "no possible value for %d<=x<=%d uV\n",
+ min_uV, max_uV);
+ return -EINVAL;
+ }
+ return bestindex;
+}
+EXPORT_SYMBOL_GPL(mc13xxx_get_best_voltage_index);
+
+static int mc13xxx_regulator_set_voltage(struct regulator_dev *rdev, int min_uV,
+ int max_uV, unsigned *selector)
+{
+ struct mc13xxx_regulator_priv *priv = rdev_get_drvdata(rdev);
+ struct mc13xxx_regulator *mc13xxx_regulators = priv->mc13xxx_regulators;
+ int value, id = rdev_get_id(rdev);
+ int ret;
+
+ dev_dbg(rdev_get_dev(rdev), "%s id: %d min_uV: %d max_uV: %d\n",
+ __func__, id, min_uV, max_uV);
+
+ /* Find the best index */
+ value = mc13xxx_get_best_voltage_index(rdev, min_uV, max_uV);
+ dev_dbg(rdev_get_dev(rdev), "%s best value: %d\n", __func__, value);
+ if (value < 0)
+ return value;
+
+ mc13xxx_lock(priv->mc13xxx);
+ ret = mc13xxx_reg_rmw(priv->mc13xxx, mc13xxx_regulators[id].vsel_reg,
+ mc13xxx_regulators[id].vsel_mask,
+ value << mc13xxx_regulators[id].vsel_shift);
+ mc13xxx_unlock(priv->mc13xxx);
+
+ return ret;
+}
+
+static int mc13xxx_regulator_get_voltage(struct regulator_dev *rdev)
+{
+ struct mc13xxx_regulator_priv *priv = rdev_get_drvdata(rdev);
+ struct mc13xxx_regulator *mc13xxx_regulators = priv->mc13xxx_regulators;
+ int ret, id = rdev_get_id(rdev);
+ unsigned int val;
+
+ dev_dbg(rdev_get_dev(rdev), "%s id: %d\n", __func__, id);
+
+ mc13xxx_lock(priv->mc13xxx);
+ ret = mc13xxx_reg_read(priv->mc13xxx,
+ mc13xxx_regulators[id].vsel_reg, &val);
+ mc13xxx_unlock(priv->mc13xxx);
+
+ if (ret)
+ return ret;
+
+ val = (val & mc13xxx_regulators[id].vsel_mask)
+ >> mc13xxx_regulators[id].vsel_shift;
+
+ dev_dbg(rdev_get_dev(rdev), "%s id: %d val: %d\n", __func__, id, val);
+
+ BUG_ON(val < 0 || val > mc13xxx_regulators[id].desc.n_voltages);
+
+ return mc13xxx_regulators[id].voltages[val];
+}
+
+struct regulator_ops mc13xxx_regulator_ops = {
+ .enable = mc13xxx_regulator_enable,
+ .disable = mc13xxx_regulator_disable,
+ .is_enabled = mc13xxx_regulator_is_enabled,
+ .list_voltage = mc13xxx_regulator_list_voltage,
+ .set_voltage = mc13xxx_regulator_set_voltage,
+ .get_voltage = mc13xxx_regulator_get_voltage,
+};
+EXPORT_SYMBOL_GPL(mc13xxx_regulator_ops);
+
+int mc13xxx_fixed_regulator_set_voltage(struct regulator_dev *rdev, int min_uV,
+ int max_uV, unsigned *selector)
+{
+ struct mc13xxx_regulator_priv *priv = rdev_get_drvdata(rdev);
+ struct mc13xxx_regulator *mc13xxx_regulators = priv->mc13xxx_regulators;
+ int id = rdev_get_id(rdev);
+
+ dev_dbg(rdev_get_dev(rdev), "%s id: %d min_uV: %d max_uV: %d\n",
+ __func__, id, min_uV, max_uV);
+
+ if (min_uV >= mc13xxx_regulators[id].voltages[0] &&
+ max_uV <= mc13xxx_regulators[id].voltages[0])
+ return 0;
+ else
+ return -EINVAL;
+}
+EXPORT_SYMBOL_GPL(mc13xxx_fixed_regulator_set_voltage);
+
+int mc13xxx_fixed_regulator_get_voltage(struct regulator_dev *rdev)
+{
+ struct mc13xxx_regulator_priv *priv = rdev_get_drvdata(rdev);
+ struct mc13xxx_regulator *mc13xxx_regulators = priv->mc13xxx_regulators;
+ int id = rdev_get_id(rdev);
+
+ dev_dbg(rdev_get_dev(rdev), "%s id: %d\n", __func__, id);
+
+ return mc13xxx_regulators[id].voltages[0];
+}
+EXPORT_SYMBOL_GPL(mc13xxx_fixed_regulator_get_voltage);
+
+struct regulator_ops mc13xxx_fixed_regulator_ops = {
+ .enable = mc13xxx_regulator_enable,
+ .disable = mc13xxx_regulator_disable,
+ .is_enabled = mc13xxx_regulator_is_enabled,
+ .list_voltage = mc13xxx_regulator_list_voltage,
+ .set_voltage = mc13xxx_fixed_regulator_set_voltage,
+ .get_voltage = mc13xxx_fixed_regulator_get_voltage,
+};
+EXPORT_SYMBOL_GPL(mc13xxx_fixed_regulator_ops);
+
+int mc13xxx_sw_regulator_is_enabled(struct regulator_dev *rdev)
+{
+ return 1;
+}
+EXPORT_SYMBOL_GPL(mc13xxx_sw_regulator_is_enabled);
+
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("Yong Shen <yong.shen@linaro.org>");
+MODULE_DESCRIPTION("Regulator Driver for Freescale MC13xxx PMIC");
+MODULE_ALIAS("mc13xxx-regulator-core");
diff --git a/drivers/regulator/mc13xxx.h b/drivers/regulator/mc13xxx.h
new file mode 100644
index 0000000..2775826
--- /dev/null
+++ b/drivers/regulator/mc13xxx.h
@@ -0,0 +1,101 @@
+/*
+ * mc13xxx.h - regulators for the Freescale mc13xxx PMIC
+ *
+ * Copyright (C) 2010 Yong Shen <yong.shen@linaro.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#ifndef __LINUX_REGULATOR_MC13XXX_H
+#define __LINUX_REGULATOR_MC13XXX_H
+
+#include <linux/regulator/driver.h>
+
+struct mc13xxx_regulator {
+ struct regulator_desc desc;
+ int reg;
+ int enable_bit;
+ int vsel_reg;
+ int vsel_shift;
+ int vsel_mask;
+ int hi_bit;
+ int const *voltages;
+};
+
+struct mc13xxx_regulator_priv {
+ struct mc13xxx *mc13xxx;
+ u32 powermisc_pwgt_state;
+ struct mc13xxx_regulator *mc13xxx_regulators;
+ struct regulator_dev *regulators[];
+};
+
+extern int mc13xxx_sw_regulator(struct regulator_dev *rdev);
+extern int mc13xxx_sw_regulator_is_enabled(struct regulator_dev *rdev);
+extern int mc13xxx_get_best_voltage_index(struct regulator_dev *rdev,
+ int min_uV, int max_uV);
+extern int mc13xxx_regulator_list_voltage(struct regulator_dev *rdev,
+ unsigned selector);
+extern int mc13xxx_fixed_regulator_set_voltage(struct regulator_dev *rdev,
+ int min_uV, int max_uV, unsigned *selector);
+extern int mc13xxx_fixed_regulator_get_voltage(struct regulator_dev *rdev);
+
+extern struct regulator_ops mc13xxx_regulator_ops;
+extern struct regulator_ops mc13xxx_fixed_regulator_ops;
+
+#define MC13xxx_DEFINE(prefix, _name, _reg, _vsel_reg, _voltages, _ops) \
+ [prefix ## _name] = { \
+ .desc = { \
+ .name = #prefix "_" #_name, \
+ .n_voltages = ARRAY_SIZE(_voltages), \
+ .ops = &_ops, \
+ .type = REGULATOR_VOLTAGE, \
+ .id = prefix ## _name, \
+ .owner = THIS_MODULE, \
+ }, \
+ .reg = prefix ## _reg, \
+ .enable_bit = prefix ## _reg ## _ ## _name ## EN, \
+ .vsel_reg = prefix ## _vsel_reg, \
+ .vsel_shift = prefix ## _vsel_reg ## _ ## _name ## VSEL,\
+ .vsel_mask = prefix ## _vsel_reg ## _ ## _name ## VSEL_M,\
+ .voltages = _voltages, \
+ }
+
+#define MC13xxx_FIXED_DEFINE(prefix, _name, _reg, _voltages, _ops) \
+ [prefix ## _name] = { \
+ .desc = { \
+ .name = #prefix "_" #_name, \
+ .n_voltages = ARRAY_SIZE(_voltages), \
+ .ops = &_ops, \
+ .type = REGULATOR_VOLTAGE, \
+ .id = prefix ## _name, \
+ .owner = THIS_MODULE, \
+ }, \
+ .reg = prefix ## _reg, \
+ .enable_bit = prefix ## _reg ## _ ## _name ## EN, \
+ .voltages = _voltages, \
+ }
+
+#define MC13xxx_GPO_DEFINE(prefix, _name, _reg, _voltages, _ops) \
+ [prefix ## _name] = { \
+ .desc = { \
+ .name = #prefix "_" #_name, \
+ .n_voltages = ARRAY_SIZE(_voltages), \
+ .ops = &_ops, \
+ .type = REGULATOR_VOLTAGE, \
+ .id = prefix ## _name, \
+ .owner = THIS_MODULE, \
+ }, \
+ .reg = prefix ## _reg, \
+ .enable_bit = prefix ## _reg ## _ ## _name ## EN, \
+ .voltages = _voltages, \
+ }
+
+#define MC13xxx_DEFINE_SW(_name, _reg, _vsel_reg, _voltages, ops) \
+ MC13xxx_DEFINE(SW, _name, _reg, _vsel_reg, _voltages, ops)
+#define MC13xxx_DEFINE_REGU(_name, _reg, _vsel_reg, _voltages, ops) \
+ MC13xxx_DEFINE(REGU, _name, _reg, _vsel_reg, _voltages, ops)
+
+#endif
diff --git a/drivers/regulator/pcap-regulator.c b/drivers/regulator/pcap-regulator.c
index 29d0566..31f6e11 100644
--- a/drivers/regulator/pcap-regulator.c
+++ b/drivers/regulator/pcap-regulator.c
@@ -151,7 +151,8 @@
};
static int pcap_regulator_set_voltage(struct regulator_dev *rdev,
- int min_uV, int max_uV)
+ int min_uV, int max_uV,
+ unsigned *selector)
{
struct pcap_regulator *vreg = &vreg_table[rdev_get_id(rdev)];
void *pcap = rdev_get_drvdata(rdev);
@@ -170,10 +171,12 @@
i = 0;
uV = vreg->voltage_table[i] * 1000;
- if (min_uV <= uV && uV <= max_uV)
+ if (min_uV <= uV && uV <= max_uV) {
+ *selector = i;
return ezx_pcap_set_bits(pcap, vreg->reg,
(vreg->n_voltages - 1) << vreg->index,
i << vreg->index);
+ }
if (i == 0 && rdev_get_id(rdev) == V1)
i = vreg->n_voltages - 1;
diff --git a/drivers/regulator/pcf50633-regulator.c b/drivers/regulator/pcf50633-regulator.c
index c8f41dc..69a11d9 100644
--- a/drivers/regulator/pcf50633-regulator.c
+++ b/drivers/regulator/pcf50633-regulator.c
@@ -108,7 +108,8 @@
}
static int pcf50633_regulator_set_voltage(struct regulator_dev *rdev,
- int min_uV, int max_uV)
+ int min_uV, int max_uV,
+ unsigned *selector)
{
struct pcf50633 *pcf;
int regulator_id, millivolts;
@@ -147,6 +148,8 @@
return -EINVAL;
}
+ *selector = volt_bits;
+
return pcf50633_reg_write(pcf, regnr, volt_bits);
}
diff --git a/drivers/regulator/tps65023-regulator.c b/drivers/regulator/tps65023-regulator.c
index cd6d4fc..60a7ca5 100644
--- a/drivers/regulator/tps65023-regulator.c
+++ b/drivers/regulator/tps65023-regulator.c
@@ -321,7 +321,8 @@
}
static int tps65023_dcdc_set_voltage(struct regulator_dev *dev,
- int min_uV, int max_uV)
+ int min_uV, int max_uV,
+ unsigned *selector)
{
struct tps_pmic *tps = rdev_get_drvdata(dev);
int dcdc = rdev_get_id(dev);
@@ -346,6 +347,8 @@
break;
}
+ *selector = vsel;
+
/* write to the register in case we found a match */
if (vsel == tps->info[dcdc]->table_len)
return -EINVAL;
@@ -371,7 +374,7 @@
}
static int tps65023_ldo_set_voltage(struct regulator_dev *dev,
- int min_uV, int max_uV)
+ int min_uV, int max_uV, unsigned *selector)
{
struct tps_pmic *tps = rdev_get_drvdata(dev);
int data, vsel, ldo = rdev_get_id(dev);
@@ -396,6 +399,8 @@
if (vsel == tps->info[ldo]->table_len)
return -EINVAL;
+ *selector = vsel;
+
data = tps_65023_reg_read(tps, TPS65023_REG_LDO_CTRL);
if (data < 0)
return data;
diff --git a/drivers/regulator/tps6507x-regulator.c b/drivers/regulator/tps6507x-regulator.c
index 020f587..0647552 100644
--- a/drivers/regulator/tps6507x-regulator.c
+++ b/drivers/regulator/tps6507x-regulator.c
@@ -369,7 +369,8 @@
}
static int tps6507x_pmic_dcdc_set_voltage(struct regulator_dev *dev,
- int min_uV, int max_uV)
+ int min_uV, int max_uV,
+ unsigned *selector)
{
struct tps6507x_pmic *tps = rdev_get_drvdata(dev);
int data, vsel, dcdc = rdev_get_id(dev);
@@ -415,6 +416,8 @@
if (vsel == tps->info[dcdc]->table_len)
return -EINVAL;
+ *selector = vsel;
+
data = tps6507x_pmic_reg_read(tps, reg);
if (data < 0)
return data;
@@ -450,7 +453,8 @@
}
static int tps6507x_pmic_ldo_set_voltage(struct regulator_dev *dev,
- int min_uV, int max_uV)
+ int min_uV, int max_uV,
+ unsigned *selector)
{
struct tps6507x_pmic *tps = rdev_get_drvdata(dev);
int data, vsel, ldo = rdev_get_id(dev);
@@ -483,6 +487,8 @@
if (vsel == tps->info[ldo]->table_len)
return -EINVAL;
+ *selector = vsel;
+
data = tps6507x_pmic_reg_read(tps, reg);
if (data < 0)
return data;
diff --git a/drivers/regulator/tps6524x-regulator.c b/drivers/regulator/tps6524x-regulator.c
new file mode 100644
index 0000000..176a6be
--- /dev/null
+++ b/drivers/regulator/tps6524x-regulator.c
@@ -0,0 +1,693 @@
+/*
+ * Regulator driver for TPS6524x PMIC
+ *
+ * Copyright (C) 2010 Texas Instruments
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation version 2.
+ *
+ * This program is distributed "as is" WITHOUT ANY WARRANTY of any kind,
+ * whether express or implied; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/err.h>
+#include <linux/errno.h>
+#include <linux/slab.h>
+#include <linux/spi/spi.h>
+#include <linux/regulator/driver.h>
+#include <linux/regulator/machine.h>
+
+#define REG_LDO_SET 0x0
+#define LDO_ILIM_MASK 1 /* 0 = 400-800, 1 = 900-1500 */
+#define LDO_VSEL_MASK 0x0f
+#define LDO2_ILIM_SHIFT 12
+#define LDO2_VSEL_SHIFT 4
+#define LDO1_ILIM_SHIFT 8
+#define LDO1_VSEL_SHIFT 0
+
+#define REG_BLOCK_EN 0x1
+#define BLOCK_MASK 1
+#define BLOCK_LDO1_SHIFT 0
+#define BLOCK_LDO2_SHIFT 1
+#define BLOCK_LCD_SHIFT 2
+#define BLOCK_USB_SHIFT 3
+
+#define REG_DCDC_SET 0x2
+#define DCDC_VDCDC_MASK 0x1f
+#define DCDC_VDCDC1_SHIFT 0
+#define DCDC_VDCDC2_SHIFT 5
+#define DCDC_VDCDC3_SHIFT 10
+
+#define REG_DCDC_EN 0x3
+#define DCDCDCDC_EN_MASK 0x1
+#define DCDCDCDC1_EN_SHIFT 0
+#define DCDCDCDC1_PG_MSK BIT(1)
+#define DCDCDCDC2_EN_SHIFT 2
+#define DCDCDCDC2_PG_MSK BIT(3)
+#define DCDCDCDC3_EN_SHIFT 4
+#define DCDCDCDC3_PG_MSK BIT(5)
+
+#define REG_USB 0x4
+#define USB_ILIM_SHIFT 0
+#define USB_ILIM_MASK 0x3
+#define USB_TSD_SHIFT 2
+#define USB_TSD_MASK 0x3
+#define USB_TWARN_SHIFT 4
+#define USB_TWARN_MASK 0x3
+#define USB_IWARN_SD BIT(6)
+#define USB_FAST_LOOP BIT(7)
+
+#define REG_ALARM 0x5
+#define ALARM_LDO1 BIT(0)
+#define ALARM_DCDC1 BIT(1)
+#define ALARM_DCDC2 BIT(2)
+#define ALARM_DCDC3 BIT(3)
+#define ALARM_LDO2 BIT(4)
+#define ALARM_USB_WARN BIT(5)
+#define ALARM_USB_ALARM BIT(6)
+#define ALARM_LCD BIT(9)
+#define ALARM_TEMP_WARM BIT(10)
+#define ALARM_TEMP_HOT BIT(11)
+#define ALARM_NRST BIT(14)
+#define ALARM_POWERUP BIT(15)
+
+#define REG_INT_ENABLE 0x6
+#define INT_LDO1 BIT(0)
+#define INT_DCDC1 BIT(1)
+#define INT_DCDC2 BIT(2)
+#define INT_DCDC3 BIT(3)
+#define INT_LDO2 BIT(4)
+#define INT_USB_WARN BIT(5)
+#define INT_USB_ALARM BIT(6)
+#define INT_LCD BIT(9)
+#define INT_TEMP_WARM BIT(10)
+#define INT_TEMP_HOT BIT(11)
+#define INT_GLOBAL_EN BIT(15)
+
+#define REG_INT_STATUS 0x7
+#define STATUS_LDO1 BIT(0)
+#define STATUS_DCDC1 BIT(1)
+#define STATUS_DCDC2 BIT(2)
+#define STATUS_DCDC3 BIT(3)
+#define STATUS_LDO2 BIT(4)
+#define STATUS_USB_WARN BIT(5)
+#define STATUS_USB_ALARM BIT(6)
+#define STATUS_LCD BIT(9)
+#define STATUS_TEMP_WARM BIT(10)
+#define STATUS_TEMP_HOT BIT(11)
+
+#define REG_SOFTWARE_RESET 0xb
+#define REG_WRITE_ENABLE 0xd
+#define REG_REV_ID 0xf
+
+#define N_DCDC 3
+#define N_LDO 2
+#define N_SWITCH 2
+#define N_REGULATORS (3 /* DCDC */ + \
+ 2 /* LDO */ + \
+ 2 /* switch */)
+
+#define FIXED_ILIMSEL BIT(0)
+#define FIXED_VOLTAGE BIT(1)
+
+#define CMD_READ(reg) ((reg) << 6)
+#define CMD_WRITE(reg) (BIT(5) | (reg) << 6)
+#define STAT_CLK BIT(3)
+#define STAT_WRITE BIT(2)
+#define STAT_INVALID BIT(1)
+#define STAT_WP BIT(0)
+
+struct field {
+ int reg;
+ int shift;
+ int mask;
+};
+
+struct supply_info {
+ const char *name;
+ int n_voltages;
+ const int *voltages;
+ int fixed_voltage;
+ int n_ilimsels;
+ const int *ilimsels;
+ int fixed_ilimsel;
+ int flags;
+ struct field enable, voltage, ilimsel;
+};
+
+struct tps6524x {
+ struct device *dev;
+ struct spi_device *spi;
+ struct mutex lock;
+ struct regulator_desc desc[N_REGULATORS];
+ struct regulator_dev *rdev[N_REGULATORS];
+};
+
+static int __read_reg(struct tps6524x *hw, int reg)
+{
+ int error = 0;
+ u16 cmd = CMD_READ(reg), in;
+ u8 status;
+ struct spi_message m;
+ struct spi_transfer t[3];
+
+ spi_message_init(&m);
+ memset(t, 0, sizeof(t));
+
+ t[0].tx_buf = &cmd;
+ t[0].len = 2;
+ t[0].bits_per_word = 12;
+ spi_message_add_tail(&t[0], &m);
+
+ t[1].rx_buf = ∈
+ t[1].len = 2;
+ t[1].bits_per_word = 16;
+ spi_message_add_tail(&t[1], &m);
+
+ t[2].rx_buf = &status;
+ t[2].len = 1;
+ t[2].bits_per_word = 4;
+ spi_message_add_tail(&t[2], &m);
+
+ error = spi_sync(hw->spi, &m);
+ if (error < 0)
+ return error;
+
+ dev_dbg(hw->dev, "read reg %d, data %x, status %x\n",
+ reg, in, status);
+
+ if (!(status & STAT_CLK) || (status & STAT_WRITE))
+ return -EIO;
+
+ if (status & STAT_INVALID)
+ return -EINVAL;
+
+ return in;
+}
+
+static int read_reg(struct tps6524x *hw, int reg)
+{
+ int ret;
+
+ mutex_lock(&hw->lock);
+ ret = __read_reg(hw, reg);
+ mutex_unlock(&hw->lock);
+
+ return ret;
+}
+
+static int __write_reg(struct tps6524x *hw, int reg, int val)
+{
+ int error = 0;
+ u16 cmd = CMD_WRITE(reg), out = val;
+ u8 status;
+ struct spi_message m;
+ struct spi_transfer t[3];
+
+ spi_message_init(&m);
+ memset(t, 0, sizeof(t));
+
+ t[0].tx_buf = &cmd;
+ t[0].len = 2;
+ t[0].bits_per_word = 12;
+ spi_message_add_tail(&t[0], &m);
+
+ t[1].tx_buf = &out;
+ t[1].len = 2;
+ t[1].bits_per_word = 16;
+ spi_message_add_tail(&t[1], &m);
+
+ t[2].rx_buf = &status;
+ t[2].len = 1;
+ t[2].bits_per_word = 4;
+ spi_message_add_tail(&t[2], &m);
+
+ error = spi_sync(hw->spi, &m);
+ if (error < 0)
+ return error;
+
+ dev_dbg(hw->dev, "wrote reg %d, data %x, status %x\n",
+ reg, out, status);
+
+ if (!(status & STAT_CLK) || !(status & STAT_WRITE))
+ return -EIO;
+
+ if (status & (STAT_INVALID | STAT_WP))
+ return -EINVAL;
+
+ return error;
+}
+
+static int __rmw_reg(struct tps6524x *hw, int reg, int mask, int val)
+{
+ int ret;
+
+ ret = __read_reg(hw, reg);
+ if (ret < 0)
+ return ret;
+
+ ret &= ~mask;
+ ret |= val;
+
+ ret = __write_reg(hw, reg, ret);
+
+ return (ret < 0) ? ret : 0;
+}
+
+static int rmw_protect(struct tps6524x *hw, int reg, int mask, int val)
+{
+ int ret;
+
+ mutex_lock(&hw->lock);
+
+ ret = __write_reg(hw, REG_WRITE_ENABLE, 1);
+ if (ret) {
+ dev_err(hw->dev, "failed to set write enable\n");
+ goto error;
+ }
+
+ ret = __rmw_reg(hw, reg, mask, val);
+ if (ret)
+ dev_err(hw->dev, "failed to rmw register %d\n", reg);
+
+ ret = __write_reg(hw, REG_WRITE_ENABLE, 0);
+ if (ret) {
+ dev_err(hw->dev, "failed to clear write enable\n");
+ goto error;
+ }
+
+error:
+ mutex_unlock(&hw->lock);
+
+ return ret;
+}
+
+static int read_field(struct tps6524x *hw, const struct field *field)
+{
+ int tmp;
+
+ tmp = read_reg(hw, field->reg);
+ if (tmp < 0)
+ return tmp;
+
+ return (tmp >> field->shift) & field->mask;
+}
+
+static int write_field(struct tps6524x *hw, const struct field *field,
+ int val)
+{
+ if (val & ~field->mask)
+ return -EOVERFLOW;
+
+ return rmw_protect(hw, field->reg,
+ field->mask << field->shift,
+ val << field->shift);
+}
+
+static const int dcdc1_voltages[] = {
+ 800000, 825000, 850000, 875000,
+ 900000, 925000, 950000, 975000,
+ 1000000, 1025000, 1050000, 1075000,
+ 1100000, 1125000, 1150000, 1175000,
+ 1200000, 1225000, 1250000, 1275000,
+ 1300000, 1325000, 1350000, 1375000,
+ 1400000, 1425000, 1450000, 1475000,
+ 1500000, 1525000, 1550000, 1575000,
+};
+
+static const int dcdc2_voltages[] = {
+ 1400000, 1450000, 1500000, 1550000,
+ 1600000, 1650000, 1700000, 1750000,
+ 1800000, 1850000, 1900000, 1950000,
+ 2000000, 2050000, 2100000, 2150000,
+ 2200000, 2250000, 2300000, 2350000,
+ 2400000, 2450000, 2500000, 2550000,
+ 2600000, 2650000, 2700000, 2750000,
+ 2800000, 2850000, 2900000, 2950000,
+};
+
+static const int dcdc3_voltages[] = {
+ 2400000, 2450000, 2500000, 2550000, 2600000,
+ 2650000, 2700000, 2750000, 2800000, 2850000,
+ 2900000, 2950000, 3000000, 3050000, 3100000,
+ 3150000, 3200000, 3250000, 3300000, 3350000,
+ 3400000, 3450000, 3500000, 3550000, 3600000,
+};
+
+static const int ldo1_voltages[] = {
+ 4300000, 4350000, 4400000, 4450000,
+ 4500000, 4550000, 4600000, 4650000,
+ 4700000, 4750000, 4800000, 4850000,
+ 4900000, 4950000, 5000000, 5050000,
+};
+
+static const int ldo2_voltages[] = {
+ 1100000, 1150000, 1200000, 1250000,
+ 1300000, 1700000, 1750000, 1800000,
+ 1850000, 1900000, 3150000, 3200000,
+ 3250000, 3300000, 3350000, 3400000,
+};
+
+static const int ldo_ilimsel[] = {
+ 400000, 1500000
+};
+
+static const int usb_ilimsel[] = {
+ 200000, 400000, 800000, 1000000
+};
+
+#define __MK_FIELD(_reg, _mask, _shift) \
+ { .reg = (_reg), .mask = (_mask), .shift = (_shift), }
+
+static const struct supply_info supply_info[N_REGULATORS] = {
+ {
+ .name = "DCDC1",
+ .flags = FIXED_ILIMSEL,
+ .n_voltages = ARRAY_SIZE(dcdc1_voltages),
+ .voltages = dcdc1_voltages,
+ .fixed_ilimsel = 2400000,
+ .enable = __MK_FIELD(REG_DCDC_EN, DCDCDCDC_EN_MASK,
+ DCDCDCDC1_EN_SHIFT),
+ .voltage = __MK_FIELD(REG_DCDC_SET, DCDC_VDCDC_MASK,
+ DCDC_VDCDC1_SHIFT),
+ },
+ {
+ .name = "DCDC2",
+ .flags = FIXED_ILIMSEL,
+ .n_voltages = ARRAY_SIZE(dcdc2_voltages),
+ .voltages = dcdc2_voltages,
+ .fixed_ilimsel = 1200000,
+ .enable = __MK_FIELD(REG_DCDC_EN, DCDCDCDC_EN_MASK,
+ DCDCDCDC2_EN_SHIFT),
+ .voltage = __MK_FIELD(REG_DCDC_SET, DCDC_VDCDC_MASK,
+ DCDC_VDCDC2_SHIFT),
+ },
+ {
+ .name = "DCDC3",
+ .flags = FIXED_ILIMSEL,
+ .n_voltages = ARRAY_SIZE(dcdc3_voltages),
+ .voltages = dcdc3_voltages,
+ .fixed_ilimsel = 1200000,
+ .enable = __MK_FIELD(REG_DCDC_EN, DCDCDCDC_EN_MASK,
+ DCDCDCDC3_EN_SHIFT),
+ .voltage = __MK_FIELD(REG_DCDC_SET, DCDC_VDCDC_MASK,
+ DCDC_VDCDC3_SHIFT),
+ },
+ {
+ .name = "LDO1",
+ .n_voltages = ARRAY_SIZE(ldo1_voltages),
+ .voltages = ldo1_voltages,
+ .n_ilimsels = ARRAY_SIZE(ldo_ilimsel),
+ .ilimsels = ldo_ilimsel,
+ .enable = __MK_FIELD(REG_BLOCK_EN, BLOCK_MASK,
+ BLOCK_LDO1_SHIFT),
+ .voltage = __MK_FIELD(REG_LDO_SET, LDO_VSEL_MASK,
+ LDO1_VSEL_SHIFT),
+ .ilimsel = __MK_FIELD(REG_LDO_SET, LDO_ILIM_MASK,
+ LDO1_ILIM_SHIFT),
+ },
+ {
+ .name = "LDO2",
+ .n_voltages = ARRAY_SIZE(ldo2_voltages),
+ .voltages = ldo2_voltages,
+ .n_ilimsels = ARRAY_SIZE(ldo_ilimsel),
+ .ilimsels = ldo_ilimsel,
+ .enable = __MK_FIELD(REG_BLOCK_EN, BLOCK_MASK,
+ BLOCK_LDO2_SHIFT),
+ .voltage = __MK_FIELD(REG_LDO_SET, LDO_VSEL_MASK,
+ LDO2_VSEL_SHIFT),
+ .ilimsel = __MK_FIELD(REG_LDO_SET, LDO_ILIM_MASK,
+ LDO2_ILIM_SHIFT),
+ },
+ {
+ .name = "USB",
+ .flags = FIXED_VOLTAGE,
+ .fixed_voltage = 5000000,
+ .n_ilimsels = ARRAY_SIZE(usb_ilimsel),
+ .ilimsels = usb_ilimsel,
+ .enable = __MK_FIELD(REG_BLOCK_EN, BLOCK_MASK,
+ BLOCK_USB_SHIFT),
+ .ilimsel = __MK_FIELD(REG_USB, USB_ILIM_MASK,
+ USB_ILIM_SHIFT),
+ },
+ {
+ .name = "LCD",
+ .flags = FIXED_VOLTAGE | FIXED_ILIMSEL,
+ .fixed_voltage = 5000000,
+ .fixed_ilimsel = 400000,
+ .enable = __MK_FIELD(REG_BLOCK_EN, BLOCK_MASK,
+ BLOCK_LCD_SHIFT),
+ },
+};
+
+static int list_voltage(struct regulator_dev *rdev, unsigned selector)
+{
+ const struct supply_info *info;
+ struct tps6524x *hw;
+
+ hw = rdev_get_drvdata(rdev);
+ info = &supply_info[rdev_get_id(rdev)];
+
+ if (info->flags & FIXED_VOLTAGE)
+ return selector ? -EINVAL : info->fixed_voltage;
+
+ return ((selector < info->n_voltages) ?
+ info->voltages[selector] : -EINVAL);
+}
+
+static int set_voltage(struct regulator_dev *rdev, int min_uV, int max_uV,
+ unsigned *selector)
+{
+ const struct supply_info *info;
+ struct tps6524x *hw;
+ unsigned i;
+
+ hw = rdev_get_drvdata(rdev);
+ info = &supply_info[rdev_get_id(rdev)];
+
+ if (info->flags & FIXED_VOLTAGE)
+ return -EINVAL;
+
+ for (i = 0; i < info->n_voltages; i++)
+ if (min_uV <= info->voltages[i] &&
+ max_uV >= info->voltages[i])
+ break;
+
+ if (i >= info->n_voltages)
+ i = info->n_voltages - 1;
+
+ *selector = info->voltages[i];
+
+ return write_field(hw, &info->voltage, i);
+}
+
+static int get_voltage(struct regulator_dev *rdev)
+{
+ const struct supply_info *info;
+ struct tps6524x *hw;
+ int ret;
+
+ hw = rdev_get_drvdata(rdev);
+ info = &supply_info[rdev_get_id(rdev)];
+
+ if (info->flags & FIXED_VOLTAGE)
+ return info->fixed_voltage;
+
+ ret = read_field(hw, &info->voltage);
+ if (ret < 0)
+ return ret;
+ if (WARN_ON(ret >= info->n_voltages))
+ return -EIO;
+
+ return info->voltages[ret];
+}
+
+static int set_current_limit(struct regulator_dev *rdev, int min_uA,
+ int max_uA)
+{
+ const struct supply_info *info;
+ struct tps6524x *hw;
+ int i;
+
+ hw = rdev_get_drvdata(rdev);
+ info = &supply_info[rdev_get_id(rdev)];
+
+ if (info->flags & FIXED_ILIMSEL)
+ return -EINVAL;
+
+ for (i = 0; i < info->n_ilimsels; i++)
+ if (min_uA <= info->ilimsels[i] &&
+ max_uA >= info->ilimsels[i])
+ break;
+
+ if (i >= info->n_ilimsels)
+ return -EINVAL;
+
+ return write_field(hw, &info->ilimsel, i);
+}
+
+static int get_current_limit(struct regulator_dev *rdev)
+{
+ const struct supply_info *info;
+ struct tps6524x *hw;
+ int ret;
+
+ hw = rdev_get_drvdata(rdev);
+ info = &supply_info[rdev_get_id(rdev)];
+
+ if (info->flags & FIXED_ILIMSEL)
+ return info->fixed_ilimsel;
+
+ ret = read_field(hw, &info->ilimsel);
+ if (ret < 0)
+ return ret;
+ if (WARN_ON(ret >= info->n_ilimsels))
+ return -EIO;
+
+ return info->ilimsels[ret];
+}
+
+static int enable_supply(struct regulator_dev *rdev)
+{
+ const struct supply_info *info;
+ struct tps6524x *hw;
+
+ hw = rdev_get_drvdata(rdev);
+ info = &supply_info[rdev_get_id(rdev)];
+
+ return write_field(hw, &info->enable, 1);
+}
+
+static int disable_supply(struct regulator_dev *rdev)
+{
+ const struct supply_info *info;
+ struct tps6524x *hw;
+
+ hw = rdev_get_drvdata(rdev);
+ info = &supply_info[rdev_get_id(rdev)];
+
+ return write_field(hw, &info->enable, 0);
+}
+
+static int is_supply_enabled(struct regulator_dev *rdev)
+{
+ const struct supply_info *info;
+ struct tps6524x *hw;
+
+ hw = rdev_get_drvdata(rdev);
+ info = &supply_info[rdev_get_id(rdev)];
+
+ return read_field(hw, &info->enable);
+}
+
+static struct regulator_ops regulator_ops = {
+ .is_enabled = is_supply_enabled,
+ .enable = enable_supply,
+ .disable = disable_supply,
+ .get_voltage = get_voltage,
+ .set_voltage = set_voltage,
+ .list_voltage = list_voltage,
+ .set_current_limit = set_current_limit,
+ .get_current_limit = get_current_limit,
+};
+
+static int __devexit pmic_remove(struct spi_device *spi)
+{
+ struct tps6524x *hw = spi_get_drvdata(spi);
+ int i;
+
+ if (!hw)
+ return 0;
+ for (i = 0; i < N_REGULATORS; i++) {
+ if (hw->rdev[i])
+ regulator_unregister(hw->rdev[i]);
+ hw->rdev[i] = NULL;
+ }
+ spi_set_drvdata(spi, NULL);
+ kfree(hw);
+ return 0;
+}
+
+static int __devinit pmic_probe(struct spi_device *spi)
+{
+ struct tps6524x *hw;
+ struct device *dev = &spi->dev;
+ const struct supply_info *info = supply_info;
+ struct regulator_init_data *init_data;
+ int ret = 0, i;
+
+ init_data = dev->platform_data;
+ if (!init_data) {
+ dev_err(dev, "could not find regulator platform data\n");
+ return -EINVAL;
+ }
+
+ hw = kzalloc(sizeof(struct tps6524x), GFP_KERNEL);
+ if (!hw) {
+ dev_err(dev, "cannot allocate regulator private data\n");
+ return -ENOMEM;
+ }
+ spi_set_drvdata(spi, hw);
+
+ memset(hw, 0, sizeof(struct tps6524x));
+ hw->dev = dev;
+ hw->spi = spi_dev_get(spi);
+ mutex_init(&hw->lock);
+
+ for (i = 0; i < N_REGULATORS; i++, info++, init_data++) {
+ hw->desc[i].name = info->name;
+ hw->desc[i].id = i;
+ hw->desc[i].n_voltages = info->n_voltages;
+ hw->desc[i].ops = ®ulator_ops;
+ hw->desc[i].type = REGULATOR_VOLTAGE;
+ hw->desc[i].owner = THIS_MODULE;
+
+ if (info->flags & FIXED_VOLTAGE)
+ hw->desc[i].n_voltages = 1;
+
+ hw->rdev[i] = regulator_register(&hw->desc[i], dev,
+ init_data, hw);
+ if (IS_ERR(hw->rdev[i])) {
+ ret = PTR_ERR(hw->rdev[i]);
+ hw->rdev[i] = NULL;
+ goto fail;
+ }
+ }
+
+ return 0;
+
+fail:
+ pmic_remove(spi);
+ return ret;
+}
+
+static struct spi_driver pmic_driver = {
+ .probe = pmic_probe,
+ .remove = __devexit_p(pmic_remove),
+ .driver = {
+ .name = "tps6524x",
+ .owner = THIS_MODULE,
+ },
+};
+
+static int __init pmic_driver_init(void)
+{
+ return spi_register_driver(&pmic_driver);
+}
+module_init(pmic_driver_init);
+
+static void __exit pmic_driver_exit(void)
+{
+ spi_unregister_driver(&pmic_driver);
+}
+module_exit(pmic_driver_exit);
+
+MODULE_DESCRIPTION("TPS6524X PMIC Driver");
+MODULE_AUTHOR("Cyril Chemparathy");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("spi:tps6524x");
diff --git a/drivers/regulator/tps6586x-regulator.c b/drivers/regulator/tps6586x-regulator.c
index 6d20b04..bb04a75 100644
--- a/drivers/regulator/tps6586x-regulator.c
+++ b/drivers/regulator/tps6586x-regulator.c
@@ -85,7 +85,8 @@
static int __tps6586x_ldo_set_voltage(struct device *parent,
struct tps6586x_regulator *ri,
- int min_uV, int max_uV)
+ int min_uV, int max_uV,
+ unsigned *selector)
{
int val, uV;
uint8_t mask;
@@ -100,6 +101,8 @@
/* use the first in-range value */
if (min_uV <= uV && uV <= max_uV) {
+ *selector = val;
+
val <<= ri->volt_shift;
mask = ((1 << ri->volt_nbits) - 1) << ri->volt_shift;
@@ -111,12 +114,13 @@
}
static int tps6586x_ldo_set_voltage(struct regulator_dev *rdev,
- int min_uV, int max_uV)
+ int min_uV, int max_uV, unsigned *selector)
{
struct tps6586x_regulator *ri = rdev_get_drvdata(rdev);
struct device *parent = to_tps6586x_dev(rdev);
- return __tps6586x_ldo_set_voltage(parent, ri, min_uV, max_uV);
+ return __tps6586x_ldo_set_voltage(parent, ri, min_uV, max_uV,
+ selector);
}
static int tps6586x_ldo_get_voltage(struct regulator_dev *rdev)
@@ -140,13 +144,14 @@
}
static int tps6586x_dvm_set_voltage(struct regulator_dev *rdev,
- int min_uV, int max_uV)
+ int min_uV, int max_uV, unsigned *selector)
{
struct tps6586x_regulator *ri = rdev_get_drvdata(rdev);
struct device *parent = to_tps6586x_dev(rdev);
int ret;
- ret = __tps6586x_ldo_set_voltage(parent, ri, min_uV, max_uV);
+ ret = __tps6586x_ldo_set_voltage(parent, ri, min_uV, max_uV,
+ selector);
if (ret)
return ret;
diff --git a/drivers/regulator/twl-regulator.c b/drivers/regulator/twl-regulator.c
index a57262a..bd332cf 100644
--- a/drivers/regulator/twl-regulator.c
+++ b/drivers/regulator/twl-regulator.c
@@ -329,7 +329,8 @@
}
static int
-twl4030ldo_set_voltage(struct regulator_dev *rdev, int min_uV, int max_uV)
+twl4030ldo_set_voltage(struct regulator_dev *rdev, int min_uV, int max_uV,
+ unsigned *selector)
{
struct twlreg_info *info = rdev_get_drvdata(rdev);
int vsel;
@@ -345,9 +346,11 @@
/* REVISIT for VAUX2, first match may not be best/lowest */
/* use the first in-range value */
- if (min_uV <= uV && uV <= max_uV)
+ if (min_uV <= uV && uV <= max_uV) {
+ *selector = vsel;
return twlreg_write(info, TWL_MODULE_PM_RECEIVER,
VREG_VOLTAGE, vsel);
+ }
}
return -EDOM;
@@ -389,7 +392,8 @@
}
static int
-twl6030ldo_set_voltage(struct regulator_dev *rdev, int min_uV, int max_uV)
+twl6030ldo_set_voltage(struct regulator_dev *rdev, int min_uV, int max_uV,
+ unsigned *selector)
{
struct twlreg_info *info = rdev_get_drvdata(rdev);
int vsel;
@@ -402,6 +406,7 @@
* mV = 1000mv + 100mv * (vsel - 1)
*/
vsel = (min_uV/1000 - 1000)/100 + 1;
+ *selector = vsel;
return twlreg_write(info, TWL_MODULE_PM_RECEIVER, VREG_VOLTAGE, vsel);
}
diff --git a/drivers/regulator/wm831x-dcdc.c b/drivers/regulator/wm831x-dcdc.c
index dbfaf59..8b0d2c4 100644
--- a/drivers/regulator/wm831x-dcdc.c
+++ b/drivers/regulator/wm831x-dcdc.c
@@ -302,7 +302,7 @@
}
static int wm831x_buckv_set_voltage(struct regulator_dev *rdev,
- int min_uV, int max_uV)
+ int min_uV, int max_uV, unsigned *selector)
{
struct wm831x_dcdc *dcdc = rdev_get_drvdata(rdev);
struct wm831x *wm831x = dcdc->wm831x;
@@ -314,6 +314,8 @@
if (vsel < 0)
return vsel;
+ *selector = vsel;
+
/* If this value is already set then do a GPIO update if we can */
if (dcdc->dvs_gpio && dcdc->on_vsel == vsel)
return wm831x_buckv_set_dvs(rdev, 0);
@@ -375,14 +377,14 @@
return wm831x_set_bits(wm831x, reg, WM831X_DC1_SLP_VSEL_MASK, vsel);
}
-static int wm831x_buckv_get_voltage(struct regulator_dev *rdev)
+static int wm831x_buckv_get_voltage_sel(struct regulator_dev *rdev)
{
struct wm831x_dcdc *dcdc = rdev_get_drvdata(rdev);
if (dcdc->dvs_gpio && dcdc->dvs_gpio_state)
- return wm831x_buckv_list_voltage(rdev, dcdc->dvs_vsel);
+ return dcdc->dvs_vsel;
else
- return wm831x_buckv_list_voltage(rdev, dcdc->on_vsel);
+ return dcdc->on_vsel;
}
/* Current limit options */
@@ -424,7 +426,7 @@
static struct regulator_ops wm831x_buckv_ops = {
.set_voltage = wm831x_buckv_set_voltage,
- .get_voltage = wm831x_buckv_get_voltage,
+ .get_voltage_sel = wm831x_buckv_get_voltage_sel,
.list_voltage = wm831x_buckv_list_voltage,
.set_suspend_voltage = wm831x_buckv_set_suspend_voltage,
.set_current_limit = wm831x_buckv_set_current_limit,
@@ -636,7 +638,7 @@
}
static int wm831x_buckp_set_voltage_int(struct regulator_dev *rdev, int reg,
- int min_uV, int max_uV)
+ int min_uV, int max_uV, int *selector)
{
struct wm831x_dcdc *dcdc = rdev_get_drvdata(rdev);
struct wm831x *wm831x = dcdc->wm831x;
@@ -650,16 +652,20 @@
if (wm831x_buckp_list_voltage(rdev, vsel) > max_uV)
return -EINVAL;
+ *selector = vsel;
+
return wm831x_set_bits(wm831x, reg, WM831X_DC3_ON_VSEL_MASK, vsel);
}
static int wm831x_buckp_set_voltage(struct regulator_dev *rdev,
- int min_uV, int max_uV)
+ int min_uV, int max_uV,
+ unsigned *selector)
{
struct wm831x_dcdc *dcdc = rdev_get_drvdata(rdev);
u16 reg = dcdc->base + WM831X_DCDC_ON_CONFIG;
- return wm831x_buckp_set_voltage_int(rdev, reg, min_uV, max_uV);
+ return wm831x_buckp_set_voltage_int(rdev, reg, min_uV, max_uV,
+ selector);
}
static int wm831x_buckp_set_suspend_voltage(struct regulator_dev *rdev,
@@ -667,11 +673,12 @@
{
struct wm831x_dcdc *dcdc = rdev_get_drvdata(rdev);
u16 reg = dcdc->base + WM831X_DCDC_SLEEP_CONTROL;
+ unsigned selector;
- return wm831x_buckp_set_voltage_int(rdev, reg, uV, uV);
+ return wm831x_buckp_set_voltage_int(rdev, reg, uV, uV, &selector);
}
-static int wm831x_buckp_get_voltage(struct regulator_dev *rdev)
+static int wm831x_buckp_get_voltage_sel(struct regulator_dev *rdev)
{
struct wm831x_dcdc *dcdc = rdev_get_drvdata(rdev);
struct wm831x *wm831x = dcdc->wm831x;
@@ -682,12 +689,12 @@
if (val < 0)
return val;
- return wm831x_buckp_list_voltage(rdev, val & WM831X_DC3_ON_VSEL_MASK);
+ return val & WM831X_DC3_ON_VSEL_MASK;
}
static struct regulator_ops wm831x_buckp_ops = {
.set_voltage = wm831x_buckp_set_voltage,
- .get_voltage = wm831x_buckp_get_voltage,
+ .get_voltage_sel = wm831x_buckp_get_voltage_sel,
.list_voltage = wm831x_buckp_list_voltage,
.set_suspend_voltage = wm831x_buckp_set_suspend_voltage,
diff --git a/drivers/regulator/wm831x-ldo.c b/drivers/regulator/wm831x-ldo.c
index 9edf8f6..c94fc5b 100644
--- a/drivers/regulator/wm831x-ldo.c
+++ b/drivers/regulator/wm831x-ldo.c
@@ -113,7 +113,8 @@
}
static int wm831x_gp_ldo_set_voltage_int(struct regulator_dev *rdev, int reg,
- int min_uV, int max_uV)
+ int min_uV, int max_uV,
+ unsigned *selector)
{
struct wm831x_ldo *ldo = rdev_get_drvdata(rdev);
struct wm831x *wm831x = ldo->wm831x;
@@ -133,16 +134,20 @@
if (ret < min_uV || ret > max_uV)
return -EINVAL;
+ *selector = vsel;
+
return wm831x_set_bits(wm831x, reg, WM831X_LDO1_ON_VSEL_MASK, vsel);
}
static int wm831x_gp_ldo_set_voltage(struct regulator_dev *rdev,
- int min_uV, int max_uV)
+ int min_uV, int max_uV,
+ unsigned *selector)
{
struct wm831x_ldo *ldo = rdev_get_drvdata(rdev);
int reg = ldo->base + WM831X_LDO_ON_CONTROL;
- return wm831x_gp_ldo_set_voltage_int(rdev, reg, min_uV, max_uV);
+ return wm831x_gp_ldo_set_voltage_int(rdev, reg, min_uV, max_uV,
+ selector);
}
static int wm831x_gp_ldo_set_suspend_voltage(struct regulator_dev *rdev,
@@ -150,11 +155,12 @@
{
struct wm831x_ldo *ldo = rdev_get_drvdata(rdev);
int reg = ldo->base + WM831X_LDO_SLEEP_CONTROL;
+ unsigned int selector;
- return wm831x_gp_ldo_set_voltage_int(rdev, reg, uV, uV);
+ return wm831x_gp_ldo_set_voltage_int(rdev, reg, uV, uV, &selector);
}
-static int wm831x_gp_ldo_get_voltage(struct regulator_dev *rdev)
+static int wm831x_gp_ldo_get_voltage_sel(struct regulator_dev *rdev)
{
struct wm831x_ldo *ldo = rdev_get_drvdata(rdev);
struct wm831x *wm831x = ldo->wm831x;
@@ -167,7 +173,7 @@
ret &= WM831X_LDO1_ON_VSEL_MASK;
- return wm831x_gp_ldo_list_voltage(rdev, ret);
+ return ret;
}
static unsigned int wm831x_gp_ldo_get_mode(struct regulator_dev *rdev)
@@ -287,7 +293,7 @@
static struct regulator_ops wm831x_gp_ldo_ops = {
.list_voltage = wm831x_gp_ldo_list_voltage,
- .get_voltage = wm831x_gp_ldo_get_voltage,
+ .get_voltage_sel = wm831x_gp_ldo_get_voltage_sel,
.set_voltage = wm831x_gp_ldo_set_voltage,
.set_suspend_voltage = wm831x_gp_ldo_set_suspend_voltage,
.get_mode = wm831x_gp_ldo_get_mode,
@@ -413,7 +419,8 @@
}
static int wm831x_aldo_set_voltage_int(struct regulator_dev *rdev, int reg,
- int min_uV, int max_uV)
+ int min_uV, int max_uV,
+ unsigned *selector)
{
struct wm831x_ldo *ldo = rdev_get_drvdata(rdev);
struct wm831x *wm831x = ldo->wm831x;
@@ -433,16 +440,19 @@
if (ret < min_uV || ret > max_uV)
return -EINVAL;
+ *selector = vsel;
+
return wm831x_set_bits(wm831x, reg, WM831X_LDO7_ON_VSEL_MASK, vsel);
}
static int wm831x_aldo_set_voltage(struct regulator_dev *rdev,
- int min_uV, int max_uV)
+ int min_uV, int max_uV, unsigned *selector)
{
struct wm831x_ldo *ldo = rdev_get_drvdata(rdev);
int reg = ldo->base + WM831X_LDO_ON_CONTROL;
- return wm831x_aldo_set_voltage_int(rdev, reg, min_uV, max_uV);
+ return wm831x_aldo_set_voltage_int(rdev, reg, min_uV, max_uV,
+ selector);
}
static int wm831x_aldo_set_suspend_voltage(struct regulator_dev *rdev,
@@ -450,11 +460,12 @@
{
struct wm831x_ldo *ldo = rdev_get_drvdata(rdev);
int reg = ldo->base + WM831X_LDO_SLEEP_CONTROL;
+ unsigned int selector;
- return wm831x_aldo_set_voltage_int(rdev, reg, uV, uV);
+ return wm831x_aldo_set_voltage_int(rdev, reg, uV, uV, &selector);
}
-static int wm831x_aldo_get_voltage(struct regulator_dev *rdev)
+static int wm831x_aldo_get_voltage_sel(struct regulator_dev *rdev)
{
struct wm831x_ldo *ldo = rdev_get_drvdata(rdev);
struct wm831x *wm831x = ldo->wm831x;
@@ -467,7 +478,7 @@
ret &= WM831X_LDO7_ON_VSEL_MASK;
- return wm831x_aldo_list_voltage(rdev, ret);
+ return ret;
}
static unsigned int wm831x_aldo_get_mode(struct regulator_dev *rdev)
@@ -548,7 +559,7 @@
static struct regulator_ops wm831x_aldo_ops = {
.list_voltage = wm831x_aldo_list_voltage,
- .get_voltage = wm831x_aldo_get_voltage,
+ .get_voltage_sel = wm831x_aldo_get_voltage_sel,
.set_voltage = wm831x_aldo_set_voltage,
.set_suspend_voltage = wm831x_aldo_set_suspend_voltage,
.get_mode = wm831x_aldo_get_mode,
@@ -666,7 +677,8 @@
static int wm831x_alive_ldo_set_voltage_int(struct regulator_dev *rdev,
int reg,
- int min_uV, int max_uV)
+ int min_uV, int max_uV,
+ unsigned *selector)
{
struct wm831x_ldo *ldo = rdev_get_drvdata(rdev);
struct wm831x *wm831x = ldo->wm831x;
@@ -680,16 +692,20 @@
if (ret < min_uV || ret > max_uV)
return -EINVAL;
+ *selector = vsel;
+
return wm831x_set_bits(wm831x, reg, WM831X_LDO11_ON_VSEL_MASK, vsel);
}
static int wm831x_alive_ldo_set_voltage(struct regulator_dev *rdev,
- int min_uV, int max_uV)
+ int min_uV, int max_uV,
+ unsigned *selector)
{
struct wm831x_ldo *ldo = rdev_get_drvdata(rdev);
int reg = ldo->base + WM831X_ALIVE_LDO_ON_CONTROL;
- return wm831x_alive_ldo_set_voltage_int(rdev, reg, min_uV, max_uV);
+ return wm831x_alive_ldo_set_voltage_int(rdev, reg, min_uV, max_uV,
+ selector);
}
static int wm831x_alive_ldo_set_suspend_voltage(struct regulator_dev *rdev,
@@ -697,11 +713,12 @@
{
struct wm831x_ldo *ldo = rdev_get_drvdata(rdev);
int reg = ldo->base + WM831X_ALIVE_LDO_SLEEP_CONTROL;
+ unsigned selector;
- return wm831x_alive_ldo_set_voltage_int(rdev, reg, uV, uV);
+ return wm831x_alive_ldo_set_voltage_int(rdev, reg, uV, uV, &selector);
}
-static int wm831x_alive_ldo_get_voltage(struct regulator_dev *rdev)
+static int wm831x_alive_ldo_get_voltage_sel(struct regulator_dev *rdev)
{
struct wm831x_ldo *ldo = rdev_get_drvdata(rdev);
struct wm831x *wm831x = ldo->wm831x;
@@ -714,7 +731,7 @@
ret &= WM831X_LDO11_ON_VSEL_MASK;
- return wm831x_alive_ldo_list_voltage(rdev, ret);
+ return ret;
}
static int wm831x_alive_ldo_get_status(struct regulator_dev *rdev)
@@ -736,7 +753,7 @@
static struct regulator_ops wm831x_alive_ldo_ops = {
.list_voltage = wm831x_alive_ldo_list_voltage,
- .get_voltage = wm831x_alive_ldo_get_voltage,
+ .get_voltage_sel = wm831x_alive_ldo_get_voltage_sel,
.set_voltage = wm831x_alive_ldo_set_voltage,
.set_suspend_voltage = wm831x_alive_ldo_set_suspend_voltage,
.get_status = wm831x_alive_ldo_get_status,
diff --git a/drivers/regulator/wm8350-regulator.c b/drivers/regulator/wm8350-regulator.c
index fe4b8a8..1bcb22c 100644
--- a/drivers/regulator/wm8350-regulator.c
+++ b/drivers/regulator/wm8350-regulator.c
@@ -360,7 +360,7 @@
EXPORT_SYMBOL_GPL(wm8350_isink_set_flash);
static int wm8350_dcdc_set_voltage(struct regulator_dev *rdev, int min_uV,
- int max_uV)
+ int max_uV, unsigned *selector)
{
struct wm8350 *wm8350 = rdev_get_drvdata(rdev);
int volt_reg, dcdc = rdev_get_id(rdev), mV,
@@ -397,17 +397,18 @@
return -EINVAL;
}
+ *selector = mV;
+
/* all DCDCs have same mV bits */
val = wm8350_reg_read(wm8350, volt_reg) & ~WM8350_DC1_VSEL_MASK;
wm8350_reg_write(wm8350, volt_reg, val | mV);
return 0;
}
-static int wm8350_dcdc_get_voltage(struct regulator_dev *rdev)
+static int wm8350_dcdc_get_voltage_sel(struct regulator_dev *rdev)
{
struct wm8350 *wm8350 = rdev_get_drvdata(rdev);
int volt_reg, dcdc = rdev_get_id(rdev);
- u16 val;
switch (dcdc) {
case WM8350_DCDC_1:
@@ -429,8 +430,7 @@
}
/* all DCDCs have same mV bits */
- val = wm8350_reg_read(wm8350, volt_reg) & WM8350_DC1_VSEL_MASK;
- return wm8350_dcdc_val_to_mvolts(val) * 1000;
+ return wm8350_reg_read(wm8350, volt_reg) & WM8350_DC1_VSEL_MASK;
}
static int wm8350_dcdc_list_voltage(struct regulator_dev *rdev,
@@ -754,7 +754,7 @@
}
static int wm8350_ldo_set_voltage(struct regulator_dev *rdev, int min_uV,
- int max_uV)
+ int max_uV, unsigned *selector)
{
struct wm8350 *wm8350 = rdev_get_drvdata(rdev);
int volt_reg, ldo = rdev_get_id(rdev), mV, min_mV = min_uV / 1000,
@@ -797,17 +797,18 @@
return -EINVAL;
}
+ *selector = mV;
+
/* all LDOs have same mV bits */
val = wm8350_reg_read(wm8350, volt_reg) & ~WM8350_LDO1_VSEL_MASK;
wm8350_reg_write(wm8350, volt_reg, val | mV);
return 0;
}
-static int wm8350_ldo_get_voltage(struct regulator_dev *rdev)
+static int wm8350_ldo_get_voltage_sel(struct regulator_dev *rdev)
{
struct wm8350 *wm8350 = rdev_get_drvdata(rdev);
int volt_reg, ldo = rdev_get_id(rdev);
- u16 val;
switch (ldo) {
case WM8350_LDO_1:
@@ -827,8 +828,7 @@
}
/* all LDOs have same mV bits */
- val = wm8350_reg_read(wm8350, volt_reg) & WM8350_LDO1_VSEL_MASK;
- return wm8350_ldo_val_to_mvolts(val) * 1000;
+ return wm8350_reg_read(wm8350, volt_reg) & WM8350_LDO1_VSEL_MASK;
}
static int wm8350_ldo_list_voltage(struct regulator_dev *rdev,
@@ -1225,7 +1225,7 @@
static struct regulator_ops wm8350_dcdc_ops = {
.set_voltage = wm8350_dcdc_set_voltage,
- .get_voltage = wm8350_dcdc_get_voltage,
+ .get_voltage_sel = wm8350_dcdc_get_voltage_sel,
.list_voltage = wm8350_dcdc_list_voltage,
.enable = wm8350_dcdc_enable,
.disable = wm8350_dcdc_disable,
@@ -1249,7 +1249,7 @@
static struct regulator_ops wm8350_ldo_ops = {
.set_voltage = wm8350_ldo_set_voltage,
- .get_voltage = wm8350_ldo_get_voltage,
+ .get_voltage_sel = wm8350_ldo_get_voltage_sel,
.list_voltage = wm8350_ldo_list_voltage,
.enable = wm8350_ldo_enable,
.disable = wm8350_ldo_disable,
diff --git a/drivers/regulator/wm8400-regulator.c b/drivers/regulator/wm8400-regulator.c
index 924c7eb..b42d01c 100644
--- a/drivers/regulator/wm8400-regulator.c
+++ b/drivers/regulator/wm8400-regulator.c
@@ -67,7 +67,7 @@
}
static int wm8400_ldo_set_voltage(struct regulator_dev *dev,
- int min_uV, int max_uV)
+ int min_uV, int max_uV, unsigned *selector)
{
struct wm8400 *wm8400 = rdev_get_drvdata(dev);
u16 val;
@@ -93,6 +93,8 @@
val += 0xf;
}
+ *selector = val;
+
return wm8400_set_bits(wm8400, WM8400_LDO1_CONTROL + rdev_get_id(dev),
WM8400_LDO1_VSEL_MASK, val);
}
@@ -156,7 +158,7 @@
}
static int wm8400_dcdc_set_voltage(struct regulator_dev *dev,
- int min_uV, int max_uV)
+ int min_uV, int max_uV, unsigned *selector)
{
struct wm8400 *wm8400 = rdev_get_drvdata(dev);
u16 val;
@@ -171,6 +173,8 @@
return -EINVAL;
BUG_ON(850000 + (25000 * val) < min_uV);
+ *selector = val;
+
return wm8400_set_bits(wm8400, WM8400_DCDC1_CONTROL_1 + offset,
WM8400_DC1_VSEL_MASK, val);
}
diff --git a/drivers/regulator/wm8994-regulator.c b/drivers/regulator/wm8994-regulator.c
index 03713bc..594f88eb 100644
--- a/drivers/regulator/wm8994-regulator.c
+++ b/drivers/regulator/wm8994-regulator.c
@@ -86,7 +86,7 @@
return (selector * 100000) + 2400000;
}
-static int wm8994_ldo1_get_voltage(struct regulator_dev *rdev)
+static int wm8994_ldo1_get_voltage_sel(struct regulator_dev *rdev)
{
struct wm8994_ldo *ldo = rdev_get_drvdata(rdev);
int val;
@@ -95,13 +95,11 @@
if (val < 0)
return val;
- val = (val & WM8994_LDO1_VSEL_MASK) >> WM8994_LDO1_VSEL_SHIFT;
-
- return wm8994_ldo1_list_voltage(rdev, val);
+ return (val & WM8994_LDO1_VSEL_MASK) >> WM8994_LDO1_VSEL_SHIFT;
}
static int wm8994_ldo1_set_voltage(struct regulator_dev *rdev,
- int min_uV, int max_uV)
+ int min_uV, int max_uV, unsigned *s)
{
struct wm8994_ldo *ldo = rdev_get_drvdata(rdev);
int selector, v;
@@ -111,6 +109,7 @@
if (v < 0 || v > max_uV)
return -EINVAL;
+ *s = selector;
selector <<= WM8994_LDO1_VSEL_SHIFT;
return wm8994_set_bits(ldo->wm8994, WM8994_LDO_1,
@@ -124,7 +123,7 @@
.enable_time = wm8994_ldo_enable_time,
.list_voltage = wm8994_ldo1_list_voltage,
- .get_voltage = wm8994_ldo1_get_voltage,
+ .get_voltage_sel = wm8994_ldo1_get_voltage_sel,
.set_voltage = wm8994_ldo1_set_voltage,
};
@@ -137,7 +136,7 @@
return (selector * 100000) + 900000;
}
-static int wm8994_ldo2_get_voltage(struct regulator_dev *rdev)
+static int wm8994_ldo2_get_voltage_sel(struct regulator_dev *rdev)
{
struct wm8994_ldo *ldo = rdev_get_drvdata(rdev);
int val;
@@ -146,13 +145,11 @@
if (val < 0)
return val;
- val = (val & WM8994_LDO2_VSEL_MASK) >> WM8994_LDO2_VSEL_SHIFT;
-
- return wm8994_ldo2_list_voltage(rdev, val);
+ return (val & WM8994_LDO2_VSEL_MASK) >> WM8994_LDO2_VSEL_SHIFT;
}
static int wm8994_ldo2_set_voltage(struct regulator_dev *rdev,
- int min_uV, int max_uV)
+ int min_uV, int max_uV, unsigned *s)
{
struct wm8994_ldo *ldo = rdev_get_drvdata(rdev);
int selector, v;
@@ -162,6 +159,7 @@
if (v < 0 || v > max_uV)
return -EINVAL;
+ *s = selector;
selector <<= WM8994_LDO2_VSEL_SHIFT;
return wm8994_set_bits(ldo->wm8994, WM8994_LDO_2,
@@ -175,7 +173,7 @@
.enable_time = wm8994_ldo_enable_time,
.list_voltage = wm8994_ldo2_list_voltage,
- .get_voltage = wm8994_ldo2_get_voltage,
+ .get_voltage_sel = wm8994_ldo2_get_voltage_sel,
.set_voltage = wm8994_ldo2_set_voltage,
};
diff --git a/drivers/rtc/rtc-cmos.c b/drivers/rtc/rtc-cmos.c
index 7e6ce62..c7ff8df 100644
--- a/drivers/rtc/rtc-cmos.c
+++ b/drivers/rtc/rtc-cmos.c
@@ -36,6 +36,7 @@
#include <linux/platform_device.h>
#include <linux/mod_devicetable.h>
#include <linux/log2.h>
+#include <linux/pm.h>
/* this is for "generic access to PC-style RTC" using CMOS_READ/CMOS_WRITE */
#include <asm-generic/rtc.h>
@@ -851,7 +852,7 @@
#ifdef CONFIG_PM
-static int cmos_suspend(struct device *dev, pm_message_t mesg)
+static int cmos_suspend(struct device *dev)
{
struct cmos_rtc *cmos = dev_get_drvdata(dev);
unsigned char tmp;
@@ -899,7 +900,7 @@
*/
static inline int cmos_poweroff(struct device *dev)
{
- return cmos_suspend(dev, PMSG_HIBERNATE);
+ return cmos_suspend(dev);
}
static int cmos_resume(struct device *dev)
@@ -946,9 +947,9 @@
return 0;
}
+static SIMPLE_DEV_PM_OPS(cmos_pm_ops, cmos_suspend, cmos_resume);
+
#else
-#define cmos_suspend NULL
-#define cmos_resume NULL
static inline int cmos_poweroff(struct device *dev)
{
@@ -1078,7 +1079,7 @@
static int cmos_pnp_suspend(struct pnp_dev *pnp, pm_message_t mesg)
{
- return cmos_suspend(&pnp->dev, mesg);
+ return cmos_suspend(&pnp->dev);
}
static int cmos_pnp_resume(struct pnp_dev *pnp)
@@ -1158,8 +1159,9 @@
.shutdown = cmos_platform_shutdown,
.driver = {
.name = (char *) driver_name,
- .suspend = cmos_suspend,
- .resume = cmos_resume,
+#ifdef CONFIG_PM
+ .pm = &cmos_pm_ops,
+#endif
}
};
diff --git a/drivers/rtc/rtc-max6902.c b/drivers/rtc/rtc-max6902.c
index 657403e..0ec3f588 100644
--- a/drivers/rtc/rtc-max6902.c
+++ b/drivers/rtc/rtc-max6902.c
@@ -139,12 +139,13 @@
if (IS_ERR(rtc))
return PTR_ERR(rtc);
+ dev_set_drvdata(&spi->dev, rtc);
return 0;
}
static int __devexit max6902_remove(struct spi_device *spi)
{
- struct rtc_device *rtc = platform_get_drvdata(spi);
+ struct rtc_device *rtc = dev_get_drvdata(&spi->dev);
rtc_device_unregister(rtc);
return 0;
diff --git a/drivers/rtc/rtc-omap.c b/drivers/rtc/rtc-omap.c
index 73377b0..e72b523 100644
--- a/drivers/rtc/rtc-omap.c
+++ b/drivers/rtc/rtc-omap.c
@@ -429,13 +429,14 @@
fail0:
iounmap(rtc_base);
fail:
- release_resource(mem);
+ release_mem_region(mem->start, resource_size(mem));
return -EIO;
}
static int __exit omap_rtc_remove(struct platform_device *pdev)
{
struct rtc_device *rtc = platform_get_drvdata(pdev);
+ struct resource *mem = dev_get_drvdata(&rtc->dev);
device_init_wakeup(&pdev->dev, 0);
@@ -447,8 +448,9 @@
if (omap_rtc_timer != omap_rtc_alarm)
free_irq(omap_rtc_alarm, rtc);
- release_resource(dev_get_drvdata(&rtc->dev));
rtc_device_unregister(rtc);
+ iounmap(rtc_base);
+ release_mem_region(mem->start, resource_size(mem));
return 0;
}
diff --git a/drivers/s390/net/lcs.c b/drivers/s390/net/lcs.c
index 09e7a05..30b2a82 100644
--- a/drivers/s390/net/lcs.c
+++ b/drivers/s390/net/lcs.c
@@ -841,7 +841,7 @@
}
/**
- * Emit buffer of a lan comand.
+ * Emit buffer of a lan command.
*/
static void
lcs_lancmd_timeout(unsigned long data)
diff --git a/drivers/s390/scsi/zfcp_cfdc.c b/drivers/s390/scsi/zfcp_cfdc.c
index 46342fe..303dde0 100644
--- a/drivers/s390/scsi/zfcp_cfdc.c
+++ b/drivers/s390/scsi/zfcp_cfdc.c
@@ -317,7 +317,7 @@
/**
* zfcp_cfdc_port_denied - Process "access denied" for port
- * @port: The port where the acces has been denied
+ * @port: The port where the access has been denied
* @qual: The FSF status qualifier for the access denied FSF status
*/
void zfcp_cfdc_port_denied(struct zfcp_port *port,
diff --git a/drivers/scsi/a100u2w.c b/drivers/scsi/a100u2w.c
index dc5ac6e..a391090 100644
--- a/drivers/scsi/a100u2w.c
+++ b/drivers/scsi/a100u2w.c
@@ -416,7 +416,7 @@
/* Go back and check they match */
outb(PRGMRST | DOWNLOAD, host->base + ORC_RISCCTL); /* Reset program count 0 */
- bios_addr -= 0x1000; /* Reset the BIOS adddress */
+ bios_addr -= 0x1000; /* Reset the BIOS address */
for (i = 0, data32_ptr = (u8 *) & data32; /* Check the code */
i < 0x1000; /* Firmware code size = 4K */
i++, bios_addr++) {
diff --git a/drivers/scsi/aacraid/commsup.c b/drivers/scsi/aacraid/commsup.c
index afc9aeb..060ac4b 100644
--- a/drivers/scsi/aacraid/commsup.c
+++ b/drivers/scsi/aacraid/commsup.c
@@ -91,7 +91,7 @@
* aac_fib_setup - setup the fibs
* @dev: Adapter to set up
*
- * Allocate the PCI space for the fibs, map it and then intialise the
+ * Allocate the PCI space for the fibs, map it and then initialise the
* fib area, the unmapped fib data and also the free list
*/
diff --git a/drivers/scsi/aic7xxx_old/aic7xxx.seq b/drivers/scsi/aic7xxx_old/aic7xxx.seq
index 5997e7c..1565be9 100644
--- a/drivers/scsi/aic7xxx_old/aic7xxx.seq
+++ b/drivers/scsi/aic7xxx_old/aic7xxx.seq
@@ -1178,7 +1178,7 @@
/*
* Retrieve an SCB by SCBID first searching the disconnected list falling
* back to DMA'ing the SCB down from the host. This routine assumes that
- * ARG_1 is the SCBID of interrest and that SINDEX is the position in the
+ * ARG_1 is the SCBID of interest and that SINDEX is the position in the
* disconnected list to start the search from. If SINDEX is SCB_LIST_NULL,
* we go directly to the host for the SCB.
*/
diff --git a/drivers/scsi/aic94xx/aic94xx_reg_def.h b/drivers/scsi/aic94xx/aic94xx_reg_def.h
index 28aaf34..40273a7 100644
--- a/drivers/scsi/aic94xx/aic94xx_reg_def.h
+++ b/drivers/scsi/aic94xx/aic94xx_reg_def.h
@@ -1689,7 +1689,7 @@
#define PHY_START_CAL 0x01
/*
- * HST_PCIX2 Registers, Addresss Range: (0x00-0xFC)
+ * HST_PCIX2 Registers, Address Range: (0x00-0xFC)
*/
#define PCIX_REG_BASE_ADR 0xB8040000
@@ -1802,7 +1802,7 @@
#define PCIC_TP_CTRL 0xFC
/*
- * EXSI Registers, Addresss Range: (0x00-0xFC)
+ * EXSI Registers, Address Range: (0x00-0xFC)
*/
#define EXSI_REG_BASE_ADR REG_BASE_ADDR_EXSI
diff --git a/drivers/scsi/aic94xx/aic94xx_scb.c b/drivers/scsi/aic94xx/aic94xx_scb.c
index c43698b..2959327 100644
--- a/drivers/scsi/aic94xx/aic94xx_scb.c
+++ b/drivers/scsi/aic94xx/aic94xx_scb.c
@@ -867,7 +867,7 @@
* resources they have with this SCB, and then call this one at the
* end of their timeout function. To do this, one should initialize
* the ascb->timer.{function, data, expires} prior to calling the post
- * funcion. The timer is started by the post function.
+ * function. The timer is started by the post function.
*/
void asd_ascb_timedout(unsigned long data)
{
diff --git a/drivers/scsi/aic94xx/aic94xx_seq.c b/drivers/scsi/aic94xx/aic94xx_seq.c
index 7437461..390168f 100644
--- a/drivers/scsi/aic94xx/aic94xx_seq.c
+++ b/drivers/scsi/aic94xx/aic94xx_seq.c
@@ -797,7 +797,7 @@
int j;
/* Start from Page 1 of Mode 0 and 1. */
moffs = LSEQ_PAGE_SIZE + i*LSEQ_MODE_SCRATCH_SIZE;
- /* All the fields of page 1 can be intialized to 0. */
+ /* All the fields of page 1 can be initialized to 0. */
for (j = 0; j < LSEQ_PAGE_SIZE; j += 4)
asd_write_reg_dword(asd_ha, LmSCRATCH(lseq)+moffs+j,0);
}
@@ -938,7 +938,7 @@
asd_write_reg_dword(asd_ha, SCBPRO, 0);
asd_write_reg_dword(asd_ha, CSEQCON, 0);
- /* Intialize CSEQ Mode 11 Interrupt Vectors.
+ /* Initialize CSEQ Mode 11 Interrupt Vectors.
* The addresses are 16 bit wide and in dword units.
* The values of their macros are in byte units.
* Thus we have to divide by 4. */
@@ -961,7 +961,7 @@
asd_write_reg_word(asd_ha, CPRGMCNT, cseq_idle_loop);
for (i = 0; i < 8; i++) {
- /* Intialize Mode n Link m Interrupt Enable. */
+ /* Initialize Mode n Link m Interrupt Enable. */
asd_write_reg_dword(asd_ha, CMnINTEN(i), EN_CMnRSPMBXF);
/* Initialize Mode n Request Mailbox. */
asd_write_reg_dword(asd_ha, CMnREQMBX(i), 0);
diff --git a/drivers/scsi/bfa/bfa_fcpim.c b/drivers/scsi/bfa/bfa_fcpim.c
index 9c410b2..c0353cd 100644
--- a/drivers/scsi/bfa/bfa_fcpim.c
+++ b/drivers/scsi/bfa/bfa_fcpim.c
@@ -1838,7 +1838,7 @@
case BFA_IOIM_SM_ABORT:
/*
- * IO is alraedy being cleaned up implicitly
+ * IO is already being cleaned up implicitly
*/
ioim->io_cbfn = __bfa_cb_ioim_abort;
break;
diff --git a/drivers/scsi/bfa/bfa_fcs_lport.c b/drivers/scsi/bfa/bfa_fcs_lport.c
index 4e2eb92..43fa986b 100644
--- a/drivers/scsi/bfa/bfa_fcs_lport.c
+++ b/drivers/scsi/bfa/bfa_fcs_lport.c
@@ -5646,7 +5646,7 @@
switch (status) {
case BFA_STATUS_OK:
/*
- * Initialiaze the V-Port fields
+ * Initialize the V-Port fields
*/
__vport_fcid(vport) = vport->lps->lp_pid;
vport->vport_stats.fdisc_accepts++;
diff --git a/drivers/scsi/dc395x.c b/drivers/scsi/dc395x.c
index 8f1b5c8..b0f8523 100644
--- a/drivers/scsi/dc395x.c
+++ b/drivers/scsi/dc395x.c
@@ -3796,7 +3796,7 @@
* adapter_add_device - Adds the device instance to the adaptor instance.
*
* @acb: The adapter device to be updated
- * @dcb: A newly created and intialised device instance to add.
+ * @dcb: A newly created and initialised device instance to add.
**/
static void adapter_add_device(struct AdapterCtlBlk *acb,
struct DeviceCtlBlk *dcb)
@@ -4498,7 +4498,7 @@
* init_adapter - Grab the resource for the card, setup the adapter
* information, set the card into a known state, create the various
* tables etc etc. This basically gets all adapter information all up
- * to date, intialised and gets the chip in sync with it.
+ * to date, initialised and gets the chip in sync with it.
*
* @host: This hosts adapter structure
* @io_port: The base I/O port
@@ -4789,7 +4789,7 @@
* that it finds in the system. The pci_dev strcuture indicates which
* instance we are being called from.
*
- * @dev: The PCI device to intialize.
+ * @dev: The PCI device to initialize.
* @id: Looks like a pointer to the entry in our pci device table
* that was actually matched by the PCI subsystem.
*
@@ -4860,7 +4860,7 @@
* dc395x_remove_one - Called to remove a single instance of the
* adapter.
*
- * @dev: The PCI device to intialize.
+ * @dev: The PCI device to initialize.
**/
static void __devexit dc395x_remove_one(struct pci_dev *dev)
{
diff --git a/drivers/scsi/libfc/fc_fcp.c b/drivers/scsi/libfc/fc_fcp.c
index cdc06cd..5962d1a 100644
--- a/drivers/scsi/libfc/fc_fcp.c
+++ b/drivers/scsi/libfc/fc_fcp.c
@@ -1250,7 +1250,7 @@
/**
* fc_lun_reset() - Send a LUN RESET command to a device
* and wait for the reply
- * @lport: The local port to sent the comand on
+ * @lport: The local port to sent the command on
* @fsp: The FCP packet that identifies the LUN to be reset
* @id: The SCSI command ID
* @lun: The LUN ID to be reset
diff --git a/drivers/scsi/lpfc/lpfc_attr.c b/drivers/scsi/lpfc/lpfc_attr.c
index c06491b..3512abb 100644
--- a/drivers/scsi/lpfc/lpfc_attr.c
+++ b/drivers/scsi/lpfc/lpfc_attr.c
@@ -1335,7 +1335,7 @@
}
/**
- * lpfc_param_init - Intializes a cfg attribute
+ * lpfc_param_init - Initializes a cfg attribute
*
* Description:
* Macro that given an attr e.g. hba_queue_depth expands
diff --git a/drivers/scsi/lpfc/lpfc_hbadisc.c b/drivers/scsi/lpfc/lpfc_hbadisc.c
index f9f160a..bb01596 100644
--- a/drivers/scsi/lpfc/lpfc_hbadisc.c
+++ b/drivers/scsi/lpfc/lpfc_hbadisc.c
@@ -2852,7 +2852,7 @@
if (unlikely(!fcf_record)) {
lpfc_printf_log(phba, KERN_ERR,
LOG_MBOX | LOG_SLI,
- "2554 Could not allocate memmory for "
+ "2554 Could not allocate memory for "
"fcf record\n");
rc = -ENODEV;
goto out;
diff --git a/drivers/scsi/lpfc/lpfc_init.c b/drivers/scsi/lpfc/lpfc_init.c
index 462242d..6d0b36a 100644
--- a/drivers/scsi/lpfc/lpfc_init.c
+++ b/drivers/scsi/lpfc/lpfc_init.c
@@ -8071,7 +8071,7 @@
* the HBA.
*/
- /* HBA interrupt will be diabled after this call */
+ /* HBA interrupt will be disabled after this call */
lpfc_sli_hba_down(phba);
/* Stop kthread signal shall trigger work_done one more time */
kthread_stop(phba->worker_thread);
diff --git a/drivers/scsi/lpfc/lpfc_sli.c b/drivers/scsi/lpfc/lpfc_sli.c
index 634b2fe..a359d2b 100644
--- a/drivers/scsi/lpfc/lpfc_sli.c
+++ b/drivers/scsi/lpfc/lpfc_sli.c
@@ -10172,7 +10172,7 @@
* lpfc_sli4_queue_free - free a queue structure and associated memory
* @queue: The queue structure to free.
*
- * This function frees a queue structure and the DMAable memeory used for
+ * This function frees a queue structure and the DMAable memory used for
* the host resident queue. This function must be called after destroying the
* queue on the HBA.
**/
diff --git a/drivers/scsi/megaraid.h b/drivers/scsi/megaraid.h
index f564474..8534119 100644
--- a/drivers/scsi/megaraid.h
+++ b/drivers/scsi/megaraid.h
@@ -13,7 +13,7 @@
*/
/*
- * Comand coalescing - This feature allows the driver to be able to combine
+ * Command coalescing - This feature allows the driver to be able to combine
* two or more commands and issue as one command in order to boost I/O
* performance. Useful if the nature of the I/O is sequential. It is not very
* useful for random natured I/Os.
diff --git a/drivers/scsi/megaraid/megaraid_mm.c b/drivers/scsi/megaraid/megaraid_mm.c
index a7008c0..25506c7 100644
--- a/drivers/scsi/megaraid/megaraid_mm.c
+++ b/drivers/scsi/megaraid/megaraid_mm.c
@@ -224,7 +224,7 @@
{
int err;
- /* inconsistant: mraid_mm_compat_ioctl doesn't take the BKL */
+ /* inconsistent: mraid_mm_compat_ioctl doesn't take the BKL */
mutex_lock(&mraid_mm_mutex);
err = mraid_mm_ioctl(filep, cmd, arg);
mutex_unlock(&mraid_mm_mutex);
diff --git a/drivers/scsi/pm8001/pm8001_init.c b/drivers/scsi/pm8001/pm8001_init.c
index f8c86b2..b95285f 100644
--- a/drivers/scsi/pm8001/pm8001_init.c
+++ b/drivers/scsi/pm8001/pm8001_init.c
@@ -603,7 +603,7 @@
#endif
intx:
- /* intialize the INT-X interrupt */
+ /* initialize the INT-X interrupt */
rc = request_irq(pdev->irq, irq_handler, IRQF_SHARED, DRV_NAME,
SHOST_TO_SAS_HA(pm8001_ha->shost));
return rc;
diff --git a/drivers/scsi/scsi_netlink.c b/drivers/scsi/scsi_netlink.c
index d53e650..a2ed201 100644
--- a/drivers/scsi/scsi_netlink.c
+++ b/drivers/scsi/scsi_netlink.c
@@ -477,7 +477,7 @@
/**
- * scsi_netlink_init - Called by SCSI subsystem to intialize
+ * scsi_netlink_init - Called by SCSI subsystem to initialize
* the SCSI transport netlink interface
*
**/
diff --git a/drivers/scsi/scsi_sysfs.c b/drivers/scsi/scsi_sysfs.c
index 4c68d36..490ce21 100644
--- a/drivers/scsi/scsi_sysfs.c
+++ b/drivers/scsi/scsi_sysfs.c
@@ -864,13 +864,15 @@
error = device_add(&sdev->sdev_gendev);
if (error) {
- printk(KERN_INFO "error 1\n");
+ sdev_printk(KERN_INFO, sdev,
+ "failed to add device: %d\n", error);
return error;
}
device_enable_async_suspend(&sdev->sdev_dev);
error = device_add(&sdev->sdev_dev);
if (error) {
- printk(KERN_INFO "error 2\n");
+ sdev_printk(KERN_INFO, sdev,
+ "failed to add class device: %d\n", error);
device_del(&sdev->sdev_gendev);
return error;
}
diff --git a/drivers/scsi/sym53c8xx_2/sym_glue.c b/drivers/scsi/sym53c8xx_2/sym_glue.c
index 6b97ded..b4543f5 100644
--- a/drivers/scsi/sym53c8xx_2/sym_glue.c
+++ b/drivers/scsi/sym53c8xx_2/sym_glue.c
@@ -1866,7 +1866,7 @@
*
* This routine is similar to sym_set_workarounds(), except
* that, at this point, we already know that the device was
- * successfully intialized at least once before, and so most
+ * successfully initialized at least once before, and so most
* of the steps taken there are un-needed here.
*/
static void sym2_reset_workarounds(struct pci_dev *pdev)
diff --git a/drivers/spi/atmel_spi.c b/drivers/spi/atmel_spi.c
index a067046..1a478bf 100644
--- a/drivers/spi/atmel_spi.c
+++ b/drivers/spi/atmel_spi.c
@@ -341,9 +341,9 @@
/*
* For DMA, tx_buf/tx_dma have the same relationship as rx_buf/rx_dma:
* - The buffer is either valid for CPU access, else NULL
- * - If the buffer is valid, so is its DMA addresss
+ * - If the buffer is valid, so is its DMA address
*
- * This driver manages the dma addresss unless message->is_dma_mapped.
+ * This driver manages the dma address unless message->is_dma_mapped.
*/
static int
atmel_spi_dma_map_xfer(struct atmel_spi *as, struct spi_transfer *xfer)
diff --git a/drivers/spi/spidev.c b/drivers/spi/spidev.c
index 4e6245e..6034282 100644
--- a/drivers/spi/spidev.c
+++ b/drivers/spi/spidev.c
@@ -38,7 +38,7 @@
/*
- * This supports acccess to SPI devices using normal userspace I/O calls.
+ * This supports access to SPI devices using normal userspace I/O calls.
* Note that while traditional UNIX/POSIX I/O semantics are half duplex,
* and often mask message boundaries, full SPI support requires full duplex
* transfers. There are several kinds of internal message boundaries to
diff --git a/drivers/staging/Kconfig b/drivers/staging/Kconfig
index e2d5869..5c8fcfc 100644
--- a/drivers/staging/Kconfig
+++ b/drivers/staging/Kconfig
@@ -123,6 +123,8 @@
source "drivers/staging/iio/Kconfig"
+source "drivers/staging/cs5535_gpio/Kconfig"
+
source "drivers/staging/zram/Kconfig"
source "drivers/staging/wlags49_h2/Kconfig"
diff --git a/drivers/staging/Makefile b/drivers/staging/Makefile
index c7d2224..d538863 100644
--- a/drivers/staging/Makefile
+++ b/drivers/staging/Makefile
@@ -44,6 +44,7 @@
obj-$(CONFIG_MRST_RAR_HANDLER) += memrar/
obj-$(CONFIG_DX_SEP) += sep/
obj-$(CONFIG_IIO) += iio/
+obj-$(CONFIG_CS5535_GPIO) += cs5535_gpio/
obj-$(CONFIG_ZRAM) += zram/
obj-$(CONFIG_WLAGS49_H2) += wlags49_h2/
obj-$(CONFIG_WLAGS49_H25) += wlags49_h25/
diff --git a/drivers/staging/cs5535_gpio/Kconfig b/drivers/staging/cs5535_gpio/Kconfig
new file mode 100644
index 0000000..a1b3a8d
--- /dev/null
+++ b/drivers/staging/cs5535_gpio/Kconfig
@@ -0,0 +1,11 @@
+config CS5535_GPIO
+ tristate "AMD CS5535/CS5536 GPIO (Geode Companion Device)"
+ depends on X86_32
+ help
+ Note: this driver is DEPRECATED. Please use the cs5535-gpio module
+ in the GPIO section instead (CONFIG_GPIO_CS5535).
+
+ Give userspace access to the GPIO pins on the AMD CS5535 and
+ CS5536 Geode companion devices.
+
+ If compiled as a module, it will be called cs5535_gpio.
diff --git a/drivers/staging/cs5535_gpio/Makefile b/drivers/staging/cs5535_gpio/Makefile
new file mode 100644
index 0000000..d67c4b8
--- /dev/null
+++ b/drivers/staging/cs5535_gpio/Makefile
@@ -0,0 +1 @@
+obj-$(CONFIG_CS5535_GPIO) += cs5535_gpio.o
diff --git a/drivers/staging/cs5535_gpio/TODO b/drivers/staging/cs5535_gpio/TODO
new file mode 100644
index 0000000..98d1cd1
--- /dev/null
+++ b/drivers/staging/cs5535_gpio/TODO
@@ -0,0 +1,6 @@
+This is an obsolete driver for some the CS5535 and CS5536 southbridge GPIOs.
+It has been replaced by a driver that makes use of the Linux GPIO subsystem.
+Please switch to that driver, and let dilinger@queued.net know if there's
+anything missing from the new driver.
+
+This driver is scheduled for removal in 2.6.40.
diff --git a/drivers/char/cs5535_gpio.c b/drivers/staging/cs5535_gpio/cs5535_gpio.c
similarity index 100%
rename from drivers/char/cs5535_gpio.c
rename to drivers/staging/cs5535_gpio/cs5535_gpio.c
diff --git a/drivers/staging/msm/msm_fb_bl.c b/drivers/staging/msm/msm_fb_bl.c
index 033fc94..2a80775 100644
--- a/drivers/staging/msm/msm_fb_bl.c
+++ b/drivers/staging/msm/msm_fb_bl.c
@@ -42,7 +42,7 @@
return 0;
}
-static struct backlight_ops msm_fb_bl_ops = {
+static const struct backlight_ops msm_fb_bl_ops = {
.get_brightness = msm_fb_bl_get_brightness,
.update_status = msm_fb_bl_update_status,
};
diff --git a/drivers/staging/olpc_dcon/TODO b/drivers/staging/olpc_dcon/TODO
index ac2d3d0..35f9cda 100644
--- a/drivers/staging/olpc_dcon/TODO
+++ b/drivers/staging/olpc_dcon/TODO
@@ -1,6 +1,5 @@
TODO:
- checkpatch.pl cleanups
- - port geode gpio calls to newer cs5535 API
- see if vx855 gpio API can be made similar enough to cs5535 so we can
share more code
- allow simultaneous XO-1 and XO-1.5 support
diff --git a/drivers/staging/olpc_dcon/olpc_dcon.c b/drivers/staging/olpc_dcon/olpc_dcon.c
index 4ca45ec..9f26dc9 100644
--- a/drivers/staging/olpc_dcon/olpc_dcon.c
+++ b/drivers/staging/olpc_dcon/olpc_dcon.c
@@ -27,7 +27,6 @@
#include <asm/uaccess.h>
#include <linux/ctype.h>
#include <linux/reboot.h>
-#include <linux/gpio.h>
#include <asm/tsc.h>
#include <asm/olpc.h>
@@ -49,7 +48,7 @@
int (*init)(void);
void (*bus_stabilize_wiggle)(void);
void (*set_dconload)(int);
- int (*read_status)(void);
+ u8 (*read_status)(void);
};
static struct dcon_platform_data *pdata;
@@ -615,7 +614,7 @@
__ATTR(resumeline, 0644, dcon_resumeline_show, dcon_resumeline_store),
};
-static struct backlight_ops dcon_bl_ops = {
+static const struct backlight_ops dcon_bl_ops = {
.get_brightness = dconbl_get,
.update_status = dconbl_set
};
diff --git a/drivers/staging/olpc_dcon/olpc_dcon.h b/drivers/staging/olpc_dcon/olpc_dcon.h
index 6453ca4..e566d21 100644
--- a/drivers/staging/olpc_dcon/olpc_dcon.h
+++ b/drivers/staging/olpc_dcon/olpc_dcon.h
@@ -29,26 +29,6 @@
#define DCON_REG_SCAN_INT 9
#define DCON_REG_BRIGHT 10
-/* GPIO registers (CS5536) */
-
-#define MSR_LBAR_GPIO 0x5140000C
-
-#define GPIOx_OUT_VAL 0x00
-#define GPIOx_OUT_EN 0x04
-#define GPIOx_IN_EN 0x20
-#define GPIOx_INV_EN 0x24
-#define GPIOx_IN_FLTR_EN 0x28
-#define GPIOx_EVNTCNT_EN 0x2C
-#define GPIOx_READ_BACK 0x30
-#define GPIOx_EVNT_EN 0x38
-#define GPIOx_NEGEDGE_EN 0x44
-#define GPIOx_NEGEDGE_STS 0x4C
-#define GPIO_FLT7_AMNT 0xD8
-#define GPIO_MAP_X 0xE0
-#define GPIO_MAP_Y 0xE4
-#define GPIO_FE7_SEL 0xF7
-
-
/* Status values */
#define DCONSTAT_SCANINT 0
diff --git a/drivers/staging/olpc_dcon/olpc_dcon_xo_1.c b/drivers/staging/olpc_dcon/olpc_dcon_xo_1.c
index 779fb7d..043198d 100644
--- a/drivers/staging/olpc_dcon/olpc_dcon_xo_1.c
+++ b/drivers/staging/olpc_dcon/olpc_dcon_xo_1.c
@@ -10,54 +10,70 @@
* modify it under the terms of version 2 of the GNU General Public
* License as published by the Free Software Foundation.
*/
-
+#include <linux/cs5535.h>
+#include <linux/gpio.h>
#include <asm/olpc.h>
#include "olpc_dcon.h"
-/* Base address of the GPIO registers */
-static unsigned long gpio_base;
-
-/*
- * List of GPIOs that we care about:
- * (in) GPIO12 -- DCONBLANK
- * (in) GPIO[56] -- DCONSTAT[01]
- * (out) GPIO11 -- DCONLOAD
- */
-
-#define IN_GPIOS ((1<<5) | (1<<6) | (1<<7) | (1<<12))
-#define OUT_GPIOS (1<<11)
-
static int dcon_init_xo_1(void)
{
- unsigned long lo, hi;
unsigned char lob;
- rdmsr(MSR_LBAR_GPIO, lo, hi);
-
- /* Check the mask and whether GPIO is enabled (sanity check) */
- if (hi != 0x0000f001) {
- printk(KERN_ERR "GPIO not enabled -- cannot use DCON\n");
- return -ENODEV;
+ if (gpio_request(OLPC_GPIO_DCON_STAT0, "OLPC-DCON")) {
+ printk(KERN_ERR "olpc-dcon: failed to request STAT0 GPIO\n");
+ return -EIO;
+ }
+ if (gpio_request(OLPC_GPIO_DCON_STAT1, "OLPC-DCON")) {
+ printk(KERN_ERR "olpc-dcon: failed to request STAT1 GPIO\n");
+ goto err_gp_stat1;
+ }
+ if (gpio_request(OLPC_GPIO_DCON_IRQ, "OLPC-DCON")) {
+ printk(KERN_ERR "olpc-dcon: failed to request IRQ GPIO\n");
+ goto err_gp_irq;
+ }
+ if (gpio_request(OLPC_GPIO_DCON_LOAD, "OLPC-DCON")) {
+ printk(KERN_ERR "olpc-dcon: failed to request LOAD GPIO\n");
+ goto err_gp_load;
+ }
+ if (gpio_request(OLPC_GPIO_DCON_BLANK, "OLPC-DCON")) {
+ printk(KERN_ERR "olpc-dcon: failed to request BLANK GPIO\n");
+ goto err_gp_blank;
}
- /* Mask off the IO base address */
- gpio_base = lo & 0x0000ff00;
-
/* Turn off the event enable for GPIO7 just to be safe */
- outl(1 << (16+7), gpio_base + GPIOx_EVNT_EN);
+ cs5535_gpio_clear(OLPC_GPIO_DCON_IRQ, GPIO_EVENTS_ENABLE);
+
+ /*
+ * Determine the current state by reading the GPIO bit; earlier
+ * stages of the boot process have established the state.
+ *
+ * Note that we read GPIO_OUPUT_VAL rather than GPIO_READ_BACK here;
+ * this is because OFW will disable input for the pin and set a value..
+ * READ_BACK will only contain a valid value if input is enabled and
+ * then a value is set. So, future readings of the pin can use
+ * READ_BACK, but the first one cannot. Awesome, huh?
+ */
+ dcon_source = cs5535_gpio_isset(OLPC_GPIO_DCON_LOAD, GPIO_OUTPUT_VAL)
+ ? DCON_SOURCE_CPU
+ : DCON_SOURCE_DCON;
+ dcon_pending = dcon_source;
/* Set the directions for the GPIO pins */
- outl(OUT_GPIOS | (IN_GPIOS << 16), gpio_base + GPIOx_OUT_EN);
- outl(IN_GPIOS | (OUT_GPIOS << 16), gpio_base + GPIOx_IN_EN);
+ gpio_direction_input(OLPC_GPIO_DCON_STAT0);
+ gpio_direction_input(OLPC_GPIO_DCON_STAT1);
+ gpio_direction_input(OLPC_GPIO_DCON_IRQ);
+ gpio_direction_input(OLPC_GPIO_DCON_BLANK);
+ gpio_direction_output(OLPC_GPIO_DCON_LOAD,
+ dcon_source == DCON_SOURCE_CPU);
/* Set up the interrupt mappings */
/* Set the IRQ to pair 2 */
- geode_gpio_event_irq(OLPC_GPIO_DCON_IRQ, 2);
+ cs5535_gpio_setup_event(OLPC_GPIO_DCON_IRQ, 2, 0);
/* Enable group 2 to trigger the DCON interrupt */
- geode_gpio_set_irq(2, DCON_IRQ);
+ cs5535_gpio_set_irq(2, DCON_IRQ);
/* Select edge level for interrupt (in PIC) */
lob = inb(0x4d0);
@@ -65,52 +81,61 @@
outb(lob, 0x4d0);
/* Register the interupt handler */
- if (request_irq(DCON_IRQ, &dcon_interrupt, 0, "DCON", &dcon_driver))
- return -EIO;
+ if (request_irq(DCON_IRQ, &dcon_interrupt, 0, "DCON", &dcon_driver)) {
+ printk(KERN_ERR "olpc-dcon: failed to request DCON's irq\n");
+ goto err_req_irq;
+ }
/* Clear INV_EN for GPIO7 (DCONIRQ) */
- outl((1<<(16+7)), gpio_base + GPIOx_INV_EN);
+ cs5535_gpio_clear(OLPC_GPIO_DCON_IRQ, GPIO_INPUT_INVERT);
/* Enable filter for GPIO12 (DCONBLANK) */
- outl(1<<(12), gpio_base + GPIOx_IN_FLTR_EN);
+ cs5535_gpio_set(OLPC_GPIO_DCON_BLANK, GPIO_INPUT_FILTER);
/* Disable filter for GPIO7 */
- outl(1<<(16+7), gpio_base + GPIOx_IN_FLTR_EN);
+ cs5535_gpio_clear(OLPC_GPIO_DCON_IRQ, GPIO_INPUT_FILTER);
/* Disable event counter for GPIO7 (DCONIRQ) and GPIO12 (DCONBLANK) */
-
- outl(1<<(16+7), gpio_base + GPIOx_EVNTCNT_EN);
- outl(1<<(16+12), gpio_base + GPIOx_EVNTCNT_EN);
+ cs5535_gpio_clear(OLPC_GPIO_DCON_IRQ, GPIO_INPUT_EVENT_COUNT);
+ cs5535_gpio_clear(OLPC_GPIO_DCON_BLANK, GPIO_INPUT_EVENT_COUNT);
/* Add GPIO12 to the Filter Event Pair #7 */
- outb(12, gpio_base + GPIO_FE7_SEL);
+ cs5535_gpio_set(OLPC_GPIO_DCON_BLANK, GPIO_FE7_SEL);
/* Turn off negative Edge Enable for GPIO12 */
- outl(1<<(16+12), gpio_base + GPIOx_NEGEDGE_EN);
+ cs5535_gpio_clear(OLPC_GPIO_DCON_BLANK, GPIO_NEGATIVE_EDGE_EN);
/* Enable negative Edge Enable for GPIO7 */
- outl(1<<7, gpio_base + GPIOx_NEGEDGE_EN);
+ cs5535_gpio_set(OLPC_GPIO_DCON_IRQ, GPIO_NEGATIVE_EDGE_EN);
/* Zero the filter amount for Filter Event Pair #7 */
- outw(0, gpio_base + GPIO_FLT7_AMNT);
+ cs5535_gpio_set(0, GPIO_FLTR7_AMOUNT);
/* Clear the negative edge status for GPIO7 and GPIO12 */
- outl((1<<7) | (1<<12), gpio_base+0x4c);
+ cs5535_gpio_set(OLPC_GPIO_DCON_IRQ, GPIO_NEGATIVE_EDGE_STS);
+ cs5535_gpio_set(OLPC_GPIO_DCON_BLANK, GPIO_NEGATIVE_EDGE_STS);
/* FIXME: Clear the posiitive status as well, just to be sure */
- outl((1<<7) | (1<<12), gpio_base+0x48);
+ cs5535_gpio_set(OLPC_GPIO_DCON_IRQ, GPIO_POSITIVE_EDGE_STS);
+ cs5535_gpio_set(OLPC_GPIO_DCON_BLANK, GPIO_POSITIVE_EDGE_STS);
/* Enable events for GPIO7 (DCONIRQ) and GPIO12 (DCONBLANK) */
- outl((1<<(7))|(1<<12), gpio_base + GPIOx_EVNT_EN);
-
- /* Determine the current state by reading the GPIO bit */
- /* Earlier stages of the boot process have established the state */
- dcon_source = inl(gpio_base + GPIOx_OUT_VAL) & (1<<11)
- ? DCON_SOURCE_CPU
- : DCON_SOURCE_DCON;
- dcon_pending = dcon_source;
+ cs5535_gpio_set(OLPC_GPIO_DCON_IRQ, GPIO_EVENTS_ENABLE);
+ cs5535_gpio_set(OLPC_GPIO_DCON_BLANK, GPIO_EVENTS_ENABLE);
return 0;
+
+err_req_irq:
+ gpio_free(OLPC_GPIO_DCON_BLANK);
+err_gp_blank:
+ gpio_free(OLPC_GPIO_DCON_LOAD);
+err_gp_load:
+ gpio_free(OLPC_GPIO_DCON_IRQ);
+err_gp_irq:
+ gpio_free(OLPC_GPIO_DCON_STAT1);
+err_gp_stat1:
+ gpio_free(OLPC_GPIO_DCON_STAT0);
+ return -EIO;
}
static void dcon_wiggle_xo_1(void)
@@ -128,37 +153,44 @@
* simultaneously set AUX1 IN/OUT to GPIO14; ditto for SMB_DATA and
* GPIO15.
*/
- geode_gpio_set(OLPC_GPIO_SMB_CLK|OLPC_GPIO_SMB_DATA, GPIO_OUTPUT_VAL);
- geode_gpio_set(OLPC_GPIO_SMB_CLK|OLPC_GPIO_SMB_DATA, GPIO_OUTPUT_ENABLE);
- geode_gpio_clear(OLPC_GPIO_SMB_CLK|OLPC_GPIO_SMB_DATA, GPIO_OUTPUT_AUX1);
- geode_gpio_clear(OLPC_GPIO_SMB_CLK|OLPC_GPIO_SMB_DATA, GPIO_OUTPUT_AUX2);
- geode_gpio_clear(OLPC_GPIO_SMB_CLK|OLPC_GPIO_SMB_DATA, GPIO_INPUT_AUX1);
+ cs5535_gpio_set(OLPC_GPIO_SMB_CLK, GPIO_OUTPUT_VAL);
+ cs5535_gpio_set(OLPC_GPIO_SMB_DATA, GPIO_OUTPUT_VAL);
+ cs5535_gpio_set(OLPC_GPIO_SMB_CLK, GPIO_OUTPUT_ENABLE);
+ cs5535_gpio_set(OLPC_GPIO_SMB_DATA, GPIO_OUTPUT_ENABLE);
+ cs5535_gpio_clear(OLPC_GPIO_SMB_CLK, GPIO_OUTPUT_AUX1);
+ cs5535_gpio_clear(OLPC_GPIO_SMB_DATA, GPIO_OUTPUT_AUX1);
+ cs5535_gpio_clear(OLPC_GPIO_SMB_CLK, GPIO_OUTPUT_AUX2);
+ cs5535_gpio_clear(OLPC_GPIO_SMB_DATA, GPIO_OUTPUT_AUX2);
+ cs5535_gpio_clear(OLPC_GPIO_SMB_CLK, GPIO_INPUT_AUX1);
+ cs5535_gpio_clear(OLPC_GPIO_SMB_DATA, GPIO_INPUT_AUX1);
for (x = 0; x < 16; x++) {
udelay(5);
- geode_gpio_clear(OLPC_GPIO_SMB_CLK, GPIO_OUTPUT_VAL);
+ cs5535_gpio_clear(OLPC_GPIO_SMB_CLK, GPIO_OUTPUT_VAL);
udelay(5);
- geode_gpio_set(OLPC_GPIO_SMB_CLK, GPIO_OUTPUT_VAL);
+ cs5535_gpio_set(OLPC_GPIO_SMB_CLK, GPIO_OUTPUT_VAL);
}
udelay(5);
- geode_gpio_set(OLPC_GPIO_SMB_CLK|OLPC_GPIO_SMB_DATA, GPIO_OUTPUT_AUX1);
- geode_gpio_set(OLPC_GPIO_SMB_CLK|OLPC_GPIO_SMB_DATA, GPIO_INPUT_AUX1);
+ cs5535_gpio_set(OLPC_GPIO_SMB_CLK, GPIO_OUTPUT_AUX1);
+ cs5535_gpio_set(OLPC_GPIO_SMB_DATA, GPIO_OUTPUT_AUX1);
+ cs5535_gpio_set(OLPC_GPIO_SMB_CLK, GPIO_INPUT_AUX1);
+ cs5535_gpio_set(OLPC_GPIO_SMB_DATA, GPIO_INPUT_AUX1);
}
static void dcon_set_dconload_1(int val)
{
- if (val)
- outl(1<<11, gpio_base + GPIOx_OUT_VAL);
- else
- outl(1<<(11 + 16), gpio_base + GPIOx_OUT_VAL);
+ gpio_set_value(OLPC_GPIO_DCON_LOAD, val);
}
-static int dcon_read_status_xo_1(void)
+static u8 dcon_read_status_xo_1(void)
{
- int status = inl(gpio_base + GPIOx_READ_BACK) >> 5;
-
+ u8 status;
+
+ status = gpio_get_value(OLPC_GPIO_DCON_STAT0);
+ status |= gpio_get_value(OLPC_GPIO_DCON_STAT1) << 1;
+
/* Clear the negative edge status for GPIO7 */
- outl(1 << 7, gpio_base + GPIOx_NEGEDGE_STS);
+ cs5535_gpio_set(OLPC_GPIO_DCON_IRQ, GPIO_NEGATIVE_EDGE_STS);
return status;
}
diff --git a/drivers/staging/olpc_dcon/olpc_dcon_xo_1_5.c b/drivers/staging/olpc_dcon/olpc_dcon_xo_1_5.c
index cca6a23..4f56098 100644
--- a/drivers/staging/olpc_dcon/olpc_dcon_xo_1_5.c
+++ b/drivers/staging/olpc_dcon/olpc_dcon_xo_1_5.c
@@ -195,9 +195,9 @@
}
}
-static int dcon_read_status_xo_1_5(void)
+static u8 dcon_read_status_xo_1_5(void)
{
- int status;
+ u8 status;
if (!dcon_was_irq())
return -1;
diff --git a/drivers/staging/samsung-laptop/samsung-laptop.c b/drivers/staging/samsung-laptop/samsung-laptop.c
index ac2bf11..701e8d5 100644
--- a/drivers/staging/samsung-laptop/samsung-laptop.c
+++ b/drivers/staging/samsung-laptop/samsung-laptop.c
@@ -269,7 +269,7 @@
return 0;
}
-static struct backlight_ops backlight_ops = {
+static const struct backlight_ops backlight_ops = {
.get_brightness = get_brightness,
.update_status = update_status,
};
diff --git a/drivers/telephony/ixj.c b/drivers/telephony/ixj.c
index 0d236f4..b001019 100644
--- a/drivers/telephony/ixj.c
+++ b/drivers/telephony/ixj.c
@@ -284,12 +284,11 @@
module_param(ixjdebug, int, 0);
-static struct pci_device_id ixj_pci_tbl[] __devinitdata = {
+static DEFINE_PCI_DEVICE_TABLE(ixj_pci_tbl) = {
{ PCI_VENDOR_ID_QUICKNET, PCI_DEVICE_ID_QUICKNET_XJ,
PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
{ }
};
-
MODULE_DEVICE_TABLE(pci, ixj_pci_tbl);
/************************************************************************
@@ -6581,7 +6580,8 @@
case IXJCTL_SET_FILTER:
if (copy_from_user(&jf, argp, sizeof(jf)))
retval = -EFAULT;
- retval = ixj_init_filter(j, &jf);
+ else
+ retval = ixj_init_filter(j, &jf);
break;
case IXJCTL_SET_FILTER_RAW:
if (copy_from_user(&jfr, argp, sizeof(jfr)))
diff --git a/drivers/usb/gadget/imx_udc.c b/drivers/usb/gadget/imx_udc.c
index 1210534..5408186 100644
--- a/drivers/usb/gadget/imx_udc.c
+++ b/drivers/usb/gadget/imx_udc.c
@@ -1320,7 +1320,7 @@
};
/*******************************************************************************
- * USB gadged driver functions
+ * USB gadget driver functions
*******************************************************************************
*/
int usb_gadget_probe_driver(struct usb_gadget_driver *driver,
diff --git a/drivers/usb/gadget/langwell_udc.c b/drivers/usb/gadget/langwell_udc.c
index 7779724..1eca8b4 100644
--- a/drivers/usb/gadget/langwell_udc.c
+++ b/drivers/usb/gadget/langwell_udc.c
@@ -3086,7 +3086,7 @@
kfree(dev->ep);
- /* diable IRQ handler */
+ /* disable IRQ handler */
if (dev->got_irq)
free_irq(pdev->irq, dev);
@@ -3406,7 +3406,7 @@
/* disable interrupt and set controller to stop state */
langwell_udc_stop(dev);
- /* diable IRQ handler */
+ /* disable IRQ handler */
if (dev->got_irq)
free_irq(pdev->irq, dev);
dev->got_irq = 0;
diff --git a/drivers/usb/host/fhci-hcd.c b/drivers/usb/host/fhci-hcd.c
index 20092a2..12fd184 100644
--- a/drivers/usb/host/fhci-hcd.c
+++ b/drivers/usb/host/fhci-hcd.c
@@ -98,13 +98,13 @@
usb->intr_nesting_cnt--;
}
-/* diable the usb interrupt */
+/* disable the usb interrupt */
void fhci_usb_disable_interrupt(struct fhci_usb *usb)
{
struct fhci_hcd *fhci = usb->fhci;
if (usb->intr_nesting_cnt == 0) {
- /* diable the timer interrupt */
+ /* disable the timer interrupt */
disable_irq_nosync(fhci->timer->irq);
/* disable the usb interrupt */
diff --git a/drivers/usb/host/fhci-tds.c b/drivers/usb/host/fhci-tds.c
index 7be548c..38fe058 100644
--- a/drivers/usb/host/fhci-tds.c
+++ b/drivers/usb/host/fhci-tds.c
@@ -271,8 +271,8 @@
/*
* Collect the submitted frames and inform the application about them
- * It is also prepearing the TDs for new frames. If the Tx interrupts
- * are diabled, the application should call that routine to get
+ * It is also preparing the TDs for new frames. If the Tx interrupts
+ * are disabled, the application should call that routine to get
* confirmation about the submitted frames. Otherwise, the routine is
* called frome the interrupt service routine during the Tx interrupt.
* In that case the application is informed by calling the application
diff --git a/drivers/usb/host/imx21-hcd.c b/drivers/usb/host/imx21-hcd.c
index e49b75a..f90d003 100644
--- a/drivers/usb/host/imx21-hcd.c
+++ b/drivers/usb/host/imx21-hcd.c
@@ -1658,7 +1658,7 @@
spin_lock_irqsave(&imx21->lock, flags);
- /* Reset the Host controler modules */
+ /* Reset the Host controller modules */
writel(USBOTG_RST_RSTCTRL | USBOTG_RST_RSTRH |
USBOTG_RST_RSTHSIE | USBOTG_RST_RSTHC,
imx21->regs + USBOTG_RST_CTRL);
diff --git a/drivers/usb/host/oxu210hp-hcd.c b/drivers/usb/host/oxu210hp-hcd.c
index 32149be..e0cb12b 100644
--- a/drivers/usb/host/oxu210hp-hcd.c
+++ b/drivers/usb/host/oxu210hp-hcd.c
@@ -3094,7 +3094,7 @@
/* Some boards (mostly VIA?) report bogus overcurrent indications,
* causing massive log spam unless we completely ignore them. It
- * may be relevant that VIA VT8235 controlers, where PORT_POWER is
+ * may be relevant that VIA VT8235 controllers, where PORT_POWER is
* always set, seem to clear PORT_OCC and PORT_CSC when writing to
* PORT_POWER; that's surprising, but maybe within-spec.
*/
diff --git a/drivers/usb/misc/adutux.c b/drivers/usb/misc/adutux.c
index 44f8b922..a6afd15 100644
--- a/drivers/usb/misc/adutux.c
+++ b/drivers/usb/misc/adutux.c
@@ -717,7 +717,7 @@
goto exit;
}
- /* allocate memory for our device state and intialize it */
+ /* allocate memory for our device state and initialize it */
dev = kzalloc(sizeof(struct adu_device), GFP_KERNEL);
if (dev == NULL) {
dev_err(&interface->dev, "Out of memory\n");
diff --git a/drivers/usb/misc/iowarrior.c b/drivers/usb/misc/iowarrior.c
index c9078e4..e573e4704 100644
--- a/drivers/usb/misc/iowarrior.c
+++ b/drivers/usb/misc/iowarrior.c
@@ -769,7 +769,7 @@
int i;
int retval = -ENOMEM;
- /* allocate memory for our device state and intialize it */
+ /* allocate memory for our device state and initialize it */
dev = kzalloc(sizeof(struct iowarrior), GFP_KERNEL);
if (dev == NULL) {
dev_err(&interface->dev, "Out of memory\n");
diff --git a/drivers/usb/misc/ldusb.c b/drivers/usb/misc/ldusb.c
index edffef6..eefb827 100644
--- a/drivers/usb/misc/ldusb.c
+++ b/drivers/usb/misc/ldusb.c
@@ -642,7 +642,7 @@
int i;
int retval = -ENOMEM;
- /* allocate memory for our device state and intialize it */
+ /* allocate memory for our device state and initialize it */
dev = kzalloc(sizeof(*dev), GFP_KERNEL);
if (dev == NULL) {
diff --git a/drivers/usb/musb/musb_gadget.c b/drivers/usb/musb/musb_gadget.c
index 9b162df..ed58c6c 100644
--- a/drivers/usb/musb/musb_gadget.c
+++ b/drivers/usb/musb/musb_gadget.c
@@ -1684,7 +1684,7 @@
struct musb_hw_ep *hw_ep;
unsigned count = 0;
- /* intialize endpoint list just once */
+ /* initialize endpoint list just once */
INIT_LIST_HEAD(&(musb->g.ep_list));
for (epnum = 0, hw_ep = musb->endpoints;
@@ -1765,7 +1765,7 @@
*
* -EINVAL something went wrong (not driver)
* -EBUSY another gadget is already using the controller
- * -ENOMEM no memeory to perform the operation
+ * -ENOMEM no memory to perform the operation
*
* @param driver the gadget driver
* @param bind the driver's bind function
diff --git a/drivers/usb/wusbcore/wa-rpipe.c b/drivers/usb/wusbcore/wa-rpipe.c
index c7b1d81..8cb9d80 100644
--- a/drivers/usb/wusbcore/wa-rpipe.c
+++ b/drivers/usb/wusbcore/wa-rpipe.c
@@ -49,7 +49,7 @@
*
* USB Stack port number 4 (1 based)
* WUSB code port index 3 (0 based)
- * USB Addresss 5 (2 based -- 0 is for default, 1 for root hub)
+ * USB Address 5 (2 based -- 0 is for default, 1 for root hub)
*
* Now, because we don't use the concept as default address exactly
* like the (wired) USB code does, we need to kind of skip it. So we
diff --git a/drivers/video/atmel_lcdfb.c b/drivers/video/atmel_lcdfb.c
index 8dce251..bac16345 100644
--- a/drivers/video/atmel_lcdfb.c
+++ b/drivers/video/atmel_lcdfb.c
@@ -111,7 +111,7 @@
return lcdc_readl(sinfo, ATMEL_LCDC_CONTRAST_VAL);
}
-static struct backlight_ops atmel_lcdc_bl_ops = {
+static const struct backlight_ops atmel_lcdc_bl_ops = {
.update_status = atmel_bl_update_status,
.get_brightness = atmel_bl_get_brightness,
};
diff --git a/drivers/video/aty/aty128fb.c b/drivers/video/aty/aty128fb.c
index 34a0851..dd9de2e 100644
--- a/drivers/video/aty/aty128fb.c
+++ b/drivers/video/aty/aty128fb.c
@@ -1786,7 +1786,7 @@
return bd->props.brightness;
}
-static struct backlight_ops aty128_bl_data = {
+static const struct backlight_ops aty128_bl_data = {
.get_brightness = aty128_bl_get_brightness,
.update_status = aty128_bl_update_status,
};
diff --git a/drivers/video/aty/atyfb_base.c b/drivers/video/aty/atyfb_base.c
index 5a3ce3a..767ab4f 100644
--- a/drivers/video/aty/atyfb_base.c
+++ b/drivers/video/aty/atyfb_base.c
@@ -2221,7 +2221,7 @@
return bd->props.brightness;
}
-static struct backlight_ops aty_bl_data = {
+static const struct backlight_ops aty_bl_data = {
.get_brightness = aty_bl_get_brightness,
.update_status = aty_bl_update_status,
};
diff --git a/drivers/video/aty/radeon_backlight.c b/drivers/video/aty/radeon_backlight.c
index 256966e..9b811dd 100644
--- a/drivers/video/aty/radeon_backlight.c
+++ b/drivers/video/aty/radeon_backlight.c
@@ -128,7 +128,7 @@
return bd->props.brightness;
}
-static struct backlight_ops radeon_bl_data = {
+static const struct backlight_ops radeon_bl_data = {
.get_brightness = radeon_bl_get_brightness,
.update_status = radeon_bl_update_status,
};
diff --git a/drivers/video/backlight/88pm860x_bl.c b/drivers/video/backlight/88pm860x_bl.c
index 38ffc3f..c789c46 100644
--- a/drivers/video/backlight/88pm860x_bl.c
+++ b/drivers/video/backlight/88pm860x_bl.c
@@ -155,7 +155,7 @@
return -EINVAL;
}
-static struct backlight_ops pm860x_backlight_ops = {
+static const struct backlight_ops pm860x_backlight_ops = {
.options = BL_CORE_SUSPENDRESUME,
.update_status = pm860x_backlight_update_status,
.get_brightness = pm860x_backlight_get_brightness,
diff --git a/drivers/video/backlight/l4f00242t03.c b/drivers/video/backlight/l4f00242t03.c
index c67801e..98ad3e5 100644
--- a/drivers/video/backlight/l4f00242t03.c
+++ b/drivers/video/backlight/l4f00242t03.c
@@ -25,7 +25,7 @@
struct l4f00242t03_priv {
struct spi_device *spi;
struct lcd_device *ld;
- int lcd_on:1;
+ int lcd_state;
struct regulator *io_reg;
struct regulator *core_reg;
};
@@ -62,11 +62,36 @@
regulator_enable(priv->core_reg);
}
+ l4f00242t03_reset(pdata->reset_gpio);
+
gpio_set_value(pdata->data_enable_gpio, 1);
msleep(60);
spi_write(spi, (const u8 *)cmd, ARRAY_SIZE(cmd) * sizeof(u16));
}
+static void l4f00242t03_lcd_powerdown(struct spi_device *spi)
+{
+ struct l4f00242t03_pdata *pdata = spi->dev.platform_data;
+ struct l4f00242t03_priv *priv = dev_get_drvdata(&spi->dev);
+
+ dev_dbg(&spi->dev, "Powering down LCD\n");
+
+ gpio_set_value(pdata->data_enable_gpio, 0);
+
+ if (priv->io_reg)
+ regulator_disable(priv->io_reg);
+
+ if (priv->core_reg)
+ regulator_disable(priv->core_reg);
+}
+
+static int l4f00242t03_lcd_power_get(struct lcd_device *ld)
+{
+ struct l4f00242t03_priv *priv = lcd_get_data(ld);
+
+ return priv->lcd_state;
+}
+
static int l4f00242t03_lcd_power_set(struct lcd_device *ld, int power)
{
struct l4f00242t03_priv *priv = lcd_get_data(ld);
@@ -79,35 +104,54 @@
const u16 disoff = 0x28;
if (power <= FB_BLANK_NORMAL) {
- if (priv->lcd_on)
- return 0;
+ if (priv->lcd_state <= FB_BLANK_NORMAL) {
+ /* Do nothing, the LCD is running */
+ } else if (priv->lcd_state < FB_BLANK_POWERDOWN) {
+ dev_dbg(&spi->dev, "Resuming LCD\n");
- dev_dbg(&spi->dev, "turning on LCD\n");
+ spi_write(spi, (const u8 *)&slpout, sizeof(u16));
+ msleep(60);
+ spi_write(spi, (const u8 *)&dison, sizeof(u16));
+ } else {
+ /* priv->lcd_state == FB_BLANK_POWERDOWN */
+ l4f00242t03_lcd_init(spi);
+ priv->lcd_state = FB_BLANK_VSYNC_SUSPEND;
+ l4f00242t03_lcd_power_set(priv->ld, power);
+ }
+ } else if (power < FB_BLANK_POWERDOWN) {
+ if (priv->lcd_state <= FB_BLANK_NORMAL) {
+ /* Send the display in standby */
+ dev_dbg(&spi->dev, "Standby the LCD\n");
- spi_write(spi, (const u8 *)&slpout, sizeof(u16));
- msleep(60);
- spi_write(spi, (const u8 *)&dison, sizeof(u16));
-
- priv->lcd_on = 1;
+ spi_write(spi, (const u8 *)&disoff, sizeof(u16));
+ msleep(60);
+ spi_write(spi, (const u8 *)&slpin, sizeof(u16));
+ } else if (priv->lcd_state < FB_BLANK_POWERDOWN) {
+ /* Do nothing, the LCD is already in standby */
+ } else {
+ /* priv->lcd_state == FB_BLANK_POWERDOWN */
+ l4f00242t03_lcd_init(spi);
+ priv->lcd_state = FB_BLANK_UNBLANK;
+ l4f00242t03_lcd_power_set(ld, power);
+ }
} else {
- if (!priv->lcd_on)
- return 0;
-
- dev_dbg(&spi->dev, "turning off LCD\n");
-
- spi_write(spi, (const u8 *)&disoff, sizeof(u16));
- msleep(60);
- spi_write(spi, (const u8 *)&slpin, sizeof(u16));
-
- priv->lcd_on = 0;
+ /* power == FB_BLANK_POWERDOWN */
+ if (priv->lcd_state != FB_BLANK_POWERDOWN) {
+ /* Clear the screen before shutting down */
+ spi_write(spi, (const u8 *)&disoff, sizeof(u16));
+ msleep(60);
+ l4f00242t03_lcd_powerdown(spi);
+ }
}
+ priv->lcd_state = power;
+
return 0;
}
static struct lcd_ops l4f_ops = {
.set_power = l4f00242t03_lcd_power_set,
- .get_power = NULL,
+ .get_power = l4f00242t03_lcd_power_get,
};
static int __devinit l4f00242t03_probe(struct spi_device *spi)
@@ -185,9 +229,9 @@
}
/* Init the LCD */
- l4f00242t03_reset(pdata->reset_gpio);
l4f00242t03_lcd_init(spi);
- l4f00242t03_lcd_power_set(priv->ld, 1);
+ priv->lcd_state = FB_BLANK_VSYNC_SUSPEND;
+ l4f00242t03_lcd_power_set(priv->ld, FB_BLANK_UNBLANK);
dev_info(&spi->dev, "Epson l4f00242t03 lcd probed.\n");
@@ -214,9 +258,11 @@
struct l4f00242t03_priv *priv = dev_get_drvdata(&spi->dev);
struct l4f00242t03_pdata *pdata = priv->spi->dev.platform_data;
- l4f00242t03_lcd_power_set(priv->ld, 0);
+ l4f00242t03_lcd_power_set(priv->ld, FB_BLANK_POWERDOWN);
lcd_device_unregister(priv->ld);
+ dev_set_drvdata(&spi->dev, NULL);
+
gpio_free(pdata->data_enable_gpio);
gpio_free(pdata->reset_gpio);
@@ -230,6 +276,15 @@
return 0;
}
+static void l4f00242t03_shutdown(struct spi_device *spi)
+{
+ struct l4f00242t03_priv *priv = dev_get_drvdata(&spi->dev);
+
+ if (priv)
+ l4f00242t03_lcd_power_set(priv->ld, FB_BLANK_POWERDOWN);
+
+}
+
static struct spi_driver l4f00242t03_driver = {
.driver = {
.name = "l4f00242t03",
@@ -237,6 +292,7 @@
},
.probe = l4f00242t03_probe,
.remove = __devexit_p(l4f00242t03_remove),
+ .shutdown = l4f00242t03_shutdown,
};
static __init int l4f00242t03_init(void)
diff --git a/drivers/video/backlight/max8925_bl.c b/drivers/video/backlight/max8925_bl.c
index b2b2c7b..209acc1 100644
--- a/drivers/video/backlight/max8925_bl.c
+++ b/drivers/video/backlight/max8925_bl.c
@@ -92,7 +92,7 @@
return ret;
}
-static struct backlight_ops max8925_backlight_ops = {
+static const struct backlight_ops max8925_backlight_ops = {
.options = BL_CORE_SUSPENDRESUME,
.update_status = max8925_backlight_update_status,
.get_brightness = max8925_backlight_get_brightness,
diff --git a/drivers/video/console/vgacon.c b/drivers/video/console/vgacon.c
index 915448e..c97491b 100644
--- a/drivers/video/console/vgacon.c
+++ b/drivers/video/console/vgacon.c
@@ -375,7 +375,8 @@
u16 saved1, saved2;
volatile u16 *p;
- if (screen_info.orig_video_isVGA == VIDEO_TYPE_VLFB) {
+ if (screen_info.orig_video_isVGA == VIDEO_TYPE_VLFB ||
+ screen_info.orig_video_isVGA == VIDEO_TYPE_EFI) {
no_vga:
#ifdef CONFIG_DUMMY_CONSOLE
conswitchp = &dummy_con;
diff --git a/drivers/video/nvidia/nv_backlight.c b/drivers/video/nvidia/nv_backlight.c
index 2fb552a..6aac6d1 100644
--- a/drivers/video/nvidia/nv_backlight.c
+++ b/drivers/video/nvidia/nv_backlight.c
@@ -87,7 +87,7 @@
return bd->props.brightness;
}
-static struct backlight_ops nvidia_bl_ops = {
+static const struct backlight_ops nvidia_bl_ops = {
.get_brightness = nvidia_bl_get_brightness,
.update_status = nvidia_bl_update_status,
};
diff --git a/drivers/video/omap2/displays/panel-taal.c b/drivers/video/omap2/displays/panel-taal.c
index e1c765d..61026f9 100644
--- a/drivers/video/omap2/displays/panel-taal.c
+++ b/drivers/video/omap2/displays/panel-taal.c
@@ -465,7 +465,7 @@
return 0;
}
-static struct backlight_ops taal_bl_ops = {
+static const struct backlight_ops taal_bl_ops = {
.get_brightness = taal_bl_get_intensity,
.update_status = taal_bl_update_status,
};
diff --git a/drivers/video/riva/fbdev.c b/drivers/video/riva/fbdev.c
index 618f36b..da38818 100644
--- a/drivers/video/riva/fbdev.c
+++ b/drivers/video/riva/fbdev.c
@@ -331,7 +331,7 @@
return bd->props.brightness;
}
-static struct backlight_ops riva_bl_ops = {
+static const struct backlight_ops riva_bl_ops = {
.get_brightness = riva_bl_get_brightness,
.update_status = riva_bl_update_status,
};
diff --git a/drivers/video/sstfb.c b/drivers/video/sstfb.c
index dee64c3..2ab7041 100644
--- a/drivers/video/sstfb.c
+++ b/drivers/video/sstfb.c
@@ -536,7 +536,7 @@
fbiinit2 = sst_read(FBIINIT2);
fbiinit3 = sst_read(FBIINIT3);
- /* everything is reset. we enable fbiinit2/3 remap : dac acces ok */
+ /* everything is reset. we enable fbiinit2/3 remap : dac access ok */
pci_write_config_dword(sst_dev, PCI_INIT_ENABLE,
PCI_EN_INIT_WR | PCI_REMAP_DAC );
diff --git a/drivers/w1/slaves/Kconfig b/drivers/w1/slaves/Kconfig
index 1f51366..f0c9096 100644
--- a/drivers/w1/slaves/Kconfig
+++ b/drivers/w1/slaves/Kconfig
@@ -16,6 +16,17 @@
Say Y here if you want to connect 1-wire
simple 64bit memory rom(ds2401/ds2411/ds1990*) to your wire.
+config W1_SLAVE_DS2423
+ tristate "Counter 1-wire device (DS2423)"
+ select CRC16
+ help
+ If you enable this you can read the counter values available
+ in the DS2423 chipset from the w1_slave file under the
+ sys file system.
+
+ Say Y here if you want to use a 1-wire
+ counter family device (DS2423).
+
config W1_SLAVE_DS2431
tristate "1kb EEPROM family support (DS2431)"
help
diff --git a/drivers/w1/slaves/Makefile b/drivers/w1/slaves/Makefile
index f1f51f1..3c76350 100644
--- a/drivers/w1/slaves/Makefile
+++ b/drivers/w1/slaves/Makefile
@@ -4,6 +4,7 @@
obj-$(CONFIG_W1_SLAVE_THERM) += w1_therm.o
obj-$(CONFIG_W1_SLAVE_SMEM) += w1_smem.o
+obj-$(CONFIG_W1_SLAVE_DS2423) += w1_ds2423.o
obj-$(CONFIG_W1_SLAVE_DS2431) += w1_ds2431.o
obj-$(CONFIG_W1_SLAVE_DS2433) += w1_ds2433.o
obj-$(CONFIG_W1_SLAVE_DS2760) += w1_ds2760.o
diff --git a/drivers/w1/slaves/w1_ds2423.c b/drivers/w1/slaves/w1_ds2423.c
new file mode 100644
index 0000000..7a7dbe50
--- /dev/null
+++ b/drivers/w1/slaves/w1_ds2423.c
@@ -0,0 +1,166 @@
+/*
+ * w1_ds2423.c
+ *
+ * Copyright (c) 2010 Mika Laitio <lamikr@pilppa.org>
+ *
+ * This driver will read and write the value of 4 counters to w1_slave file in
+ * sys filesystem.
+ * Inspired by the w1_therm and w1_ds2431 drivers.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the therms 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
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/device.h>
+#include <linux/types.h>
+#include <linux/delay.h>
+#include <linux/crc16.h>
+
+#include "../w1.h"
+#include "../w1_int.h"
+#include "../w1_family.h"
+
+#define CRC16_VALID 0xb001
+#define CRC16_INIT 0
+
+#define COUNTER_COUNT 4
+#define READ_BYTE_COUNT 42
+
+static ssize_t w1_counter_read(struct device *device,
+ struct device_attribute *attr, char *buf);
+
+static struct device_attribute w1_counter_attr =
+ __ATTR(w1_slave, S_IRUGO, w1_counter_read, NULL);
+
+static ssize_t w1_counter_read(struct device *device,
+ struct device_attribute *attr, char *out_buf)
+{
+ struct w1_slave *sl = dev_to_w1_slave(device);
+ struct w1_master *dev = sl->master;
+ u8 rbuf[COUNTER_COUNT * READ_BYTE_COUNT];
+ u8 wrbuf[3];
+ int rom_addr;
+ int read_byte_count;
+ int result;
+ ssize_t c;
+ int ii;
+ int p;
+ int crc;
+
+ c = PAGE_SIZE;
+ rom_addr = (12 << 5) + 31;
+ wrbuf[0] = 0xA5;
+ wrbuf[1] = rom_addr & 0xFF;
+ wrbuf[2] = rom_addr >> 8;
+ mutex_lock(&dev->mutex);
+ if (!w1_reset_select_slave(sl)) {
+ w1_write_block(dev, wrbuf, 3);
+ read_byte_count = 0;
+ for (p = 0; p < 4; p++) {
+ /*
+ * 1 byte for first bytes in ram page read
+ * 4 bytes for counter
+ * 4 bytes for zero bits
+ * 2 bytes for crc
+ * 31 remaining bytes from the ram page
+ */
+ read_byte_count += w1_read_block(dev,
+ rbuf + (p * READ_BYTE_COUNT), READ_BYTE_COUNT);
+ for (ii = 0; ii < READ_BYTE_COUNT; ++ii)
+ c -= snprintf(out_buf + PAGE_SIZE - c,
+ c, "%02x ",
+ rbuf[(p * READ_BYTE_COUNT) + ii]);
+ if (read_byte_count != (p + 1) * READ_BYTE_COUNT) {
+ dev_warn(device,
+ "w1_counter_read() returned %u bytes "
+ "instead of %d bytes wanted.\n",
+ read_byte_count,
+ READ_BYTE_COUNT);
+ c -= snprintf(out_buf + PAGE_SIZE - c,
+ c, "crc=NO\n");
+ } else {
+ if (p == 0) {
+ crc = crc16(CRC16_INIT, wrbuf, 3);
+ crc = crc16(crc, rbuf, 11);
+ } else {
+ /*
+ * DS2423 calculates crc from all bytes
+ * read after the previous crc bytes.
+ */
+ crc = crc16(CRC16_INIT,
+ (rbuf + 11) +
+ ((p - 1) * READ_BYTE_COUNT),
+ READ_BYTE_COUNT);
+ }
+ if (crc == CRC16_VALID) {
+ result = 0;
+ for (ii = 4; ii > 0; ii--) {
+ result <<= 8;
+ result |= rbuf[(p *
+ READ_BYTE_COUNT) + ii];
+ }
+ c -= snprintf(out_buf + PAGE_SIZE - c,
+ c, "crc=YES c=%d\n", result);
+ } else {
+ c -= snprintf(out_buf + PAGE_SIZE - c,
+ c, "crc=NO\n");
+ }
+ }
+ }
+ } else {
+ c -= snprintf(out_buf + PAGE_SIZE - c, c, "Connection error");
+ }
+ mutex_unlock(&dev->mutex);
+ return PAGE_SIZE - c;
+}
+
+static int w1_f1d_add_slave(struct w1_slave *sl)
+{
+ return device_create_file(&sl->dev, &w1_counter_attr);
+}
+
+static void w1_f1d_remove_slave(struct w1_slave *sl)
+{
+ device_remove_file(&sl->dev, &w1_counter_attr);
+}
+
+static struct w1_family_ops w1_f1d_fops = {
+ .add_slave = w1_f1d_add_slave,
+ .remove_slave = w1_f1d_remove_slave,
+};
+
+static struct w1_family w1_family_1d = {
+ .fid = W1_COUNTER_DS2423,
+ .fops = &w1_f1d_fops,
+};
+
+static int __init w1_f1d_init(void)
+{
+ return w1_register_family(&w1_family_1d);
+}
+
+static void __exit w1_f1d_exit(void)
+{
+ w1_unregister_family(&w1_family_1d);
+}
+
+module_init(w1_f1d_init);
+module_exit(w1_f1d_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Mika Laitio <lamikr@pilppa.org>");
+MODULE_DESCRIPTION("w1 family 1d driver for DS2423, 4 counters and 4kb ram");
diff --git a/drivers/w1/w1_family.h b/drivers/w1/w1_family.h
index 3ca1b92..f3b636d 100644
--- a/drivers/w1/w1_family.h
+++ b/drivers/w1/w1_family.h
@@ -30,6 +30,7 @@
#define W1_FAMILY_SMEM_01 0x01
#define W1_FAMILY_SMEM_81 0x81
#define W1_THERM_DS18S20 0x10
+#define W1_COUNTER_DS2423 0x1D
#define W1_THERM_DS1822 0x22
#define W1_EEPROM_DS2433 0x23
#define W1_THERM_DS18B20 0x28
diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig
index a5ad77e..2e2400e 100644
--- a/drivers/watchdog/Kconfig
+++ b/drivers/watchdog/Kconfig
@@ -409,15 +409,26 @@
Most people will say N.
config F71808E_WDT
- tristate "Fintek F71808E, F71882FG and F71889FG Watchdog"
+ tristate "Fintek F71808E, F71862FG, F71869, F71882FG and F71889FG Watchdog"
depends on X86 && EXPERIMENTAL
help
This is the driver for the hardware watchdog on the Fintek
- F71808E, F71882FG and F71889FG Super I/O controllers.
+ F71808E, F71862FG, F71869, F71882FG and F71889FG Super I/O controllers.
You can compile this driver directly into the kernel, or use
it as a module. The module will be called f71808e_wdt.
+config SP5100_TCO
+ tristate "AMD/ATI SP5100 TCO Timer/Watchdog"
+ depends on X86 && PCI
+ ---help---
+ Hardware watchdog driver for the AMD/ATI SP5100 chipset. The TCO
+ (Total Cost of Ownership) timer is a watchdog timer that will reboot
+ the machine after its expiration. The expiration time can be
+ configured with the "heartbeat" parameter.
+
+ To compile this driver as a module, choose M here: the
+ module will be called sp5100_tco.
config GEODE_WDT
tristate "AMD Geode CS5535/CS5536 Watchdog"
@@ -631,6 +642,24 @@
Most people will say N.
+config NV_TCO
+ tristate "nVidia TCO Timer/Watchdog"
+ depends on X86 && PCI
+ ---help---
+ Hardware driver for the TCO timer built into the nVidia Hub family
+ (such as the MCP51). The TCO (Total Cost of Ownership) timer is a
+ watchdog timer that will reboot the machine after its second
+ expiration. The expiration time can be configured with the
+ "heartbeat" parameter.
+
+ On some motherboards the driver may fail to reset the chipset's
+ NO_REBOOT flag which prevents the watchdog from rebooting the
+ machine. If this is the case you will get a kernel message like
+ "failed to reset NO_REBOOT flag, reboot disabled by hardware".
+
+ To compile this driver as a module, choose M here: the
+ module will be called nv_tco.
+
config RDC321X_WDT
tristate "RDC R-321x SoC watchdog"
depends on X86_RDC321X
@@ -722,14 +751,15 @@
Most people will say N.
config W83627HF_WDT
- tristate "W83627HF Watchdog Timer"
+ tristate "W83627HF/W83627DHG Watchdog Timer"
depends on X86
---help---
This is the driver for the hardware watchdog on the W83627HF chipset
as used in Advantech PC-9578 and Tyan S2721-533 motherboards
- (and likely others). This watchdog simply watches your kernel to
- make sure it doesn't freeze, and if it does, it reboots your computer
- after a certain amount of time.
+ (and likely others). The driver also supports the W83627DHG chip.
+ This watchdog simply watches your kernel to make sure it doesn't
+ freeze, and if it does, it reboots your computer after a certain
+ amount of time.
To compile this driver as a module, choose M here: the
module will be called w83627hf_wdt.
@@ -832,10 +862,22 @@
# M68K Architecture
-# M68KNOMMU Architecture
+config M548x_WATCHDOG
+ tristate "MCF548x watchdog support"
+ depends on M548x
+ help
+ To compile this driver as a module, choose M here: the
+ module will be called m548x_wdt.
# MIPS Architecture
+config ATH79_WDT
+ tristate "Atheros AR71XX/AR724X/AR913X hardware watchdog"
+ depends on ATH79
+ help
+ Hardware driver for the built-in watchdog timer on the Atheros
+ AR71XX/AR724X/AR913X SoCs.
+
config BCM47XX_WDT
tristate "Broadcom BCM47xx Watchdog Timer"
depends on BCM47XX
diff --git a/drivers/watchdog/Makefile b/drivers/watchdog/Makefile
index 4b0ef38..dd77665 100644
--- a/drivers/watchdog/Makefile
+++ b/drivers/watchdog/Makefile
@@ -68,6 +68,7 @@
obj-$(CONFIG_ALIM1535_WDT) += alim1535_wdt.o
obj-$(CONFIG_ALIM7101_WDT) += alim7101_wdt.o
obj-$(CONFIG_F71808E_WDT) += f71808e_wdt.o
+obj-$(CONFIG_SP5100_TCO) += sp5100_tco.o
obj-$(CONFIG_GEODE_WDT) += geodewdt.o
obj-$(CONFIG_SC520_WDT) += sc520_wdt.o
obj-$(CONFIG_SBC_FITPC2_WATCHDOG) += sbc_fitpc2_wdt.o
@@ -86,6 +87,7 @@
obj-$(CONFIG_SC1200_WDT) += sc1200wdt.o
obj-$(CONFIG_SCx200_WDT) += scx200_wdt.o
obj-$(CONFIG_PC87413_WDT) += pc87413_wdt.o
+obj-$(CONFIG_NV_TCO) += nv_tco.o
obj-$(CONFIG_RDC321X_WDT) += rdc321x_wdt.o
obj-$(CONFIG_60XX_WDT) += sbc60xxwdt.o
obj-$(CONFIG_SBC8360_WDT) += sbc8360.o
@@ -104,10 +106,10 @@
# M32R Architecture
# M68K Architecture
-
-# M68KNOMMU Architecture
+obj-$(CONFIG_M548x_WATCHDOG) += m548x_wdt.o
# MIPS Architecture
+obj-$(CONFIG_ATH79_WDT) += ath79_wdt.o
obj-$(CONFIG_BCM47XX_WDT) += bcm47xx_wdt.o
obj-$(CONFIG_BCM63XX_WDT) += bcm63xx_wdt.o
obj-$(CONFIG_RC32434_WDT) += rc32434_wdt.o
diff --git a/drivers/watchdog/alim1535_wdt.c b/drivers/watchdog/alim1535_wdt.c
index 1e9caea..fa4d360 100644
--- a/drivers/watchdog/alim1535_wdt.c
+++ b/drivers/watchdog/alim1535_wdt.c
@@ -301,7 +301,7 @@
* want to register another driver on the same PCI id.
*/
-static struct pci_device_id ali_pci_tbl[] = {
+static struct pci_device_id ali_pci_tbl[] __used = {
{ PCI_VENDOR_ID_AL, 0x1533, PCI_ANY_ID, PCI_ANY_ID,},
{ PCI_VENDOR_ID_AL, 0x1535, PCI_ANY_ID, PCI_ANY_ID,},
{ 0, },
diff --git a/drivers/watchdog/alim7101_wdt.c b/drivers/watchdog/alim7101_wdt.c
index d8d4da9..4b7a2b4 100644
--- a/drivers/watchdog/alim7101_wdt.c
+++ b/drivers/watchdog/alim7101_wdt.c
@@ -430,7 +430,7 @@
module_init(alim7101_wdt_init);
module_exit(alim7101_wdt_unload);
-static struct pci_device_id alim7101_pci_tbl[] __devinitdata = {
+static struct pci_device_id alim7101_pci_tbl[] __devinitdata __used = {
{ PCI_DEVICE(PCI_VENDOR_ID_AL, PCI_DEVICE_ID_AL_M1533) },
{ PCI_DEVICE(PCI_VENDOR_ID_AL, PCI_DEVICE_ID_AL_M7101) },
{ }
diff --git a/drivers/watchdog/ath79_wdt.c b/drivers/watchdog/ath79_wdt.c
new file mode 100644
index 0000000..725c84b
--- /dev/null
+++ b/drivers/watchdog/ath79_wdt.c
@@ -0,0 +1,305 @@
+/*
+ * Atheros AR71XX/AR724X/AR913X built-in hardware watchdog timer.
+ *
+ * Copyright (C) 2008-2011 Gabor Juhos <juhosg@openwrt.org>
+ * Copyright (C) 2008 Imre Kaloz <kaloz@openwrt.org>
+ *
+ * This driver was based on: drivers/watchdog/ixp4xx_wdt.c
+ * Author: Deepak Saxena <dsaxena@plexity.net>
+ * Copyright 2004 (c) MontaVista, Software, Inc.
+ *
+ * which again was based on sa1100 driver,
+ * Copyright (C) 2000 Oleg Drokin <green@crimea.edu>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published
+ * by the Free Software Foundation.
+ *
+ */
+
+#include <linux/bitops.h>
+#include <linux/errno.h>
+#include <linux/fs.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/miscdevice.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/platform_device.h>
+#include <linux/types.h>
+#include <linux/watchdog.h>
+#include <linux/clk.h>
+#include <linux/err.h>
+
+#include <asm/mach-ath79/ath79.h>
+#include <asm/mach-ath79/ar71xx_regs.h>
+
+#define DRIVER_NAME "ath79-wdt"
+
+#define WDT_TIMEOUT 15 /* seconds */
+
+#define WDOG_CTRL_LAST_RESET BIT(31)
+#define WDOG_CTRL_ACTION_MASK 3
+#define WDOG_CTRL_ACTION_NONE 0 /* no action */
+#define WDOG_CTRL_ACTION_GPI 1 /* general purpose interrupt */
+#define WDOG_CTRL_ACTION_NMI 2 /* NMI */
+#define WDOG_CTRL_ACTION_FCR 3 /* full chip reset */
+
+static int nowayout = WATCHDOG_NOWAYOUT;
+module_param(nowayout, int, 0);
+MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started "
+ "(default=" __MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
+
+static int timeout = WDT_TIMEOUT;
+module_param(timeout, int, 0);
+MODULE_PARM_DESC(timeout, "Watchdog timeout in seconds "
+ "(default=" __MODULE_STRING(WDT_TIMEOUT) "s)");
+
+static unsigned long wdt_flags;
+
+#define WDT_FLAGS_BUSY 0
+#define WDT_FLAGS_EXPECT_CLOSE 1
+
+static struct clk *wdt_clk;
+static unsigned long wdt_freq;
+static int boot_status;
+static int max_timeout;
+
+static inline void ath79_wdt_keepalive(void)
+{
+ ath79_reset_wr(AR71XX_RESET_REG_WDOG, wdt_freq * timeout);
+}
+
+static inline void ath79_wdt_enable(void)
+{
+ ath79_wdt_keepalive();
+ ath79_reset_wr(AR71XX_RESET_REG_WDOG_CTRL, WDOG_CTRL_ACTION_FCR);
+}
+
+static inline void ath79_wdt_disable(void)
+{
+ ath79_reset_wr(AR71XX_RESET_REG_WDOG_CTRL, WDOG_CTRL_ACTION_NONE);
+}
+
+static int ath79_wdt_set_timeout(int val)
+{
+ if (val < 1 || val > max_timeout)
+ return -EINVAL;
+
+ timeout = val;
+ ath79_wdt_keepalive();
+
+ return 0;
+}
+
+static int ath79_wdt_open(struct inode *inode, struct file *file)
+{
+ if (test_and_set_bit(WDT_FLAGS_BUSY, &wdt_flags))
+ return -EBUSY;
+
+ clear_bit(WDT_FLAGS_EXPECT_CLOSE, &wdt_flags);
+ ath79_wdt_enable();
+
+ return nonseekable_open(inode, file);
+}
+
+static int ath79_wdt_release(struct inode *inode, struct file *file)
+{
+ if (test_bit(WDT_FLAGS_EXPECT_CLOSE, &wdt_flags))
+ ath79_wdt_disable();
+ else {
+ pr_crit(DRIVER_NAME ": device closed unexpectedly, "
+ "watchdog timer will not stop!\n");
+ ath79_wdt_keepalive();
+ }
+
+ clear_bit(WDT_FLAGS_BUSY, &wdt_flags);
+ clear_bit(WDT_FLAGS_EXPECT_CLOSE, &wdt_flags);
+
+ return 0;
+}
+
+static ssize_t ath79_wdt_write(struct file *file, const char *data,
+ size_t len, loff_t *ppos)
+{
+ if (len) {
+ if (!nowayout) {
+ size_t i;
+
+ clear_bit(WDT_FLAGS_EXPECT_CLOSE, &wdt_flags);
+
+ for (i = 0; i != len; i++) {
+ char c;
+
+ if (get_user(c, data + i))
+ return -EFAULT;
+
+ if (c == 'V')
+ set_bit(WDT_FLAGS_EXPECT_CLOSE,
+ &wdt_flags);
+ }
+ }
+
+ ath79_wdt_keepalive();
+ }
+
+ return len;
+}
+
+static const struct watchdog_info ath79_wdt_info = {
+ .options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING |
+ WDIOF_MAGICCLOSE | WDIOF_CARDRESET,
+ .firmware_version = 0,
+ .identity = "ATH79 watchdog",
+};
+
+static long ath79_wdt_ioctl(struct file *file, unsigned int cmd,
+ unsigned long arg)
+{
+ void __user *argp = (void __user *)arg;
+ int __user *p = argp;
+ int err;
+ int t;
+
+ switch (cmd) {
+ case WDIOC_GETSUPPORT:
+ err = copy_to_user(argp, &ath79_wdt_info,
+ sizeof(ath79_wdt_info)) ? -EFAULT : 0;
+ break;
+
+ case WDIOC_GETSTATUS:
+ err = put_user(0, p);
+ break;
+
+ case WDIOC_GETBOOTSTATUS:
+ err = put_user(boot_status, p);
+ break;
+
+ case WDIOC_KEEPALIVE:
+ ath79_wdt_keepalive();
+ err = 0;
+ break;
+
+ case WDIOC_SETTIMEOUT:
+ err = get_user(t, p);
+ if (err)
+ break;
+
+ err = ath79_wdt_set_timeout(t);
+ if (err)
+ break;
+
+ /* fallthrough */
+ case WDIOC_GETTIMEOUT:
+ err = put_user(timeout, p);
+ break;
+
+ default:
+ err = -ENOTTY;
+ break;
+ }
+
+ return err;
+}
+
+static const struct file_operations ath79_wdt_fops = {
+ .owner = THIS_MODULE,
+ .llseek = no_llseek,
+ .write = ath79_wdt_write,
+ .unlocked_ioctl = ath79_wdt_ioctl,
+ .open = ath79_wdt_open,
+ .release = ath79_wdt_release,
+};
+
+static struct miscdevice ath79_wdt_miscdev = {
+ .minor = WATCHDOG_MINOR,
+ .name = "watchdog",
+ .fops = &ath79_wdt_fops,
+};
+
+static int __devinit ath79_wdt_probe(struct platform_device *pdev)
+{
+ u32 ctrl;
+ int err;
+
+ wdt_clk = clk_get(&pdev->dev, "wdt");
+ if (IS_ERR(wdt_clk))
+ return PTR_ERR(wdt_clk);
+
+ err = clk_enable(wdt_clk);
+ if (err)
+ goto err_clk_put;
+
+ wdt_freq = clk_get_rate(wdt_clk);
+ if (!wdt_freq) {
+ err = -EINVAL;
+ goto err_clk_disable;
+ }
+
+ max_timeout = (0xfffffffful / wdt_freq);
+ if (timeout < 1 || timeout > max_timeout) {
+ timeout = max_timeout;
+ dev_info(&pdev->dev,
+ "timeout value must be 0 < timeout < %d, using %d\n",
+ max_timeout, timeout);
+ }
+
+ ctrl = ath79_reset_rr(AR71XX_RESET_REG_WDOG_CTRL);
+ boot_status = (ctrl & WDOG_CTRL_LAST_RESET) ? WDIOF_CARDRESET : 0;
+
+ err = misc_register(&ath79_wdt_miscdev);
+ if (err) {
+ dev_err(&pdev->dev,
+ "unable to register misc device, err=%d\n", err);
+ goto err_clk_disable;
+ }
+
+ return 0;
+
+err_clk_disable:
+ clk_disable(wdt_clk);
+err_clk_put:
+ clk_put(wdt_clk);
+ return err;
+}
+
+static int __devexit ath79_wdt_remove(struct platform_device *pdev)
+{
+ misc_deregister(&ath79_wdt_miscdev);
+ clk_disable(wdt_clk);
+ clk_put(wdt_clk);
+ return 0;
+}
+
+static void ath97_wdt_shutdown(struct platform_device *pdev)
+{
+ ath79_wdt_disable();
+}
+
+static struct platform_driver ath79_wdt_driver = {
+ .remove = __devexit_p(ath79_wdt_remove),
+ .shutdown = ath97_wdt_shutdown,
+ .driver = {
+ .name = DRIVER_NAME,
+ .owner = THIS_MODULE,
+ },
+};
+
+static int __init ath79_wdt_init(void)
+{
+ return platform_driver_probe(&ath79_wdt_driver, ath79_wdt_probe);
+}
+module_init(ath79_wdt_init);
+
+static void __exit ath79_wdt_exit(void)
+{
+ platform_driver_unregister(&ath79_wdt_driver);
+}
+module_exit(ath79_wdt_exit);
+
+MODULE_DESCRIPTION("Atheros AR71XX/AR724X/AR913X hardware watchdog driver");
+MODULE_AUTHOR("Gabor Juhos <juhosg@openwrt.org");
+MODULE_AUTHOR("Imre Kaloz <kaloz@openwrt.org");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:" DRIVER_NAME);
+MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
diff --git a/drivers/watchdog/booke_wdt.c b/drivers/watchdog/booke_wdt.c
index d11ffb0..7e7ec9c 100644
--- a/drivers/watchdog/booke_wdt.c
+++ b/drivers/watchdog/booke_wdt.c
@@ -85,6 +85,22 @@
return 0;
}
+static void __booke_wdt_set(void *data)
+{
+ u32 val;
+
+ val = mfspr(SPRN_TCR);
+ val &= ~WDTP_MASK;
+ val |= WDTP(booke_wdt_period);
+
+ mtspr(SPRN_TCR, val);
+}
+
+static void booke_wdt_set(void)
+{
+ on_each_cpu(__booke_wdt_set, NULL, 0);
+}
+
static void __booke_wdt_ping(void *data)
{
mtspr(SPRN_TSR, TSR_ENW|TSR_WIS);
@@ -181,8 +197,7 @@
#else
booke_wdt_period = tmp;
#endif
- mtspr(SPRN_TCR, (mfspr(SPRN_TCR) & ~WDTP_MASK) |
- WDTP(booke_wdt_period));
+ booke_wdt_set();
return 0;
case WDIOC_GETTIMEOUT:
return put_user(booke_wdt_period, p);
@@ -193,8 +208,15 @@
return 0;
}
+/* wdt_is_active stores wether or not the /dev/watchdog device is opened */
+static unsigned long wdt_is_active;
+
static int booke_wdt_open(struct inode *inode, struct file *file)
{
+ /* /dev/watchdog can only be opened once */
+ if (test_and_set_bit(0, &wdt_is_active))
+ return -EBUSY;
+
spin_lock(&booke_wdt_lock);
if (booke_wdt_enabled == 0) {
booke_wdt_enabled = 1;
@@ -210,8 +232,17 @@
static int booke_wdt_release(struct inode *inode, struct file *file)
{
+#ifndef CONFIG_WATCHDOG_NOWAYOUT
+ /* Normally, the watchdog is disabled when /dev/watchdog is closed, but
+ * if CONFIG_WATCHDOG_NOWAYOUT is defined, then it means that the
+ * watchdog should remain enabled. So we disable it only if
+ * CONFIG_WATCHDOG_NOWAYOUT is not defined.
+ */
on_each_cpu(__booke_wdt_disable, NULL, 0);
booke_wdt_enabled = 0;
+#endif
+
+ clear_bit(0, &wdt_is_active);
return 0;
}
diff --git a/drivers/watchdog/f71808e_wdt.c b/drivers/watchdog/f71808e_wdt.c
index 65e5796..d4d8d1f 100644
--- a/drivers/watchdog/f71808e_wdt.c
+++ b/drivers/watchdog/f71808e_wdt.c
@@ -42,18 +42,21 @@
#define SIO_REG_DEVID 0x20 /* Device ID (2 bytes) */
#define SIO_REG_DEVREV 0x22 /* Device revision */
#define SIO_REG_MANID 0x23 /* Fintek ID (2 bytes) */
+#define SIO_REG_ROM_ADDR_SEL 0x27 /* ROM address select */
+#define SIO_REG_MFUNCT1 0x29 /* Multi function select 1 */
+#define SIO_REG_MFUNCT2 0x2a /* Multi function select 2 */
+#define SIO_REG_MFUNCT3 0x2b /* Multi function select 3 */
#define SIO_REG_ENABLE 0x30 /* Logical device enable */
#define SIO_REG_ADDR 0x60 /* Logical device address (2 bytes) */
#define SIO_FINTEK_ID 0x1934 /* Manufacturers ID */
-#define SIO_F71808_ID 0x0901 /* Chipset ID */
-#define SIO_F71858_ID 0x0507 /* Chipset ID */
+#define SIO_F71808_ID 0x0901 /* Chipset ID */
+#define SIO_F71858_ID 0x0507 /* Chipset ID */
#define SIO_F71862_ID 0x0601 /* Chipset ID */
+#define SIO_F71869_ID 0x0814 /* Chipset ID */
#define SIO_F71882_ID 0x0541 /* Chipset ID */
#define SIO_F71889_ID 0x0723 /* Chipset ID */
-#define F71882FG_REG_START 0x01
-
#define F71808FG_REG_WDO_CONF 0xf0
#define F71808FG_REG_WDT_CONF 0xf5
#define F71808FG_REG_WD_TIME 0xf6
@@ -70,13 +73,15 @@
#define WATCHDOG_MAX_TIMEOUT (60 * 255)
#define WATCHDOG_PULSE_WIDTH 125 /* 125 ms, default pulse width for
watchdog signal */
+#define WATCHDOG_F71862FG_PIN 63 /* default watchdog reset output
+ pin number 63 */
static unsigned short force_id;
module_param(force_id, ushort, 0);
MODULE_PARM_DESC(force_id, "Override the detected device ID");
static const int max_timeout = WATCHDOG_MAX_TIMEOUT;
-static int timeout = 60; /* default timeout in seconds */
+static int timeout = WATCHDOG_TIMEOUT; /* default timeout in seconds */
module_param(timeout, int, 0);
MODULE_PARM_DESC(timeout,
"Watchdog timeout in seconds. 1<= timeout <="
@@ -89,6 +94,12 @@
"Watchdog signal pulse width. 0(=level), 1 ms, 25 ms, 125 ms or 5000 ms"
" (default=" __MODULE_STRING(WATCHDOG_PULSE_WIDTH) ")");
+static unsigned int f71862fg_pin = WATCHDOG_F71862FG_PIN;
+module_param(f71862fg_pin, uint, 0);
+MODULE_PARM_DESC(f71862fg_pin,
+ "Watchdog f71862fg reset output pin configuration. Choose pin 56 or 63"
+ " (default=" __MODULE_STRING(WATCHDOG_F71862FG_PIN)")");
+
static int nowayout = WATCHDOG_NOWAYOUT;
module_param(nowayout, bool, 0444);
MODULE_PARM_DESC(nowayout, "Disable watchdog shutdown on close");
@@ -98,12 +109,13 @@
MODULE_PARM_DESC(start_withtimeout, "Start watchdog timer on module load with"
" given initial timeout. Zero (default) disables this feature.");
-enum chips { f71808fg, f71858fg, f71862fg, f71882fg, f71889fg };
+enum chips { f71808fg, f71858fg, f71862fg, f71869, f71882fg, f71889fg };
static const char *f71808e_names[] = {
"f71808fg",
"f71858fg",
"f71862fg",
+ "f71869",
"f71882fg",
"f71889fg",
};
@@ -282,6 +294,28 @@
return err;
}
+static int f71862fg_pin_configure(unsigned short ioaddr)
+{
+ /* When ioaddr is non-zero the calling function has to take care of
+ mutex handling and superio preparation! */
+
+ if (f71862fg_pin == 63) {
+ if (ioaddr) {
+ /* SPI must be disabled first to use this pin! */
+ superio_clear_bit(ioaddr, SIO_REG_ROM_ADDR_SEL, 6);
+ superio_set_bit(ioaddr, SIO_REG_MFUNCT3, 4);
+ }
+ } else if (f71862fg_pin == 56) {
+ if (ioaddr)
+ superio_set_bit(ioaddr, SIO_REG_MFUNCT1, 1);
+ } else {
+ printk(KERN_ERR DRVNAME ": Invalid argument f71862fg_pin=%d\n",
+ f71862fg_pin);
+ return -EINVAL;
+ }
+ return 0;
+}
+
static int watchdog_start(void)
{
/* Make sure we don't die as soon as the watchdog is enabled below */
@@ -299,19 +333,30 @@
switch (watchdog.type) {
case f71808fg:
/* Set pin 21 to GPIO23/WDTRST#, then to WDTRST# */
- superio_clear_bit(watchdog.sioaddr, 0x2a, 3);
- superio_clear_bit(watchdog.sioaddr, 0x2b, 3);
+ superio_clear_bit(watchdog.sioaddr, SIO_REG_MFUNCT2, 3);
+ superio_clear_bit(watchdog.sioaddr, SIO_REG_MFUNCT3, 3);
+ break;
+
+ case f71862fg:
+ err = f71862fg_pin_configure(watchdog.sioaddr);
+ if (err)
+ goto exit_superio;
+ break;
+
+ case f71869:
+ /* GPIO14 --> WDTRST# */
+ superio_clear_bit(watchdog.sioaddr, SIO_REG_MFUNCT1, 4);
break;
case f71882fg:
/* Set pin 56 to WDTRST# */
- superio_set_bit(watchdog.sioaddr, 0x29, 1);
+ superio_set_bit(watchdog.sioaddr, SIO_REG_MFUNCT1, 1);
break;
case f71889fg:
/* set pin 40 to WDTRST# */
- superio_outb(watchdog.sioaddr, 0x2b,
- superio_inb(watchdog.sioaddr, 0x2b) & 0xcf);
+ superio_outb(watchdog.sioaddr, SIO_REG_MFUNCT3,
+ superio_inb(watchdog.sioaddr, SIO_REG_MFUNCT3) & 0xcf);
break;
default:
@@ -711,16 +756,19 @@
case SIO_F71808_ID:
watchdog.type = f71808fg;
break;
+ case SIO_F71862_ID:
+ watchdog.type = f71862fg;
+ err = f71862fg_pin_configure(0); /* validate module parameter */
+ break;
+ case SIO_F71869_ID:
+ watchdog.type = f71869;
+ break;
case SIO_F71882_ID:
watchdog.type = f71882fg;
break;
case SIO_F71889_ID:
watchdog.type = f71889fg;
break;
- case SIO_F71862_ID:
- /* These have a watchdog, though it isn't implemented (yet). */
- err = -ENOSYS;
- goto exit;
case SIO_F71858_ID:
/* Confirmed (by datasheet) not to have a watchdog. */
err = -ENODEV;
diff --git a/drivers/watchdog/iTCO_wdt.c b/drivers/watchdog/iTCO_wdt.c
index b8838d2..2c6c2b4 100644
--- a/drivers/watchdog/iTCO_wdt.c
+++ b/drivers/watchdog/iTCO_wdt.c
@@ -1,7 +1,7 @@
/*
* intel TCO Watchdog Driver
*
- * (c) Copyright 2006-2009 Wim Van Sebroeck <wim@iguana.be>.
+ * (c) Copyright 2006-2010 Wim Van Sebroeck <wim@iguana.be>.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@@ -26,13 +26,15 @@
* document number 301473-002, 301474-026: 82801F (ICH6)
* document number 313082-001, 313075-006: 631xESB, 632xESB
* document number 307013-003, 307014-024: 82801G (ICH7)
+ * document number 322896-001, 322897-001: NM10
* document number 313056-003, 313057-017: 82801H (ICH8)
* document number 316972-004, 316973-012: 82801I (ICH9)
* document number 319973-002, 319974-002: 82801J (ICH10)
* document number 322169-001, 322170-003: 5 Series, 3400 Series (PCH)
* document number 320066-003, 320257-008: EP80597 (IICH)
- * document number TBD : Cougar Point (CPT)
+ * document number 324645-001, 324646-001: Cougar Point (CPT)
* document number TBD : Patsburg (PBG)
+ * document number TBD : DH89xxCC
*/
/*
@@ -85,6 +87,7 @@
TCO_ICH7DH, /* ICH7DH */
TCO_ICH7M, /* ICH7-M & ICH7-U */
TCO_ICH7MDH, /* ICH7-M DH */
+ TCO_NM10, /* NM10 */
TCO_ICH8, /* ICH8 & ICH8R */
TCO_ICH8DH, /* ICH8DH */
TCO_ICH8DO, /* ICH8DO */
@@ -149,6 +152,7 @@
TCO_CPT31, /* Cougar Point */
TCO_PBG1, /* Patsburg */
TCO_PBG2, /* Patsburg */
+ TCO_DH89XXCC, /* DH89xxCC */
};
static struct {
@@ -174,6 +178,7 @@
{"ICH7DH", 2},
{"ICH7-M or ICH7-U", 2},
{"ICH7-M DH", 2},
+ {"NM10", 2},
{"ICH8 or ICH8R", 2},
{"ICH8DH", 2},
{"ICH8DO", 2},
@@ -238,6 +243,7 @@
{"Cougar Point", 2},
{"Patsburg", 2},
{"Patsburg", 2},
+ {"DH89xxCC", 2},
{NULL, 0}
};
@@ -291,6 +297,7 @@
{ ITCO_PCI_DEVICE(PCI_DEVICE_ID_INTEL_ICH7_30, TCO_ICH7DH)},
{ ITCO_PCI_DEVICE(PCI_DEVICE_ID_INTEL_ICH7_1, TCO_ICH7M)},
{ ITCO_PCI_DEVICE(PCI_DEVICE_ID_INTEL_ICH7_31, TCO_ICH7MDH)},
+ { ITCO_PCI_DEVICE(0x27bc, TCO_NM10)},
{ ITCO_PCI_DEVICE(PCI_DEVICE_ID_INTEL_ICH8_0, TCO_ICH8)},
{ ITCO_PCI_DEVICE(PCI_DEVICE_ID_INTEL_ICH8_2, TCO_ICH8DH)},
{ ITCO_PCI_DEVICE(PCI_DEVICE_ID_INTEL_ICH8_3, TCO_ICH8DO)},
@@ -355,6 +362,7 @@
{ ITCO_PCI_DEVICE(0x1c5f, TCO_CPT31)},
{ ITCO_PCI_DEVICE(0x1d40, TCO_PBG1)},
{ ITCO_PCI_DEVICE(0x1d41, TCO_PBG2)},
+ { ITCO_PCI_DEVICE(0x2310, TCO_DH89XXCC)},
{ 0, }, /* End of list */
};
MODULE_DEVICE_TABLE(pci, iTCO_wdt_pci_tbl);
diff --git a/drivers/watchdog/ks8695_wdt.c b/drivers/watchdog/ks8695_wdt.c
index 2852bb2..8114719 100644
--- a/drivers/watchdog/ks8695_wdt.c
+++ b/drivers/watchdog/ks8695_wdt.c
@@ -21,7 +21,7 @@
#include <linux/watchdog.h>
#include <linux/io.h>
#include <linux/uaccess.h>
-#include <mach/timex.h>
+#include <mach/hardware.h>
#include <mach/regs-timer.h>
#define WDT_DEFAULT_TIME 5 /* seconds */
diff --git a/drivers/watchdog/m548x_wdt.c b/drivers/watchdog/m548x_wdt.c
new file mode 100644
index 0000000..cabbcfe
--- /dev/null
+++ b/drivers/watchdog/m548x_wdt.c
@@ -0,0 +1,227 @@
+/*
+ * drivers/watchdog/m548x_wdt.c
+ *
+ * Watchdog driver for ColdFire MCF548x processors
+ * Copyright 2010 (c) Philippe De Muyter <phdm@macqel.be>
+ *
+ * Adapted from the IXP4xx watchdog driver, which carries these notices:
+ *
+ * Author: Deepak Saxena <dsaxena@plexity.net>
+ *
+ * Copyright 2004 (c) MontaVista, Software, Inc.
+ * Based on sa1100 driver, Copyright (C) 2000 Oleg Drokin <green@crimea.edu>
+ *
+ * This file is licensed under the terms of the GNU General Public
+ * License version 2. This program is licensed "as is" without any
+ * warranty of any kind, whether express or implied.
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/fs.h>
+#include <linux/miscdevice.h>
+#include <linux/watchdog.h>
+#include <linux/init.h>
+#include <linux/bitops.h>
+#include <linux/ioport.h>
+#include <linux/uaccess.h>
+
+#include <asm/coldfire.h>
+#include <asm/m548xsim.h>
+#include <asm/m548xgpt.h>
+
+static int nowayout = WATCHDOG_NOWAYOUT;
+static unsigned int heartbeat = 30; /* (secs) Default is 0.5 minute */
+static unsigned long wdt_status;
+
+#define WDT_IN_USE 0
+#define WDT_OK_TO_CLOSE 1
+
+static void wdt_enable(void)
+{
+ unsigned int gms0;
+
+ /* preserve GPIO usage, if any */
+ gms0 = __raw_readl(MCF_MBAR + MCF_GPT_GMS0);
+ if (gms0 & MCF_GPT_GMS_TMS_GPIO)
+ gms0 &= (MCF_GPT_GMS_TMS_GPIO | MCF_GPT_GMS_GPIO_MASK
+ | MCF_GPT_GMS_OD);
+ else
+ gms0 = MCF_GPT_GMS_TMS_GPIO | MCF_GPT_GMS_OD;
+ __raw_writel(gms0, MCF_MBAR + MCF_GPT_GMS0);
+ __raw_writel(MCF_GPT_GCIR_PRE(heartbeat*(MCF_BUSCLK/0xffff)) |
+ MCF_GPT_GCIR_CNT(0xffff), MCF_MBAR + MCF_GPT_GCIR0);
+ gms0 |= MCF_GPT_GMS_OCPW(0xA5) | MCF_GPT_GMS_WDEN | MCF_GPT_GMS_CE;
+ __raw_writel(gms0, MCF_MBAR + MCF_GPT_GMS0);
+}
+
+static void wdt_disable(void)
+{
+ unsigned int gms0;
+
+ /* disable watchdog */
+ gms0 = __raw_readl(MCF_MBAR + MCF_GPT_GMS0);
+ gms0 &= ~(MCF_GPT_GMS_WDEN | MCF_GPT_GMS_CE);
+ __raw_writel(gms0, MCF_MBAR + MCF_GPT_GMS0);
+}
+
+static void wdt_keepalive(void)
+{
+ unsigned int gms0;
+
+ gms0 = __raw_readl(MCF_MBAR + MCF_GPT_GMS0);
+ gms0 |= MCF_GPT_GMS_OCPW(0xA5);
+ __raw_writel(gms0, MCF_MBAR + MCF_GPT_GMS0);
+}
+
+static int m548x_wdt_open(struct inode *inode, struct file *file)
+{
+ if (test_and_set_bit(WDT_IN_USE, &wdt_status))
+ return -EBUSY;
+
+ clear_bit(WDT_OK_TO_CLOSE, &wdt_status);
+ wdt_enable();
+ return nonseekable_open(inode, file);
+}
+
+static ssize_t m548x_wdt_write(struct file *file, const char *data,
+ size_t len, loff_t *ppos)
+{
+ if (len) {
+ if (!nowayout) {
+ size_t i;
+
+ clear_bit(WDT_OK_TO_CLOSE, &wdt_status);
+
+ for (i = 0; i != len; i++) {
+ char c;
+
+ if (get_user(c, data + i))
+ return -EFAULT;
+ if (c == 'V')
+ set_bit(WDT_OK_TO_CLOSE, &wdt_status);
+ }
+ }
+ wdt_keepalive();
+ }
+ return len;
+}
+
+static const struct watchdog_info ident = {
+ .options = WDIOF_MAGICCLOSE | WDIOF_SETTIMEOUT |
+ WDIOF_KEEPALIVEPING,
+ .identity = "Coldfire M548x Watchdog",
+};
+
+static long m548x_wdt_ioctl(struct file *file, unsigned int cmd,
+ unsigned long arg)
+{
+ int ret = -ENOTTY;
+ int time;
+
+ switch (cmd) {
+ case WDIOC_GETSUPPORT:
+ ret = copy_to_user((struct watchdog_info *)arg, &ident,
+ sizeof(ident)) ? -EFAULT : 0;
+ break;
+
+ case WDIOC_GETSTATUS:
+ ret = put_user(0, (int *)arg);
+ break;
+
+ case WDIOC_GETBOOTSTATUS:
+ ret = put_user(0, (int *)arg);
+ break;
+
+ case WDIOC_KEEPALIVE:
+ wdt_keepalive();
+ ret = 0;
+ break;
+
+ case WDIOC_SETTIMEOUT:
+ ret = get_user(time, (int *)arg);
+ if (ret)
+ break;
+
+ if (time <= 0 || time > 30) {
+ ret = -EINVAL;
+ break;
+ }
+
+ heartbeat = time;
+ wdt_enable();
+ /* Fall through */
+
+ case WDIOC_GETTIMEOUT:
+ ret = put_user(heartbeat, (int *)arg);
+ break;
+ }
+ return ret;
+}
+
+static int m548x_wdt_release(struct inode *inode, struct file *file)
+{
+ if (test_bit(WDT_OK_TO_CLOSE, &wdt_status))
+ wdt_disable();
+ else {
+ printk(KERN_CRIT "WATCHDOG: Device closed unexpectedly - "
+ "timer will not stop\n");
+ wdt_keepalive();
+ }
+ clear_bit(WDT_IN_USE, &wdt_status);
+ clear_bit(WDT_OK_TO_CLOSE, &wdt_status);
+
+ return 0;
+}
+
+
+static const struct file_operations m548x_wdt_fops = {
+ .owner = THIS_MODULE,
+ .llseek = no_llseek,
+ .write = m548x_wdt_write,
+ .unlocked_ioctl = m548x_wdt_ioctl,
+ .open = m548x_wdt_open,
+ .release = m548x_wdt_release,
+};
+
+static struct miscdevice m548x_wdt_miscdev = {
+ .minor = WATCHDOG_MINOR,
+ .name = "watchdog",
+ .fops = &m548x_wdt_fops,
+};
+
+static int __init m548x_wdt_init(void)
+{
+ if (!request_mem_region(MCF_MBAR + MCF_GPT_GCIR0, 4,
+ "Coldfire M548x Watchdog")) {
+ printk(KERN_WARNING
+ "Coldfire M548x Watchdog : I/O region busy\n");
+ return -EBUSY;
+ }
+ printk(KERN_INFO "ColdFire watchdog driver is loaded.\n");
+
+ return misc_register(&m548x_wdt_miscdev);
+}
+
+static void __exit m548x_wdt_exit(void)
+{
+ misc_deregister(&m548x_wdt_miscdev);
+ release_mem_region(MCF_MBAR + MCF_GPT_GCIR0, 4);
+}
+
+module_init(m548x_wdt_init);
+module_exit(m548x_wdt_exit);
+
+MODULE_AUTHOR("Philippe De Muyter <phdm@macqel.be>");
+MODULE_DESCRIPTION("Coldfire M548x Watchdog");
+
+module_param(heartbeat, int, 0);
+MODULE_PARM_DESC(heartbeat, "Watchdog heartbeat in seconds (default 30s)");
+
+module_param(nowayout, int, 0);
+MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started");
+
+MODULE_LICENSE("GPL");
+MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
diff --git a/drivers/watchdog/nv_tco.c b/drivers/watchdog/nv_tco.c
new file mode 100644
index 0000000..1a50aa7
--- /dev/null
+++ b/drivers/watchdog/nv_tco.c
@@ -0,0 +1,512 @@
+/*
+ * nv_tco 0.01: TCO timer driver for NV chipsets
+ *
+ * (c) Copyright 2005 Google Inc., All Rights Reserved.
+ *
+ * Based off i8xx_tco.c:
+ * (c) Copyright 2000 kernel concepts <nils@kernelconcepts.de>, All Rights
+ * Reserved.
+ * http://www.kernelconcepts.de
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ *
+ * TCO timer driver for NV chipsets
+ * based on softdog.c by Alan Cox <alan@redhat.com>
+ */
+
+/*
+ * Includes, defines, variables, module parameters, ...
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/types.h>
+#include <linux/miscdevice.h>
+#include <linux/watchdog.h>
+#include <linux/init.h>
+#include <linux/fs.h>
+#include <linux/pci.h>
+#include <linux/ioport.h>
+#include <linux/jiffies.h>
+#include <linux/platform_device.h>
+#include <linux/uaccess.h>
+#include <linux/io.h>
+
+#include "nv_tco.h"
+
+/* Module and version information */
+#define TCO_VERSION "0.01"
+#define TCO_MODULE_NAME "NV_TCO"
+#define TCO_DRIVER_NAME TCO_MODULE_NAME ", v" TCO_VERSION
+#define PFX TCO_MODULE_NAME ": "
+
+/* internal variables */
+static unsigned int tcobase;
+static DEFINE_SPINLOCK(tco_lock); /* Guards the hardware */
+static unsigned long timer_alive;
+static char tco_expect_close;
+static struct pci_dev *tco_pci;
+
+/* the watchdog platform device */
+static struct platform_device *nv_tco_platform_device;
+
+/* module parameters */
+#define WATCHDOG_HEARTBEAT 30 /* 30 sec default heartbeat (2<heartbeat<39) */
+static int heartbeat = WATCHDOG_HEARTBEAT; /* in seconds */
+module_param(heartbeat, int, 0);
+MODULE_PARM_DESC(heartbeat, "Watchdog heartbeat in seconds. (2<heartbeat<39, "
+ "default=" __MODULE_STRING(WATCHDOG_HEARTBEAT) ")");
+
+static int nowayout = WATCHDOG_NOWAYOUT;
+module_param(nowayout, int, 0);
+MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started"
+ " (default=" __MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
+
+/*
+ * Some TCO specific functions
+ */
+static inline unsigned char seconds_to_ticks(int seconds)
+{
+ /* the internal timer is stored as ticks which decrement
+ * every 0.6 seconds */
+ return (seconds * 10) / 6;
+}
+
+static void tco_timer_start(void)
+{
+ u32 val;
+ unsigned long flags;
+
+ spin_lock_irqsave(&tco_lock, flags);
+ val = inl(TCO_CNT(tcobase));
+ val &= ~TCO_CNT_TCOHALT;
+ outl(val, TCO_CNT(tcobase));
+ spin_unlock_irqrestore(&tco_lock, flags);
+}
+
+static void tco_timer_stop(void)
+{
+ u32 val;
+ unsigned long flags;
+
+ spin_lock_irqsave(&tco_lock, flags);
+ val = inl(TCO_CNT(tcobase));
+ val |= TCO_CNT_TCOHALT;
+ outl(val, TCO_CNT(tcobase));
+ spin_unlock_irqrestore(&tco_lock, flags);
+}
+
+static void tco_timer_keepalive(void)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&tco_lock, flags);
+ outb(0x01, TCO_RLD(tcobase));
+ spin_unlock_irqrestore(&tco_lock, flags);
+}
+
+static int tco_timer_set_heartbeat(int t)
+{
+ int ret = 0;
+ unsigned char tmrval;
+ unsigned long flags;
+ u8 val;
+
+ /*
+ * note seconds_to_ticks(t) > t, so if t > 0x3f, so is
+ * tmrval=seconds_to_ticks(t). Check that the count in seconds isn't
+ * out of range on it's own (to avoid overflow in tmrval).
+ */
+ if (t < 0 || t > 0x3f)
+ return -EINVAL;
+ tmrval = seconds_to_ticks(t);
+
+ /* "Values of 0h-3h are ignored and should not be attempted" */
+ if (tmrval > 0x3f || tmrval < 0x04)
+ return -EINVAL;
+
+ /* Write new heartbeat to watchdog */
+ spin_lock_irqsave(&tco_lock, flags);
+ val = inb(TCO_TMR(tcobase));
+ val &= 0xc0;
+ val |= tmrval;
+ outb(val, TCO_TMR(tcobase));
+ val = inb(TCO_TMR(tcobase));
+
+ if ((val & 0x3f) != tmrval)
+ ret = -EINVAL;
+ spin_unlock_irqrestore(&tco_lock, flags);
+
+ if (ret)
+ return ret;
+
+ heartbeat = t;
+ return 0;
+}
+
+/*
+ * /dev/watchdog handling
+ */
+
+static int nv_tco_open(struct inode *inode, struct file *file)
+{
+ /* /dev/watchdog can only be opened once */
+ if (test_and_set_bit(0, &timer_alive))
+ return -EBUSY;
+
+ /* Reload and activate timer */
+ tco_timer_keepalive();
+ tco_timer_start();
+ return nonseekable_open(inode, file);
+}
+
+static int nv_tco_release(struct inode *inode, struct file *file)
+{
+ /* Shut off the timer */
+ if (tco_expect_close == 42) {
+ tco_timer_stop();
+ } else {
+ printk(KERN_CRIT PFX "Unexpected close, not stopping "
+ "watchdog!\n");
+ tco_timer_keepalive();
+ }
+ clear_bit(0, &timer_alive);
+ tco_expect_close = 0;
+ return 0;
+}
+
+static ssize_t nv_tco_write(struct file *file, const char __user *data,
+ size_t len, loff_t *ppos)
+{
+ /* See if we got the magic character 'V' and reload the timer */
+ if (len) {
+ if (!nowayout) {
+ size_t i;
+
+ /*
+ * note: just in case someone wrote the magic character
+ * five months ago...
+ */
+ tco_expect_close = 0;
+
+ /*
+ * scan to see whether or not we got the magic
+ * character
+ */
+ for (i = 0; i != len; i++) {
+ char c;
+ if (get_user(c, data + i))
+ return -EFAULT;
+ if (c == 'V')
+ tco_expect_close = 42;
+ }
+ }
+
+ /* someone wrote to us, we should reload the timer */
+ tco_timer_keepalive();
+ }
+ return len;
+}
+
+static long nv_tco_ioctl(struct file *file, unsigned int cmd,
+ unsigned long arg)
+{
+ int new_options, retval = -EINVAL;
+ int new_heartbeat;
+ void __user *argp = (void __user *)arg;
+ int __user *p = argp;
+ static const struct watchdog_info ident = {
+ .options = WDIOF_SETTIMEOUT |
+ WDIOF_KEEPALIVEPING |
+ WDIOF_MAGICCLOSE,
+ .firmware_version = 0,
+ .identity = TCO_MODULE_NAME,
+ };
+
+ switch (cmd) {
+ case WDIOC_GETSUPPORT:
+ return copy_to_user(argp, &ident, sizeof(ident)) ? -EFAULT : 0;
+ case WDIOC_GETSTATUS:
+ case WDIOC_GETBOOTSTATUS:
+ return put_user(0, p);
+ case WDIOC_SETOPTIONS:
+ if (get_user(new_options, p))
+ return -EFAULT;
+ if (new_options & WDIOS_DISABLECARD) {
+ tco_timer_stop();
+ retval = 0;
+ }
+ if (new_options & WDIOS_ENABLECARD) {
+ tco_timer_keepalive();
+ tco_timer_start();
+ retval = 0;
+ }
+ return retval;
+ case WDIOC_KEEPALIVE:
+ tco_timer_keepalive();
+ return 0;
+ case WDIOC_SETTIMEOUT:
+ if (get_user(new_heartbeat, p))
+ return -EFAULT;
+ if (tco_timer_set_heartbeat(new_heartbeat))
+ return -EINVAL;
+ tco_timer_keepalive();
+ /* Fall through */
+ case WDIOC_GETTIMEOUT:
+ return put_user(heartbeat, p);
+ default:
+ return -ENOTTY;
+ }
+}
+
+/*
+ * Kernel Interfaces
+ */
+
+static const struct file_operations nv_tco_fops = {
+ .owner = THIS_MODULE,
+ .llseek = no_llseek,
+ .write = nv_tco_write,
+ .unlocked_ioctl = nv_tco_ioctl,
+ .open = nv_tco_open,
+ .release = nv_tco_release,
+};
+
+static struct miscdevice nv_tco_miscdev = {
+ .minor = WATCHDOG_MINOR,
+ .name = "watchdog",
+ .fops = &nv_tco_fops,
+};
+
+/*
+ * Data for PCI driver interface
+ *
+ * This data only exists for exporting the supported
+ * PCI ids via MODULE_DEVICE_TABLE. We do not actually
+ * register a pci_driver, because someone else might one day
+ * want to register another driver on the same PCI id.
+ */
+static struct pci_device_id tco_pci_tbl[] = {
+ { PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE_MCP51_SMBUS,
+ PCI_ANY_ID, PCI_ANY_ID, },
+ { PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE_MCP55_SMBUS,
+ PCI_ANY_ID, PCI_ANY_ID, },
+ { 0, }, /* End of list */
+};
+MODULE_DEVICE_TABLE(pci, tco_pci_tbl);
+
+/*
+ * Init & exit routines
+ */
+
+static unsigned char __init nv_tco_getdevice(void)
+{
+ struct pci_dev *dev = NULL;
+ u32 val;
+
+ /* Find the PCI device */
+ for_each_pci_dev(dev) {
+ if (pci_match_id(tco_pci_tbl, dev) != NULL) {
+ tco_pci = dev;
+ break;
+ }
+ }
+
+ if (!tco_pci)
+ return 0;
+
+ /* Find the base io port */
+ pci_read_config_dword(tco_pci, 0x64, &val);
+ val &= 0xffff;
+ if (val == 0x0001 || val == 0x0000) {
+ /* Something is wrong here, bar isn't setup */
+ printk(KERN_ERR PFX "failed to get tcobase address\n");
+ return 0;
+ }
+ val &= 0xff00;
+ tcobase = val + 0x40;
+
+ if (!request_region(tcobase, 0x10, "NV TCO")) {
+ printk(KERN_ERR PFX "I/O address 0x%04x already in use\n",
+ tcobase);
+ return 0;
+ }
+
+ /* Set a reasonable heartbeat before we stop the timer */
+ tco_timer_set_heartbeat(30);
+
+ /*
+ * Stop the TCO before we change anything so we don't race with
+ * a zeroed timer.
+ */
+ tco_timer_keepalive();
+ tco_timer_stop();
+
+ /* Disable SMI caused by TCO */
+ if (!request_region(MCP51_SMI_EN(tcobase), 4, "NV TCO")) {
+ printk(KERN_ERR PFX "I/O address 0x%04x already in use\n",
+ MCP51_SMI_EN(tcobase));
+ goto out;
+ }
+ val = inl(MCP51_SMI_EN(tcobase));
+ val &= ~MCP51_SMI_EN_TCO;
+ outl(val, MCP51_SMI_EN(tcobase));
+ val = inl(MCP51_SMI_EN(tcobase));
+ release_region(MCP51_SMI_EN(tcobase), 4);
+ if (val & MCP51_SMI_EN_TCO) {
+ printk(KERN_ERR PFX "Could not disable SMI caused by TCO\n");
+ goto out;
+ }
+
+ /* Check chipset's NO_REBOOT bit */
+ pci_read_config_dword(tco_pci, MCP51_SMBUS_SETUP_B, &val);
+ val |= MCP51_SMBUS_SETUP_B_TCO_REBOOT;
+ pci_write_config_dword(tco_pci, MCP51_SMBUS_SETUP_B, val);
+ pci_read_config_dword(tco_pci, MCP51_SMBUS_SETUP_B, &val);
+ if (!(val & MCP51_SMBUS_SETUP_B_TCO_REBOOT)) {
+ printk(KERN_ERR PFX "failed to reset NO_REBOOT flag, reboot "
+ "disabled by hardware\n");
+ goto out;
+ }
+
+ return 1;
+out:
+ release_region(tcobase, 0x10);
+ return 0;
+}
+
+static int __devinit nv_tco_init(struct platform_device *dev)
+{
+ int ret;
+
+ /* Check whether or not the hardware watchdog is there */
+ if (!nv_tco_getdevice())
+ return -ENODEV;
+
+ /* Check to see if last reboot was due to watchdog timeout */
+ printk(KERN_INFO PFX "Watchdog reboot %sdetected.\n",
+ inl(TCO_STS(tcobase)) & TCO_STS_TCO2TO_STS ? "" : "not ");
+
+ /* Clear out the old status */
+ outl(TCO_STS_RESET, TCO_STS(tcobase));
+
+ /*
+ * Check that the heartbeat value is within it's range.
+ * If not, reset to the default.
+ */
+ if (tco_timer_set_heartbeat(heartbeat)) {
+ heartbeat = WATCHDOG_HEARTBEAT;
+ tco_timer_set_heartbeat(heartbeat);
+ printk(KERN_INFO PFX "heartbeat value must be 2<heartbeat<39, "
+ "using %d\n", heartbeat);
+ }
+
+ ret = misc_register(&nv_tco_miscdev);
+ if (ret != 0) {
+ printk(KERN_ERR PFX "cannot register miscdev on minor=%d "
+ "(err=%d)\n", WATCHDOG_MINOR, ret);
+ goto unreg_region;
+ }
+
+ clear_bit(0, &timer_alive);
+
+ tco_timer_stop();
+
+ printk(KERN_INFO PFX "initialized (0x%04x). heartbeat=%d sec "
+ "(nowayout=%d)\n", tcobase, heartbeat, nowayout);
+
+ return 0;
+
+unreg_region:
+ release_region(tcobase, 0x10);
+ return ret;
+}
+
+static void __devexit nv_tco_cleanup(void)
+{
+ u32 val;
+
+ /* Stop the timer before we leave */
+ if (!nowayout)
+ tco_timer_stop();
+
+ /* Set the NO_REBOOT bit to prevent later reboots, just for sure */
+ pci_read_config_dword(tco_pci, MCP51_SMBUS_SETUP_B, &val);
+ val &= ~MCP51_SMBUS_SETUP_B_TCO_REBOOT;
+ pci_write_config_dword(tco_pci, MCP51_SMBUS_SETUP_B, val);
+ pci_read_config_dword(tco_pci, MCP51_SMBUS_SETUP_B, &val);
+ if (val & MCP51_SMBUS_SETUP_B_TCO_REBOOT) {
+ printk(KERN_CRIT PFX "Couldn't unset REBOOT bit. Machine may "
+ "soon reset\n");
+ }
+
+ /* Deregister */
+ misc_deregister(&nv_tco_miscdev);
+ release_region(tcobase, 0x10);
+}
+
+static int __devexit nv_tco_remove(struct platform_device *dev)
+{
+ if (tcobase)
+ nv_tco_cleanup();
+
+ return 0;
+}
+
+static void nv_tco_shutdown(struct platform_device *dev)
+{
+ tco_timer_stop();
+}
+
+static struct platform_driver nv_tco_driver = {
+ .probe = nv_tco_init,
+ .remove = __devexit_p(nv_tco_remove),
+ .shutdown = nv_tco_shutdown,
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = TCO_MODULE_NAME,
+ },
+};
+
+static int __init nv_tco_init_module(void)
+{
+ int err;
+
+ printk(KERN_INFO PFX "NV TCO WatchDog Timer Driver v%s\n",
+ TCO_VERSION);
+
+ err = platform_driver_register(&nv_tco_driver);
+ if (err)
+ return err;
+
+ nv_tco_platform_device = platform_device_register_simple(
+ TCO_MODULE_NAME, -1, NULL, 0);
+ if (IS_ERR(nv_tco_platform_device)) {
+ err = PTR_ERR(nv_tco_platform_device);
+ goto unreg_platform_driver;
+ }
+
+ return 0;
+
+unreg_platform_driver:
+ platform_driver_unregister(&nv_tco_driver);
+ return err;
+}
+
+static void __exit nv_tco_cleanup_module(void)
+{
+ platform_device_unregister(nv_tco_platform_device);
+ platform_driver_unregister(&nv_tco_driver);
+ printk(KERN_INFO PFX "NV TCO Watchdog Module Unloaded.\n");
+}
+
+module_init(nv_tco_init_module);
+module_exit(nv_tco_cleanup_module);
+
+MODULE_AUTHOR("Mike Waychison");
+MODULE_DESCRIPTION("TCO timer driver for NV chipsets");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
diff --git a/drivers/watchdog/nv_tco.h b/drivers/watchdog/nv_tco.h
new file mode 100644
index 0000000..c2d1d04
--- /dev/null
+++ b/drivers/watchdog/nv_tco.h
@@ -0,0 +1,64 @@
+/*
+ * nv_tco: TCO timer driver for nVidia chipsets.
+ *
+ * (c) Copyright 2005 Google Inc., All Rights Reserved.
+ *
+ * Supported Chipsets:
+ * - MCP51/MCP55
+ *
+ * (c) Copyright 2000 kernel concepts <nils@kernelconcepts.de>, All Rights
+ * Reserved.
+ * http://www.kernelconcepts.de
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ *
+ * Neither kernel concepts nor Nils Faerber admit liability nor provide
+ * warranty for any of this software. This material is provided
+ * "AS-IS" and at no charge.
+ *
+ * (c) Copyright 2000 kernel concepts <nils@kernelconcepts.de>
+ * developed for
+ * Jentro AG, Haar/Munich (Germany)
+ *
+ * TCO timer driver for NV chipsets
+ * based on softdog.c by Alan Cox <alan@redhat.com>
+ */
+
+/*
+ * Some address definitions for the TCO
+ */
+
+#define TCO_RLD(base) ((base) + 0x00) /* TCO Timer Reload and Current Value */
+#define TCO_TMR(base) ((base) + 0x01) /* TCO Timer Initial Value */
+
+#define TCO_STS(base) ((base) + 0x04) /* TCO Status Register */
+/*
+ * TCO Boot Status bit: set on TCO reset, reset by software or standby
+ * power-good (survives reboots), unfortunately this bit is never
+ * set.
+ */
+# define TCO_STS_BOOT_STS (1 << 9)
+/*
+ * first and 2nd timeout status bits, these also survive a warm boot,
+ * and they work, so we use them.
+ */
+# define TCO_STS_TCO_INT_STS (1 << 1)
+# define TCO_STS_TCO2TO_STS (1 << 10)
+# define TCO_STS_RESET (TCO_STS_BOOT_STS | TCO_STS_TCO2TO_STS | \
+ TCO_STS_TCO_INT_STS)
+
+#define TCO_CNT(base) ((base) + 0x08) /* TCO Control Register */
+# define TCO_CNT_TCOHALT (1 << 12)
+
+#define MCP51_SMBUS_SETUP_B 0xe8
+# define MCP51_SMBUS_SETUP_B_TCO_REBOOT (1 << 25)
+
+/*
+ * The SMI_EN register is at the base io address + 0x04,
+ * while TCOBASE is + 0x40.
+ */
+#define MCP51_SMI_EN(base) ((base) - 0x40 + 0x04)
+# define MCP51_SMI_EN_TCO ((1 << 4) | (1 << 5))
diff --git a/drivers/watchdog/sp5100_tco.c b/drivers/watchdog/sp5100_tco.c
new file mode 100644
index 0000000..8083728
--- /dev/null
+++ b/drivers/watchdog/sp5100_tco.c
@@ -0,0 +1,480 @@
+/*
+ * sp5100_tco : TCO timer driver for sp5100 chipsets
+ *
+ * (c) Copyright 2009 Google Inc., All Rights Reserved.
+ *
+ * Based on i8xx_tco.c:
+ * (c) Copyright 2000 kernel concepts <nils@kernelconcepts.de>, All Rights
+ * Reserved.
+ * http://www.kernelconcepts.de
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ *
+ * See AMD Publication 43009 "AMD SB700/710/750 Register Reference Guide"
+ */
+
+/*
+ * Includes, defines, variables, module parameters, ...
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/types.h>
+#include <linux/miscdevice.h>
+#include <linux/watchdog.h>
+#include <linux/init.h>
+#include <linux/fs.h>
+#include <linux/pci.h>
+#include <linux/ioport.h>
+#include <linux/platform_device.h>
+#include <linux/uaccess.h>
+#include <linux/io.h>
+
+#include "sp5100_tco.h"
+
+/* Module and version information */
+#define TCO_VERSION "0.01"
+#define TCO_MODULE_NAME "SP5100 TCO timer"
+#define TCO_DRIVER_NAME TCO_MODULE_NAME ", v" TCO_VERSION
+#define PFX TCO_MODULE_NAME ": "
+
+/* internal variables */
+static void __iomem *tcobase;
+static unsigned int pm_iobase;
+static DEFINE_SPINLOCK(tco_lock); /* Guards the hardware */
+static unsigned long timer_alive;
+static char tco_expect_close;
+static struct pci_dev *sp5100_tco_pci;
+
+/* the watchdog platform device */
+static struct platform_device *sp5100_tco_platform_device;
+
+/* module parameters */
+
+#define WATCHDOG_HEARTBEAT 60 /* 60 sec default heartbeat. */
+static int heartbeat = WATCHDOG_HEARTBEAT; /* in seconds */
+module_param(heartbeat, int, 0);
+MODULE_PARM_DESC(heartbeat, "Watchdog heartbeat in seconds. (default="
+ __MODULE_STRING(WATCHDOG_HEARTBEAT) ")");
+
+static int nowayout = WATCHDOG_NOWAYOUT;
+module_param(nowayout, int, 0);
+MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started"
+ " (default=" __MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
+
+/*
+ * Some TCO specific functions
+ */
+static void tco_timer_start(void)
+{
+ u32 val;
+ unsigned long flags;
+
+ spin_lock_irqsave(&tco_lock, flags);
+ val = readl(SP5100_WDT_CONTROL(tcobase));
+ val |= SP5100_WDT_START_STOP_BIT;
+ writel(val, SP5100_WDT_CONTROL(tcobase));
+ spin_unlock_irqrestore(&tco_lock, flags);
+}
+
+static void tco_timer_stop(void)
+{
+ u32 val;
+ unsigned long flags;
+
+ spin_lock_irqsave(&tco_lock, flags);
+ val = readl(SP5100_WDT_CONTROL(tcobase));
+ val &= ~SP5100_WDT_START_STOP_BIT;
+ writel(val, SP5100_WDT_CONTROL(tcobase));
+ spin_unlock_irqrestore(&tco_lock, flags);
+}
+
+static void tco_timer_keepalive(void)
+{
+ u32 val;
+ unsigned long flags;
+
+ spin_lock_irqsave(&tco_lock, flags);
+ val = readl(SP5100_WDT_CONTROL(tcobase));
+ val |= SP5100_WDT_TRIGGER_BIT;
+ writel(val, SP5100_WDT_CONTROL(tcobase));
+ spin_unlock_irqrestore(&tco_lock, flags);
+}
+
+static int tco_timer_set_heartbeat(int t)
+{
+ unsigned long flags;
+
+ if (t < 0 || t > 0xffff)
+ return -EINVAL;
+
+ /* Write new heartbeat to watchdog */
+ spin_lock_irqsave(&tco_lock, flags);
+ writel(t, SP5100_WDT_COUNT(tcobase));
+ spin_unlock_irqrestore(&tco_lock, flags);
+
+ heartbeat = t;
+ return 0;
+}
+
+/*
+ * /dev/watchdog handling
+ */
+
+static int sp5100_tco_open(struct inode *inode, struct file *file)
+{
+ /* /dev/watchdog can only be opened once */
+ if (test_and_set_bit(0, &timer_alive))
+ return -EBUSY;
+
+ /* Reload and activate timer */
+ tco_timer_start();
+ tco_timer_keepalive();
+ return nonseekable_open(inode, file);
+}
+
+static int sp5100_tco_release(struct inode *inode, struct file *file)
+{
+ /* Shut off the timer. */
+ if (tco_expect_close == 42) {
+ tco_timer_stop();
+ } else {
+ printk(KERN_CRIT PFX
+ "Unexpected close, not stopping watchdog!\n");
+ tco_timer_keepalive();
+ }
+ clear_bit(0, &timer_alive);
+ tco_expect_close = 0;
+ return 0;
+}
+
+static ssize_t sp5100_tco_write(struct file *file, const char __user *data,
+ size_t len, loff_t *ppos)
+{
+ /* See if we got the magic character 'V' and reload the timer */
+ if (len) {
+ if (!nowayout) {
+ size_t i;
+
+ /* note: just in case someone wrote the magic character
+ * five months ago... */
+ tco_expect_close = 0;
+
+ /* scan to see whether or not we got the magic character
+ */
+ for (i = 0; i != len; i++) {
+ char c;
+ if (get_user(c, data + i))
+ return -EFAULT;
+ if (c == 'V')
+ tco_expect_close = 42;
+ }
+ }
+
+ /* someone wrote to us, we should reload the timer */
+ tco_timer_keepalive();
+ }
+ return len;
+}
+
+static long sp5100_tco_ioctl(struct file *file, unsigned int cmd,
+ unsigned long arg)
+{
+ int new_options, retval = -EINVAL;
+ int new_heartbeat;
+ void __user *argp = (void __user *)arg;
+ int __user *p = argp;
+ static const struct watchdog_info ident = {
+ .options = WDIOF_SETTIMEOUT |
+ WDIOF_KEEPALIVEPING |
+ WDIOF_MAGICCLOSE,
+ .firmware_version = 0,
+ .identity = TCO_MODULE_NAME,
+ };
+
+ switch (cmd) {
+ case WDIOC_GETSUPPORT:
+ return copy_to_user(argp, &ident,
+ sizeof(ident)) ? -EFAULT : 0;
+ case WDIOC_GETSTATUS:
+ case WDIOC_GETBOOTSTATUS:
+ return put_user(0, p);
+ case WDIOC_SETOPTIONS:
+ if (get_user(new_options, p))
+ return -EFAULT;
+ if (new_options & WDIOS_DISABLECARD) {
+ tco_timer_stop();
+ retval = 0;
+ }
+ if (new_options & WDIOS_ENABLECARD) {
+ tco_timer_start();
+ tco_timer_keepalive();
+ retval = 0;
+ }
+ return retval;
+ case WDIOC_KEEPALIVE:
+ tco_timer_keepalive();
+ return 0;
+ case WDIOC_SETTIMEOUT:
+ if (get_user(new_heartbeat, p))
+ return -EFAULT;
+ if (tco_timer_set_heartbeat(new_heartbeat))
+ return -EINVAL;
+ tco_timer_keepalive();
+ /* Fall through */
+ case WDIOC_GETTIMEOUT:
+ return put_user(heartbeat, p);
+ default:
+ return -ENOTTY;
+ }
+}
+
+/*
+ * Kernel Interfaces
+ */
+
+static const struct file_operations sp5100_tco_fops = {
+ .owner = THIS_MODULE,
+ .llseek = no_llseek,
+ .write = sp5100_tco_write,
+ .unlocked_ioctl = sp5100_tco_ioctl,
+ .open = sp5100_tco_open,
+ .release = sp5100_tco_release,
+};
+
+static struct miscdevice sp5100_tco_miscdev = {
+ .minor = WATCHDOG_MINOR,
+ .name = "watchdog",
+ .fops = &sp5100_tco_fops,
+};
+
+/*
+ * Data for PCI driver interface
+ *
+ * This data only exists for exporting the supported
+ * PCI ids via MODULE_DEVICE_TABLE. We do not actually
+ * register a pci_driver, because someone else might
+ * want to register another driver on the same PCI id.
+ */
+static struct pci_device_id sp5100_tco_pci_tbl[] = {
+ { PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_SBX00_SMBUS, PCI_ANY_ID,
+ PCI_ANY_ID, },
+ { 0, }, /* End of list */
+};
+MODULE_DEVICE_TABLE(pci, sp5100_tco_pci_tbl);
+
+/*
+ * Init & exit routines
+ */
+
+static unsigned char __devinit sp5100_tco_setupdevice(void)
+{
+ struct pci_dev *dev = NULL;
+ u32 val;
+
+ /* Match the PCI device */
+ for_each_pci_dev(dev) {
+ if (pci_match_id(sp5100_tco_pci_tbl, dev) != NULL) {
+ sp5100_tco_pci = dev;
+ break;
+ }
+ }
+
+ if (!sp5100_tco_pci)
+ return 0;
+
+ /* Request the IO ports used by this driver */
+ pm_iobase = SP5100_IO_PM_INDEX_REG;
+ if (!request_region(pm_iobase, SP5100_PM_IOPORTS_SIZE, "SP5100 TCO")) {
+ printk(KERN_ERR PFX "I/O address 0x%04x already in use\n",
+ pm_iobase);
+ goto exit;
+ }
+
+ /* Find the watchdog base address. */
+ outb(SP5100_PM_WATCHDOG_BASE3, SP5100_IO_PM_INDEX_REG);
+ val = inb(SP5100_IO_PM_DATA_REG);
+ outb(SP5100_PM_WATCHDOG_BASE2, SP5100_IO_PM_INDEX_REG);
+ val = val << 8 | inb(SP5100_IO_PM_DATA_REG);
+ outb(SP5100_PM_WATCHDOG_BASE1, SP5100_IO_PM_INDEX_REG);
+ val = val << 8 | inb(SP5100_IO_PM_DATA_REG);
+ outb(SP5100_PM_WATCHDOG_BASE0, SP5100_IO_PM_INDEX_REG);
+ /* Low three bits of BASE0 are reserved. */
+ val = val << 8 | (inb(SP5100_IO_PM_DATA_REG) & 0xf8);
+
+ tcobase = ioremap(val, SP5100_WDT_MEM_MAP_SIZE);
+ if (tcobase == 0) {
+ printk(KERN_ERR PFX "failed to get tcobase address\n");
+ goto unreg_region;
+ }
+
+ /* Enable watchdog decode bit */
+ pci_read_config_dword(sp5100_tco_pci,
+ SP5100_PCI_WATCHDOG_MISC_REG,
+ &val);
+
+ val |= SP5100_PCI_WATCHDOG_DECODE_EN;
+
+ pci_write_config_dword(sp5100_tco_pci,
+ SP5100_PCI_WATCHDOG_MISC_REG,
+ val);
+
+ /* Enable Watchdog timer and set the resolution to 1 sec. */
+ outb(SP5100_PM_WATCHDOG_CONTROL, SP5100_IO_PM_INDEX_REG);
+ val = inb(SP5100_IO_PM_DATA_REG);
+ val |= SP5100_PM_WATCHDOG_SECOND_RES;
+ val &= ~SP5100_PM_WATCHDOG_DISABLE;
+ outb(val, SP5100_IO_PM_DATA_REG);
+
+ /* Check that the watchdog action is set to reset the system. */
+ val = readl(SP5100_WDT_CONTROL(tcobase));
+ val &= ~SP5100_PM_WATCHDOG_ACTION_RESET;
+ writel(val, SP5100_WDT_CONTROL(tcobase));
+
+ /* Set a reasonable heartbeat before we stop the timer */
+ tco_timer_set_heartbeat(heartbeat);
+
+ /*
+ * Stop the TCO before we change anything so we don't race with
+ * a zeroed timer.
+ */
+ tco_timer_stop();
+
+ /* Done */
+ return 1;
+
+ iounmap(tcobase);
+unreg_region:
+ release_region(pm_iobase, SP5100_PM_IOPORTS_SIZE);
+exit:
+ return 0;
+}
+
+static int __devinit sp5100_tco_init(struct platform_device *dev)
+{
+ int ret;
+ u32 val;
+
+ /* Check whether or not the hardware watchdog is there. If found, then
+ * set it up.
+ */
+ if (!sp5100_tco_setupdevice())
+ return -ENODEV;
+
+ /* Check to see if last reboot was due to watchdog timeout */
+ printk(KERN_INFO PFX "Watchdog reboot %sdetected.\n",
+ readl(SP5100_WDT_CONTROL(tcobase)) & SP5100_PM_WATCHDOG_FIRED ?
+ "" : "not ");
+
+ /* Clear out the old status */
+ val = readl(SP5100_WDT_CONTROL(tcobase));
+ val &= ~SP5100_PM_WATCHDOG_FIRED;
+ writel(val, SP5100_WDT_CONTROL(tcobase));
+
+ /*
+ * Check that the heartbeat value is within it's range.
+ * If not, reset to the default.
+ */
+ if (tco_timer_set_heartbeat(heartbeat)) {
+ heartbeat = WATCHDOG_HEARTBEAT;
+ tco_timer_set_heartbeat(heartbeat);
+ }
+
+ ret = misc_register(&sp5100_tco_miscdev);
+ if (ret != 0) {
+ printk(KERN_ERR PFX "cannot register miscdev on minor="
+ "%d (err=%d)\n",
+ WATCHDOG_MINOR, ret);
+ goto exit;
+ }
+
+ clear_bit(0, &timer_alive);
+
+ printk(KERN_INFO PFX "initialized (0x%p). heartbeat=%d sec"
+ " (nowayout=%d)\n",
+ tcobase, heartbeat, nowayout);
+
+ return 0;
+
+exit:
+ iounmap(tcobase);
+ release_region(pm_iobase, SP5100_PM_IOPORTS_SIZE);
+ return ret;
+}
+
+static void __devexit sp5100_tco_cleanup(void)
+{
+ /* Stop the timer before we leave */
+ if (!nowayout)
+ tco_timer_stop();
+
+ /* Deregister */
+ misc_deregister(&sp5100_tco_miscdev);
+ iounmap(tcobase);
+ release_region(pm_iobase, SP5100_PM_IOPORTS_SIZE);
+}
+
+static int __devexit sp5100_tco_remove(struct platform_device *dev)
+{
+ if (tcobase)
+ sp5100_tco_cleanup();
+ return 0;
+}
+
+static void sp5100_tco_shutdown(struct platform_device *dev)
+{
+ tco_timer_stop();
+}
+
+static struct platform_driver sp5100_tco_driver = {
+ .probe = sp5100_tco_init,
+ .remove = __devexit_p(sp5100_tco_remove),
+ .shutdown = sp5100_tco_shutdown,
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = TCO_MODULE_NAME,
+ },
+};
+
+static int __init sp5100_tco_init_module(void)
+{
+ int err;
+
+ printk(KERN_INFO PFX "SP5100 TCO WatchDog Timer Driver v%s\n",
+ TCO_VERSION);
+
+ err = platform_driver_register(&sp5100_tco_driver);
+ if (err)
+ return err;
+
+ sp5100_tco_platform_device = platform_device_register_simple(
+ TCO_MODULE_NAME, -1, NULL, 0);
+ if (IS_ERR(sp5100_tco_platform_device)) {
+ err = PTR_ERR(sp5100_tco_platform_device);
+ goto unreg_platform_driver;
+ }
+
+ return 0;
+
+unreg_platform_driver:
+ platform_driver_unregister(&sp5100_tco_driver);
+ return err;
+}
+
+static void __exit sp5100_tco_cleanup_module(void)
+{
+ platform_device_unregister(sp5100_tco_platform_device);
+ platform_driver_unregister(&sp5100_tco_driver);
+ printk(KERN_INFO PFX "SP5100 TCO Watchdog Module Unloaded.\n");
+}
+
+module_init(sp5100_tco_init_module);
+module_exit(sp5100_tco_cleanup_module);
+
+MODULE_AUTHOR("Priyanka Gupta");
+MODULE_DESCRIPTION("TCO timer driver for SP5100 chipset");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
diff --git a/drivers/watchdog/sp5100_tco.h b/drivers/watchdog/sp5100_tco.h
new file mode 100644
index 0000000..a5a16cc
--- /dev/null
+++ b/drivers/watchdog/sp5100_tco.h
@@ -0,0 +1,41 @@
+/*
+ * sp5100_tco: TCO timer driver for sp5100 chipsets.
+ *
+ * (c) Copyright 2009 Google Inc., All Rights Reserved.
+ *
+ * TCO timer driver for sp5100 chipsets
+ */
+
+/*
+ * Some address definitions for the Watchdog
+ */
+
+#define SP5100_WDT_MEM_MAP_SIZE 0x08
+#define SP5100_WDT_CONTROL(base) ((base) + 0x00) /* Watchdog Control */
+#define SP5100_WDT_COUNT(base) ((base) + 0x04) /* Watchdog Count */
+
+#define SP5100_WDT_START_STOP_BIT 1
+#define SP5100_WDT_TRIGGER_BIT (1 << 7)
+
+#define SP5100_PCI_WATCHDOG_MISC_REG 0x41
+#define SP5100_PCI_WATCHDOG_DECODE_EN (1 << 3)
+
+#define SP5100_PM_IOPORTS_SIZE 0x02
+
+/* These two IO registers are hardcoded and there doesn't seem to be a way to
+ * read them from a register.
+ */
+#define SP5100_IO_PM_INDEX_REG 0xCD6
+#define SP5100_IO_PM_DATA_REG 0xCD7
+
+#define SP5100_PM_WATCHDOG_CONTROL 0x69
+#define SP5100_PM_WATCHDOG_BASE0 0x6C
+#define SP5100_PM_WATCHDOG_BASE1 0x6D
+#define SP5100_PM_WATCHDOG_BASE2 0x6E
+#define SP5100_PM_WATCHDOG_BASE3 0x6F
+
+#define SP5100_PM_WATCHDOG_FIRED (1 << 1)
+#define SP5100_PM_WATCHDOG_ACTION_RESET (1 << 2)
+
+#define SP5100_PM_WATCHDOG_DISABLE 1
+#define SP5100_PM_WATCHDOG_SECOND_RES (3 << 1)
diff --git a/drivers/watchdog/w83627hf_wdt.c b/drivers/watchdog/w83627hf_wdt.c
index 0f5288d..e5c91d4 100644
--- a/drivers/watchdog/w83627hf_wdt.c
+++ b/drivers/watchdog/w83627hf_wdt.c
@@ -42,7 +42,7 @@
#include <asm/system.h>
-#define WATCHDOG_NAME "w83627hf/thf/hg WDT"
+#define WATCHDOG_NAME "w83627hf/thf/hg/dhg WDT"
#define PFX WATCHDOG_NAME ": "
#define WATCHDOG_TIMEOUT 60 /* 60 sec default timeout */
@@ -89,7 +89,7 @@
c = ((inb_p(WDT_EFDR) & 0xf7) | 0x04); /* select WDT0 */
outb_p(0x2b, WDT_EFER);
outb_p(c, WDT_EFDR); /* set GPIO3 to WDT0 */
- } else if (c == 0x88) { /* W83627EHF */
+ } else if (c == 0x88 || c == 0xa0) { /* W83627EHF / W83627DHG */
outb_p(0x2d, WDT_EFER); /* select GPIO5 */
c = inb_p(WDT_EFDR) & ~0x01; /* PIN77 -> WDT0# */
outb_p(0x2d, WDT_EFER);
@@ -129,6 +129,8 @@
t = inb_p(WDT_EFDR); /* read CRF5 */
t &= ~0x0C; /* set second mode & disable keyboard
turning off watchdog */
+ t |= 0x02; /* enable the WDTO# output low pulse
+ to the KBRST# pin (PIN60) */
outb_p(t, WDT_EFDR); /* Write back to CRF5 */
outb_p(0xF7, WDT_EFER); /* Select CRF7 */
@@ -321,7 +323,7 @@
{
int ret;
- printk(KERN_INFO "WDT driver for the Winbond(TM) W83627HF/THF/HG Super I/O chip initialising.\n");
+ printk(KERN_INFO "WDT driver for the Winbond(TM) W83627HF/THF/HG/DHG Super I/O chip initialising.\n");
if (wdt_set_heartbeat(timeout)) {
wdt_set_heartbeat(WATCHDOG_TIMEOUT);
diff --git a/drivers/xen/Kconfig b/drivers/xen/Kconfig
index 6e6180c..5a48ce9 100644
--- a/drivers/xen/Kconfig
+++ b/drivers/xen/Kconfig
@@ -29,6 +29,14 @@
firing.
If in doubt, say yes.
+config XEN_BACKEND
+ bool "Backend driver support"
+ depends on XEN_DOM0
+ default y
+ help
+ Support for backend device drivers that provide I/O services
+ to other virtual machines.
+
config XENFS
tristate "Xen filesystem"
default y
@@ -62,6 +70,9 @@
virtual environment, /sys/hypervisor will still be present,
but will have no xen contents.
+config XEN_XENBUS_FRONTEND
+ tristate
+
config XEN_PLATFORM_PCI
tristate "xen platform pci device driver"
depends on XEN_PVHVM
diff --git a/drivers/xen/xenbus/Makefile b/drivers/xen/xenbus/Makefile
index 5571f5b..8dca685 100644
--- a/drivers/xen/xenbus/Makefile
+++ b/drivers/xen/xenbus/Makefile
@@ -5,3 +5,8 @@
xenbus-objs += xenbus_comms.o
xenbus-objs += xenbus_xs.o
xenbus-objs += xenbus_probe.o
+
+xenbus-be-objs-$(CONFIG_XEN_BACKEND) += xenbus_probe_backend.o
+xenbus-objs += $(xenbus-be-objs-y)
+
+obj-$(CONFIG_XEN_XENBUS_FRONTEND) += xenbus_probe_frontend.o
diff --git a/drivers/xen/xenbus/xenbus_probe.c b/drivers/xen/xenbus/xenbus_probe.c
index deb9c4b..baa65e7 100644
--- a/drivers/xen/xenbus/xenbus_probe.c
+++ b/drivers/xen/xenbus/xenbus_probe.c
@@ -56,7 +56,6 @@
#include <xen/events.h>
#include <xen/page.h>
-#include <xen/platform_pci.h>
#include <xen/hvm.h>
#include "xenbus_comms.h"
@@ -73,15 +72,6 @@
static BLOCKING_NOTIFIER_HEAD(xenstore_chain);
-static void wait_for_devices(struct xenbus_driver *xendrv);
-
-static int xenbus_probe_frontend(const char *type, const char *name);
-
-static void xenbus_dev_shutdown(struct device *_dev);
-
-static int xenbus_dev_suspend(struct device *dev, pm_message_t state);
-static int xenbus_dev_resume(struct device *dev);
-
/* If something in array of ids matches this device, return it. */
static const struct xenbus_device_id *
match_device(const struct xenbus_device_id *arr, struct xenbus_device *dev)
@@ -102,34 +92,7 @@
return match_device(drv->ids, to_xenbus_device(_dev)) != NULL;
}
-
-static int xenbus_uevent(struct device *_dev, struct kobj_uevent_env *env)
-{
- struct xenbus_device *dev = to_xenbus_device(_dev);
-
- if (add_uevent_var(env, "MODALIAS=xen:%s", dev->devicetype))
- return -ENOMEM;
-
- return 0;
-}
-
-/* device/<type>/<id> => <type>-<id> */
-static int frontend_bus_id(char bus_id[XEN_BUS_ID_SIZE], const char *nodename)
-{
- nodename = strchr(nodename, '/');
- if (!nodename || strlen(nodename + 1) >= XEN_BUS_ID_SIZE) {
- printk(KERN_WARNING "XENBUS: bad frontend %s\n", nodename);
- return -EINVAL;
- }
-
- strlcpy(bus_id, nodename + 1, XEN_BUS_ID_SIZE);
- if (!strchr(bus_id, '/')) {
- printk(KERN_WARNING "XENBUS: bus_id %s no slash\n", bus_id);
- return -EINVAL;
- }
- *strchr(bus_id, '/') = '-';
- return 0;
-}
+EXPORT_SYMBOL_GPL(xenbus_match);
static void free_otherend_details(struct xenbus_device *dev)
@@ -149,7 +112,30 @@
}
-int read_otherend_details(struct xenbus_device *xendev,
+static int talk_to_otherend(struct xenbus_device *dev)
+{
+ struct xenbus_driver *drv = to_xenbus_driver(dev->dev.driver);
+
+ free_otherend_watch(dev);
+ free_otherend_details(dev);
+
+ return drv->read_otherend_details(dev);
+}
+
+
+
+static int watch_otherend(struct xenbus_device *dev)
+{
+ struct xen_bus_type *bus =
+ container_of(dev->dev.bus, struct xen_bus_type, bus);
+
+ return xenbus_watch_pathfmt(dev, &dev->otherend_watch,
+ bus->otherend_changed,
+ "%s/%s", dev->otherend, "state");
+}
+
+
+int xenbus_read_otherend_details(struct xenbus_device *xendev,
char *id_node, char *path_node)
{
int err = xenbus_gather(XBT_NIL, xendev->nodename,
@@ -174,39 +160,11 @@
return 0;
}
+EXPORT_SYMBOL_GPL(xenbus_read_otherend_details);
-
-static int read_backend_details(struct xenbus_device *xendev)
-{
- return read_otherend_details(xendev, "backend-id", "backend");
-}
-
-static struct device_attribute xenbus_dev_attrs[] = {
- __ATTR_NULL
-};
-
-/* Bus type for frontend drivers. */
-static struct xen_bus_type xenbus_frontend = {
- .root = "device",
- .levels = 2, /* device/type/<id> */
- .get_bus_id = frontend_bus_id,
- .probe = xenbus_probe_frontend,
- .bus = {
- .name = "xen",
- .match = xenbus_match,
- .uevent = xenbus_uevent,
- .probe = xenbus_dev_probe,
- .remove = xenbus_dev_remove,
- .shutdown = xenbus_dev_shutdown,
- .dev_attrs = xenbus_dev_attrs,
-
- .suspend = xenbus_dev_suspend,
- .resume = xenbus_dev_resume,
- },
-};
-
-static void otherend_changed(struct xenbus_watch *watch,
- const char **vec, unsigned int len)
+void xenbus_otherend_changed(struct xenbus_watch *watch,
+ const char **vec, unsigned int len,
+ int ignore_on_shutdown)
{
struct xenbus_device *dev =
container_of(watch, struct xenbus_device, otherend_watch);
@@ -234,11 +192,7 @@
* work that can fail e.g., when the rootfs is gone.
*/
if (system_state > SYSTEM_RUNNING) {
- struct xen_bus_type *bus = bus;
- bus = container_of(dev->dev.bus, struct xen_bus_type, bus);
- /* If we're frontend, drive the state machine to Closed. */
- /* This should cause the backend to release our resources. */
- if ((bus == &xenbus_frontend) && (state == XenbusStateClosing))
+ if (ignore_on_shutdown && (state == XenbusStateClosing))
xenbus_frontend_closed(dev);
return;
}
@@ -246,25 +200,7 @@
if (drv->otherend_changed)
drv->otherend_changed(dev, state);
}
-
-
-static int talk_to_otherend(struct xenbus_device *dev)
-{
- struct xenbus_driver *drv = to_xenbus_driver(dev->dev.driver);
-
- free_otherend_watch(dev);
- free_otherend_details(dev);
-
- return drv->read_otherend_details(dev);
-}
-
-
-static int watch_otherend(struct xenbus_device *dev)
-{
- return xenbus_watch_pathfmt(dev, &dev->otherend_watch, otherend_changed,
- "%s/%s", dev->otherend, "state");
-}
-
+EXPORT_SYMBOL_GPL(xenbus_otherend_changed);
int xenbus_dev_probe(struct device *_dev)
{
@@ -308,8 +244,9 @@
fail:
xenbus_dev_error(dev, err, "xenbus_dev_probe on %s", dev->nodename);
xenbus_switch_state(dev, XenbusStateClosed);
- return -ENODEV;
+ return err;
}
+EXPORT_SYMBOL_GPL(xenbus_dev_probe);
int xenbus_dev_remove(struct device *_dev)
{
@@ -327,8 +264,9 @@
xenbus_switch_state(dev, XenbusStateClosed);
return 0;
}
+EXPORT_SYMBOL_GPL(xenbus_dev_remove);
-static void xenbus_dev_shutdown(struct device *_dev)
+void xenbus_dev_shutdown(struct device *_dev)
{
struct xenbus_device *dev = to_xenbus_device(_dev);
unsigned long timeout = 5*HZ;
@@ -349,6 +287,7 @@
out:
put_device(&dev->dev);
}
+EXPORT_SYMBOL_GPL(xenbus_dev_shutdown);
int xenbus_register_driver_common(struct xenbus_driver *drv,
struct xen_bus_type *bus,
@@ -362,25 +301,7 @@
return driver_register(&drv->driver);
}
-
-int __xenbus_register_frontend(struct xenbus_driver *drv,
- struct module *owner, const char *mod_name)
-{
- int ret;
-
- drv->read_otherend_details = read_backend_details;
-
- ret = xenbus_register_driver_common(drv, &xenbus_frontend,
- owner, mod_name);
- if (ret)
- return ret;
-
- /* If this driver is loaded as a module wait for devices to attach. */
- wait_for_devices(drv);
-
- return 0;
-}
-EXPORT_SYMBOL_GPL(__xenbus_register_frontend);
+EXPORT_SYMBOL_GPL(xenbus_register_driver_common);
void xenbus_unregister_driver(struct xenbus_driver *drv)
{
@@ -551,24 +472,7 @@
kfree(xendev);
return err;
}
-
-/* device/<typename>/<name> */
-static int xenbus_probe_frontend(const char *type, const char *name)
-{
- char *nodename;
- int err;
-
- nodename = kasprintf(GFP_KERNEL, "%s/%s/%s",
- xenbus_frontend.root, type, name);
- if (!nodename)
- return -ENOMEM;
-
- DPRINTK("%s", nodename);
-
- err = xenbus_probe_node(&xenbus_frontend, type, nodename);
- kfree(nodename);
- return err;
-}
+EXPORT_SYMBOL_GPL(xenbus_probe_node);
static int xenbus_probe_device_type(struct xen_bus_type *bus, const char *type)
{
@@ -582,10 +486,11 @@
return PTR_ERR(dir);
for (i = 0; i < dir_n; i++) {
- err = bus->probe(type, dir[i]);
+ err = bus->probe(bus, type, dir[i]);
if (err)
break;
}
+
kfree(dir);
return err;
}
@@ -605,9 +510,11 @@
if (err)
break;
}
+
kfree(dir);
return err;
}
+EXPORT_SYMBOL_GPL(xenbus_probe_devices);
static unsigned int char_count(const char *str, char c)
{
@@ -670,32 +577,18 @@
}
EXPORT_SYMBOL_GPL(xenbus_dev_changed);
-static void frontend_changed(struct xenbus_watch *watch,
- const char **vec, unsigned int len)
-{
- DPRINTK("");
-
- xenbus_dev_changed(vec[XS_WATCH_PATH], &xenbus_frontend);
-}
-
-/* We watch for devices appearing and vanishing. */
-static struct xenbus_watch fe_watch = {
- .node = "device",
- .callback = frontend_changed,
-};
-
-static int xenbus_dev_suspend(struct device *dev, pm_message_t state)
+int xenbus_dev_suspend(struct device *dev, pm_message_t state)
{
int err = 0;
struct xenbus_driver *drv;
- struct xenbus_device *xdev;
+ struct xenbus_device *xdev
+ = container_of(dev, struct xenbus_device, dev);
- DPRINTK("");
+ DPRINTK("%s", xdev->nodename);
if (dev->driver == NULL)
return 0;
drv = to_xenbus_driver(dev->driver);
- xdev = container_of(dev, struct xenbus_device, dev);
if (drv->suspend)
err = drv->suspend(xdev, state);
if (err)
@@ -703,21 +596,20 @@
"xenbus: suspend %s failed: %i\n", dev_name(dev), err);
return 0;
}
+EXPORT_SYMBOL_GPL(xenbus_dev_suspend);
-static int xenbus_dev_resume(struct device *dev)
+int xenbus_dev_resume(struct device *dev)
{
int err;
struct xenbus_driver *drv;
- struct xenbus_device *xdev;
+ struct xenbus_device *xdev
+ = container_of(dev, struct xenbus_device, dev);
- DPRINTK("");
+ DPRINTK("%s", xdev->nodename);
if (dev->driver == NULL)
return 0;
-
drv = to_xenbus_driver(dev->driver);
- xdev = container_of(dev, struct xenbus_device, dev);
-
err = talk_to_otherend(xdev);
if (err) {
printk(KERN_WARNING
@@ -748,6 +640,7 @@
return 0;
}
+EXPORT_SYMBOL_GPL(xenbus_dev_resume);
/* A flag to determine if xenstored is 'ready' (i.e. has started) */
int xenstored_ready = 0;
@@ -776,11 +669,6 @@
{
xenstored_ready = 1;
- /* Enumerate devices in xenstore and watch for changes. */
- xenbus_probe_devices(&xenbus_frontend);
- register_xenbus_watch(&fe_watch);
- xenbus_backend_probe_and_watch();
-
/* Notify others that xenstore is up */
blocking_notifier_call_chain(&xenstore_chain, 0, NULL);
}
@@ -809,16 +697,7 @@
err = -ENODEV;
if (!xen_domain())
- goto out_error;
-
- /* Register ourselves with the kernel bus subsystem */
- err = bus_register(&xenbus_frontend.bus);
- if (err)
- goto out_error;
-
- err = xenbus_backend_bus_register();
- if (err)
- goto out_unreg_front;
+ return err;
/*
* Domain0 doesn't have a store_evtchn or store_mfn yet.
@@ -874,7 +753,7 @@
if (err) {
printk(KERN_WARNING
"XENBUS: Error initializing xenstore comms: %i\n", err);
- goto out_unreg_back;
+ goto out_error;
}
#ifdef CONFIG_XEN_COMPAT_XENFS
@@ -887,133 +766,13 @@
return 0;
- out_unreg_back:
- xenbus_backend_bus_unregister();
-
- out_unreg_front:
- bus_unregister(&xenbus_frontend.bus);
-
out_error:
if (page != 0)
free_page(page);
+
return err;
}
postcore_initcall(xenbus_init);
MODULE_LICENSE("GPL");
-
-static int is_device_connecting(struct device *dev, void *data)
-{
- struct xenbus_device *xendev = to_xenbus_device(dev);
- struct device_driver *drv = data;
- struct xenbus_driver *xendrv;
-
- /*
- * A device with no driver will never connect. We care only about
- * devices which should currently be in the process of connecting.
- */
- if (!dev->driver)
- return 0;
-
- /* Is this search limited to a particular driver? */
- if (drv && (dev->driver != drv))
- return 0;
-
- xendrv = to_xenbus_driver(dev->driver);
- return (xendev->state < XenbusStateConnected ||
- (xendev->state == XenbusStateConnected &&
- xendrv->is_ready && !xendrv->is_ready(xendev)));
-}
-
-static int exists_connecting_device(struct device_driver *drv)
-{
- return bus_for_each_dev(&xenbus_frontend.bus, NULL, drv,
- is_device_connecting);
-}
-
-static int print_device_status(struct device *dev, void *data)
-{
- struct xenbus_device *xendev = to_xenbus_device(dev);
- struct device_driver *drv = data;
-
- /* Is this operation limited to a particular driver? */
- if (drv && (dev->driver != drv))
- return 0;
-
- if (!dev->driver) {
- /* Information only: is this too noisy? */
- printk(KERN_INFO "XENBUS: Device with no driver: %s\n",
- xendev->nodename);
- } else if (xendev->state < XenbusStateConnected) {
- enum xenbus_state rstate = XenbusStateUnknown;
- if (xendev->otherend)
- rstate = xenbus_read_driver_state(xendev->otherend);
- printk(KERN_WARNING "XENBUS: Timeout connecting "
- "to device: %s (local state %d, remote state %d)\n",
- xendev->nodename, xendev->state, rstate);
- }
-
- return 0;
-}
-
-/* We only wait for device setup after most initcalls have run. */
-static int ready_to_wait_for_devices;
-
-/*
- * On a 5-minute timeout, wait for all devices currently configured. We need
- * to do this to guarantee that the filesystems and / or network devices
- * needed for boot are available, before we can allow the boot to proceed.
- *
- * This needs to be on a late_initcall, to happen after the frontend device
- * drivers have been initialised, but before the root fs is mounted.
- *
- * A possible improvement here would be to have the tools add a per-device
- * flag to the store entry, indicating whether it is needed at boot time.
- * This would allow people who knew what they were doing to accelerate their
- * boot slightly, but of course needs tools or manual intervention to set up
- * those flags correctly.
- */
-static void wait_for_devices(struct xenbus_driver *xendrv)
-{
- unsigned long start = jiffies;
- struct device_driver *drv = xendrv ? &xendrv->driver : NULL;
- unsigned int seconds_waited = 0;
-
- if (!ready_to_wait_for_devices || !xen_domain())
- return;
-
- while (exists_connecting_device(drv)) {
- if (time_after(jiffies, start + (seconds_waited+5)*HZ)) {
- if (!seconds_waited)
- printk(KERN_WARNING "XENBUS: Waiting for "
- "devices to initialise: ");
- seconds_waited += 5;
- printk("%us...", 300 - seconds_waited);
- if (seconds_waited == 300)
- break;
- }
-
- schedule_timeout_interruptible(HZ/10);
- }
-
- if (seconds_waited)
- printk("\n");
-
- bus_for_each_dev(&xenbus_frontend.bus, NULL, drv,
- print_device_status);
-}
-
-#ifndef MODULE
-static int __init boot_wait_for_devices(void)
-{
- if (xen_hvm_domain() && !xen_platform_pci_unplug)
- return -ENODEV;
-
- ready_to_wait_for_devices = 1;
- wait_for_devices(NULL);
- return 0;
-}
-
-late_initcall(boot_wait_for_devices);
-#endif
diff --git a/drivers/xen/xenbus/xenbus_probe.h b/drivers/xen/xenbus/xenbus_probe.h
index 6c5e318..2466581 100644
--- a/drivers/xen/xenbus/xenbus_probe.h
+++ b/drivers/xen/xenbus/xenbus_probe.h
@@ -36,26 +36,15 @@
#define XEN_BUS_ID_SIZE 20
-#ifdef CONFIG_XEN_BACKEND
-extern void xenbus_backend_suspend(int (*fn)(struct device *, void *));
-extern void xenbus_backend_resume(int (*fn)(struct device *, void *));
-extern void xenbus_backend_probe_and_watch(void);
-extern int xenbus_backend_bus_register(void);
-extern void xenbus_backend_bus_unregister(void);
-#else
-static inline void xenbus_backend_suspend(int (*fn)(struct device *, void *)) {}
-static inline void xenbus_backend_resume(int (*fn)(struct device *, void *)) {}
-static inline void xenbus_backend_probe_and_watch(void) {}
-static inline int xenbus_backend_bus_register(void) { return 0; }
-static inline void xenbus_backend_bus_unregister(void) {}
-#endif
-
struct xen_bus_type
{
char *root;
unsigned int levels;
int (*get_bus_id)(char bus_id[XEN_BUS_ID_SIZE], const char *nodename);
- int (*probe)(const char *type, const char *dir);
+ int (*probe)(struct xen_bus_type *bus, const char *type,
+ const char *dir);
+ void (*otherend_changed)(struct xenbus_watch *watch, const char **vec,
+ unsigned int len);
struct bus_type bus;
};
@@ -73,4 +62,16 @@
extern void xenbus_dev_changed(const char *node, struct xen_bus_type *bus);
+extern void xenbus_dev_shutdown(struct device *_dev);
+
+extern int xenbus_dev_suspend(struct device *dev, pm_message_t state);
+extern int xenbus_dev_resume(struct device *dev);
+
+extern void xenbus_otherend_changed(struct xenbus_watch *watch,
+ const char **vec, unsigned int len,
+ int ignore_on_shutdown);
+
+extern int xenbus_read_otherend_details(struct xenbus_device *xendev,
+ char *id_node, char *path_node);
+
#endif
diff --git a/drivers/xen/xenbus/xenbus_probe_backend.c b/drivers/xen/xenbus/xenbus_probe_backend.c
new file mode 100644
index 0000000..6cf467b
--- /dev/null
+++ b/drivers/xen/xenbus/xenbus_probe_backend.c
@@ -0,0 +1,276 @@
+/******************************************************************************
+ * Talks to Xen Store to figure out what devices we have (backend half).
+ *
+ * Copyright (C) 2005 Rusty Russell, IBM Corporation
+ * Copyright (C) 2005 Mike Wray, Hewlett-Packard
+ * Copyright (C) 2005, 2006 XenSource Ltd
+ * Copyright (C) 2007 Solarflare Communications, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License version 2
+ * as published by the Free Software Foundation; or, when distributed
+ * separately from the Linux kernel or incorporated into other
+ * software packages, subject to the following license:
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this source file (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy, modify,
+ * merge, publish, distribute, sublicense, and/or sell copies of the Software,
+ * and to permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
+
+#define DPRINTK(fmt, args...) \
+ pr_debug("xenbus_probe (%s:%d) " fmt ".\n", \
+ __func__, __LINE__, ##args)
+
+#include <linux/kernel.h>
+#include <linux/err.h>
+#include <linux/string.h>
+#include <linux/ctype.h>
+#include <linux/fcntl.h>
+#include <linux/mm.h>
+#include <linux/notifier.h>
+
+#include <asm/page.h>
+#include <asm/pgtable.h>
+#include <asm/xen/hypervisor.h>
+#include <asm/hypervisor.h>
+#include <xen/xenbus.h>
+#include <xen/features.h>
+
+#include "xenbus_comms.h"
+#include "xenbus_probe.h"
+
+/* backend/<type>/<fe-uuid>/<id> => <type>-<fe-domid>-<id> */
+static int backend_bus_id(char bus_id[XEN_BUS_ID_SIZE], const char *nodename)
+{
+ int domid, err;
+ const char *devid, *type, *frontend;
+ unsigned int typelen;
+
+ type = strchr(nodename, '/');
+ if (!type)
+ return -EINVAL;
+ type++;
+ typelen = strcspn(type, "/");
+ if (!typelen || type[typelen] != '/')
+ return -EINVAL;
+
+ devid = strrchr(nodename, '/') + 1;
+
+ err = xenbus_gather(XBT_NIL, nodename, "frontend-id", "%i", &domid,
+ "frontend", NULL, &frontend,
+ NULL);
+ if (err)
+ return err;
+ if (strlen(frontend) == 0)
+ err = -ERANGE;
+ if (!err && !xenbus_exists(XBT_NIL, frontend, ""))
+ err = -ENOENT;
+ kfree(frontend);
+
+ if (err)
+ return err;
+
+ if (snprintf(bus_id, XEN_BUS_ID_SIZE, "%.*s-%i-%s",
+ typelen, type, domid, devid) >= XEN_BUS_ID_SIZE)
+ return -ENOSPC;
+ return 0;
+}
+
+static int xenbus_uevent_backend(struct device *dev,
+ struct kobj_uevent_env *env)
+{
+ struct xenbus_device *xdev;
+ struct xenbus_driver *drv;
+ struct xen_bus_type *bus;
+
+ DPRINTK("");
+
+ if (dev == NULL)
+ return -ENODEV;
+
+ xdev = to_xenbus_device(dev);
+ bus = container_of(xdev->dev.bus, struct xen_bus_type, bus);
+ if (xdev == NULL)
+ return -ENODEV;
+
+ /* stuff we want to pass to /sbin/hotplug */
+ if (add_uevent_var(env, "XENBUS_TYPE=%s", xdev->devicetype))
+ return -ENOMEM;
+
+ if (add_uevent_var(env, "XENBUS_PATH=%s", xdev->nodename))
+ return -ENOMEM;
+
+ if (add_uevent_var(env, "XENBUS_BASE_PATH=%s", bus->root))
+ return -ENOMEM;
+
+ if (dev->driver) {
+ drv = to_xenbus_driver(dev->driver);
+ if (drv && drv->uevent)
+ return drv->uevent(xdev, env);
+ }
+
+ return 0;
+}
+
+/* backend/<typename>/<frontend-uuid>/<name> */
+static int xenbus_probe_backend_unit(struct xen_bus_type *bus,
+ const char *dir,
+ const char *type,
+ const char *name)
+{
+ char *nodename;
+ int err;
+
+ nodename = kasprintf(GFP_KERNEL, "%s/%s", dir, name);
+ if (!nodename)
+ return -ENOMEM;
+
+ DPRINTK("%s\n", nodename);
+
+ err = xenbus_probe_node(bus, type, nodename);
+ kfree(nodename);
+ return err;
+}
+
+/* backend/<typename>/<frontend-domid> */
+static int xenbus_probe_backend(struct xen_bus_type *bus, const char *type,
+ const char *domid)
+{
+ char *nodename;
+ int err = 0;
+ char **dir;
+ unsigned int i, dir_n = 0;
+
+ DPRINTK("");
+
+ nodename = kasprintf(GFP_KERNEL, "%s/%s/%s", bus->root, type, domid);
+ if (!nodename)
+ return -ENOMEM;
+
+ dir = xenbus_directory(XBT_NIL, nodename, "", &dir_n);
+ if (IS_ERR(dir)) {
+ kfree(nodename);
+ return PTR_ERR(dir);
+ }
+
+ for (i = 0; i < dir_n; i++) {
+ err = xenbus_probe_backend_unit(bus, nodename, type, dir[i]);
+ if (err)
+ break;
+ }
+ kfree(dir);
+ kfree(nodename);
+ return err;
+}
+
+static void frontend_changed(struct xenbus_watch *watch,
+ const char **vec, unsigned int len)
+{
+ xenbus_otherend_changed(watch, vec, len, 0);
+}
+
+static struct device_attribute xenbus_backend_dev_attrs[] = {
+ __ATTR_NULL
+};
+
+static struct xen_bus_type xenbus_backend = {
+ .root = "backend",
+ .levels = 3, /* backend/type/<frontend>/<id> */
+ .get_bus_id = backend_bus_id,
+ .probe = xenbus_probe_backend,
+ .otherend_changed = frontend_changed,
+ .bus = {
+ .name = "xen-backend",
+ .match = xenbus_match,
+ .uevent = xenbus_uevent_backend,
+ .probe = xenbus_dev_probe,
+ .remove = xenbus_dev_remove,
+ .shutdown = xenbus_dev_shutdown,
+ .dev_attrs = xenbus_backend_dev_attrs,
+ },
+};
+
+static void backend_changed(struct xenbus_watch *watch,
+ const char **vec, unsigned int len)
+{
+ DPRINTK("");
+
+ xenbus_dev_changed(vec[XS_WATCH_PATH], &xenbus_backend);
+}
+
+static struct xenbus_watch be_watch = {
+ .node = "backend",
+ .callback = backend_changed,
+};
+
+static int read_frontend_details(struct xenbus_device *xendev)
+{
+ return xenbus_read_otherend_details(xendev, "frontend-id", "frontend");
+}
+
+int xenbus_dev_is_online(struct xenbus_device *dev)
+{
+ int rc, val;
+
+ rc = xenbus_scanf(XBT_NIL, dev->nodename, "online", "%d", &val);
+ if (rc != 1)
+ val = 0; /* no online node present */
+
+ return val;
+}
+EXPORT_SYMBOL_GPL(xenbus_dev_is_online);
+
+int __xenbus_register_backend(struct xenbus_driver *drv,
+ struct module *owner, const char *mod_name)
+{
+ drv->read_otherend_details = read_frontend_details;
+
+ return xenbus_register_driver_common(drv, &xenbus_backend,
+ owner, mod_name);
+}
+EXPORT_SYMBOL_GPL(__xenbus_register_backend);
+
+static int backend_probe_and_watch(struct notifier_block *notifier,
+ unsigned long event,
+ void *data)
+{
+ /* Enumerate devices in xenstore and watch for changes. */
+ xenbus_probe_devices(&xenbus_backend);
+ register_xenbus_watch(&be_watch);
+
+ return NOTIFY_DONE;
+}
+
+static int __init xenbus_probe_backend_init(void)
+{
+ static struct notifier_block xenstore_notifier = {
+ .notifier_call = backend_probe_and_watch
+ };
+ int err;
+
+ DPRINTK("");
+
+ /* Register ourselves with the kernel bus subsystem */
+ err = bus_register(&xenbus_backend.bus);
+ if (err)
+ return err;
+
+ register_xenstore_notifier(&xenstore_notifier);
+
+ return 0;
+}
+subsys_initcall(xenbus_probe_backend_init);
diff --git a/drivers/xen/xenbus/xenbus_probe_frontend.c b/drivers/xen/xenbus/xenbus_probe_frontend.c
new file mode 100644
index 0000000..5bcc2d6
--- /dev/null
+++ b/drivers/xen/xenbus/xenbus_probe_frontend.c
@@ -0,0 +1,294 @@
+#define DPRINTK(fmt, args...) \
+ pr_debug("xenbus_probe (%s:%d) " fmt ".\n", \
+ __func__, __LINE__, ##args)
+
+#include <linux/kernel.h>
+#include <linux/err.h>
+#include <linux/string.h>
+#include <linux/ctype.h>
+#include <linux/fcntl.h>
+#include <linux/mm.h>
+#include <linux/proc_fs.h>
+#include <linux/notifier.h>
+#include <linux/kthread.h>
+#include <linux/mutex.h>
+#include <linux/io.h>
+
+#include <asm/page.h>
+#include <asm/pgtable.h>
+#include <asm/xen/hypervisor.h>
+#include <xen/xenbus.h>
+#include <xen/events.h>
+#include <xen/page.h>
+
+#include <xen/platform_pci.h>
+
+#include "xenbus_comms.h"
+#include "xenbus_probe.h"
+
+
+/* device/<type>/<id> => <type>-<id> */
+static int frontend_bus_id(char bus_id[XEN_BUS_ID_SIZE], const char *nodename)
+{
+ nodename = strchr(nodename, '/');
+ if (!nodename || strlen(nodename + 1) >= XEN_BUS_ID_SIZE) {
+ printk(KERN_WARNING "XENBUS: bad frontend %s\n", nodename);
+ return -EINVAL;
+ }
+
+ strlcpy(bus_id, nodename + 1, XEN_BUS_ID_SIZE);
+ if (!strchr(bus_id, '/')) {
+ printk(KERN_WARNING "XENBUS: bus_id %s no slash\n", bus_id);
+ return -EINVAL;
+ }
+ *strchr(bus_id, '/') = '-';
+ return 0;
+}
+
+/* device/<typename>/<name> */
+static int xenbus_probe_frontend(struct xen_bus_type *bus, const char *type,
+ const char *name)
+{
+ char *nodename;
+ int err;
+
+ nodename = kasprintf(GFP_KERNEL, "%s/%s/%s", bus->root, type, name);
+ if (!nodename)
+ return -ENOMEM;
+
+ DPRINTK("%s", nodename);
+
+ err = xenbus_probe_node(bus, type, nodename);
+ kfree(nodename);
+ return err;
+}
+
+static int xenbus_uevent_frontend(struct device *_dev,
+ struct kobj_uevent_env *env)
+{
+ struct xenbus_device *dev = to_xenbus_device(_dev);
+
+ if (add_uevent_var(env, "MODALIAS=xen:%s", dev->devicetype))
+ return -ENOMEM;
+
+ return 0;
+}
+
+
+static void backend_changed(struct xenbus_watch *watch,
+ const char **vec, unsigned int len)
+{
+ xenbus_otherend_changed(watch, vec, len, 1);
+}
+
+static struct device_attribute xenbus_frontend_dev_attrs[] = {
+ __ATTR_NULL
+};
+
+static struct xen_bus_type xenbus_frontend = {
+ .root = "device",
+ .levels = 2, /* device/type/<id> */
+ .get_bus_id = frontend_bus_id,
+ .probe = xenbus_probe_frontend,
+ .otherend_changed = backend_changed,
+ .bus = {
+ .name = "xen",
+ .match = xenbus_match,
+ .uevent = xenbus_uevent_frontend,
+ .probe = xenbus_dev_probe,
+ .remove = xenbus_dev_remove,
+ .shutdown = xenbus_dev_shutdown,
+ .dev_attrs = xenbus_frontend_dev_attrs,
+
+ .suspend = xenbus_dev_suspend,
+ .resume = xenbus_dev_resume,
+ },
+};
+
+static void frontend_changed(struct xenbus_watch *watch,
+ const char **vec, unsigned int len)
+{
+ DPRINTK("");
+
+ xenbus_dev_changed(vec[XS_WATCH_PATH], &xenbus_frontend);
+}
+
+
+/* We watch for devices appearing and vanishing. */
+static struct xenbus_watch fe_watch = {
+ .node = "device",
+ .callback = frontend_changed,
+};
+
+static int read_backend_details(struct xenbus_device *xendev)
+{
+ return xenbus_read_otherend_details(xendev, "backend-id", "backend");
+}
+
+static int is_device_connecting(struct device *dev, void *data)
+{
+ struct xenbus_device *xendev = to_xenbus_device(dev);
+ struct device_driver *drv = data;
+ struct xenbus_driver *xendrv;
+
+ /*
+ * A device with no driver will never connect. We care only about
+ * devices which should currently be in the process of connecting.
+ */
+ if (!dev->driver)
+ return 0;
+
+ /* Is this search limited to a particular driver? */
+ if (drv && (dev->driver != drv))
+ return 0;
+
+ xendrv = to_xenbus_driver(dev->driver);
+ return (xendev->state < XenbusStateConnected ||
+ (xendev->state == XenbusStateConnected &&
+ xendrv->is_ready && !xendrv->is_ready(xendev)));
+}
+
+static int exists_connecting_device(struct device_driver *drv)
+{
+ return bus_for_each_dev(&xenbus_frontend.bus, NULL, drv,
+ is_device_connecting);
+}
+
+static int print_device_status(struct device *dev, void *data)
+{
+ struct xenbus_device *xendev = to_xenbus_device(dev);
+ struct device_driver *drv = data;
+
+ /* Is this operation limited to a particular driver? */
+ if (drv && (dev->driver != drv))
+ return 0;
+
+ if (!dev->driver) {
+ /* Information only: is this too noisy? */
+ printk(KERN_INFO "XENBUS: Device with no driver: %s\n",
+ xendev->nodename);
+ } else if (xendev->state < XenbusStateConnected) {
+ enum xenbus_state rstate = XenbusStateUnknown;
+ if (xendev->otherend)
+ rstate = xenbus_read_driver_state(xendev->otherend);
+ printk(KERN_WARNING "XENBUS: Timeout connecting "
+ "to device: %s (local state %d, remote state %d)\n",
+ xendev->nodename, xendev->state, rstate);
+ }
+
+ return 0;
+}
+
+/* We only wait for device setup after most initcalls have run. */
+static int ready_to_wait_for_devices;
+
+/*
+ * On a 5-minute timeout, wait for all devices currently configured. We need
+ * to do this to guarantee that the filesystems and / or network devices
+ * needed for boot are available, before we can allow the boot to proceed.
+ *
+ * This needs to be on a late_initcall, to happen after the frontend device
+ * drivers have been initialised, but before the root fs is mounted.
+ *
+ * A possible improvement here would be to have the tools add a per-device
+ * flag to the store entry, indicating whether it is needed at boot time.
+ * This would allow people who knew what they were doing to accelerate their
+ * boot slightly, but of course needs tools or manual intervention to set up
+ * those flags correctly.
+ */
+static void wait_for_devices(struct xenbus_driver *xendrv)
+{
+ unsigned long start = jiffies;
+ struct device_driver *drv = xendrv ? &xendrv->driver : NULL;
+ unsigned int seconds_waited = 0;
+
+ if (!ready_to_wait_for_devices || !xen_domain())
+ return;
+
+ while (exists_connecting_device(drv)) {
+ if (time_after(jiffies, start + (seconds_waited+5)*HZ)) {
+ if (!seconds_waited)
+ printk(KERN_WARNING "XENBUS: Waiting for "
+ "devices to initialise: ");
+ seconds_waited += 5;
+ printk("%us...", 300 - seconds_waited);
+ if (seconds_waited == 300)
+ break;
+ }
+
+ schedule_timeout_interruptible(HZ/10);
+ }
+
+ if (seconds_waited)
+ printk("\n");
+
+ bus_for_each_dev(&xenbus_frontend.bus, NULL, drv,
+ print_device_status);
+}
+
+int __xenbus_register_frontend(struct xenbus_driver *drv,
+ struct module *owner, const char *mod_name)
+{
+ int ret;
+
+ drv->read_otherend_details = read_backend_details;
+
+ ret = xenbus_register_driver_common(drv, &xenbus_frontend,
+ owner, mod_name);
+ if (ret)
+ return ret;
+
+ /* If this driver is loaded as a module wait for devices to attach. */
+ wait_for_devices(drv);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(__xenbus_register_frontend);
+
+static int frontend_probe_and_watch(struct notifier_block *notifier,
+ unsigned long event,
+ void *data)
+{
+ /* Enumerate devices in xenstore and watch for changes. */
+ xenbus_probe_devices(&xenbus_frontend);
+ register_xenbus_watch(&fe_watch);
+
+ return NOTIFY_DONE;
+}
+
+
+static int __init xenbus_probe_frontend_init(void)
+{
+ static struct notifier_block xenstore_notifier = {
+ .notifier_call = frontend_probe_and_watch
+ };
+ int err;
+
+ DPRINTK("");
+
+ /* Register ourselves with the kernel bus subsystem */
+ err = bus_register(&xenbus_frontend.bus);
+ if (err)
+ return err;
+
+ register_xenstore_notifier(&xenstore_notifier);
+
+ return 0;
+}
+subsys_initcall(xenbus_probe_frontend_init);
+
+#ifndef MODULE
+static int __init boot_wait_for_devices(void)
+{
+ if (xen_hvm_domain() && !xen_platform_pci_unplug)
+ return -ENODEV;
+
+ ready_to_wait_for_devices = 1;
+ wait_for_devices(NULL);
+ return 0;
+}
+
+late_initcall(boot_wait_for_devices);
+#endif
+
+MODULE_LICENSE("GPL");
diff --git a/firmware/ihex2fw.c b/firmware/ihex2fw.c
index ba0cf0b..cf38e15 100644
--- a/firmware/ihex2fw.c
+++ b/firmware/ihex2fw.c
@@ -124,8 +124,7 @@
if (process_ihex(data, st.st_size))
return 1;
- output_records(outfd);
- return 0;
+ return output_records(outfd);
}
static int process_ihex(uint8_t *data, ssize_t size)
@@ -269,11 +268,13 @@
p->addr = htonl(p->addr);
p->len = htons(p->len);
- write(outfd, &p->addr, writelen);
+ if (write(outfd, &p->addr, writelen) != writelen)
+ return 1;
p = p->next;
}
/* EOF record is zero length, since we don't bother to represent
the type field in the binary version */
- write(outfd, zeroes, 6);
+ if (write(outfd, zeroes, 6) != 6)
+ return 1;
return 0;
}
diff --git a/fs/aio.c b/fs/aio.c
index 8c8f6c5..5e00f15 100644
--- a/fs/aio.c
+++ b/fs/aio.c
@@ -798,29 +798,12 @@
queue_delayed_work(aio_wq, &ctx->wq, timeout);
}
-
/*
- * aio_run_iocbs:
- * Process all pending retries queued on the ioctx
- * run list.
- * Assumes it is operating within the aio issuer's mm
- * context.
- */
-static inline void aio_run_iocbs(struct kioctx *ctx)
-{
- int requeue;
-
- spin_lock_irq(&ctx->ctx_lock);
-
- requeue = __aio_run_iocbs(ctx);
- spin_unlock_irq(&ctx->ctx_lock);
- if (requeue)
- aio_queue_work(ctx);
-}
-
-/*
- * just like aio_run_iocbs, but keeps running them until
- * the list stays empty
+ * aio_run_all_iocbs:
+ * Process all pending retries queued on the ioctx
+ * run list, and keep running them until the list
+ * stays empty.
+ * Assumes it is operating within the aio issuer's mm context.
*/
static inline void aio_run_all_iocbs(struct kioctx *ctx)
{
@@ -1839,7 +1822,7 @@
long ret = -EINVAL;
if (likely(ioctx)) {
- if (likely(min_nr <= nr && min_nr >= 0 && nr >= 0))
+ if (likely(min_nr <= nr && min_nr >= 0))
ret = read_events(ioctx, min_nr, nr, events, timeout);
put_ioctx(ioctx);
}
diff --git a/fs/anon_inodes.c b/fs/anon_inodes.c
index 549a53c..cbe57f3 100644
--- a/fs/anon_inodes.c
+++ b/fs/anon_inodes.c
@@ -66,9 +66,9 @@
};
/**
- * anon_inode_getfd - creates a new file instance by hooking it up to an
- * anonymous inode, and a dentry that describe the "class"
- * of the file
+ * anon_inode_getfile - creates a new file instance by hooking it up to an
+ * anonymous inode, and a dentry that describe the "class"
+ * of the file
*
* @name: [in] name of the "class" of the new file
* @fops: [in] file operations for the new file
diff --git a/fs/befs/endian.h b/fs/befs/endian.h
index 6cb84d8..2722387 100644
--- a/fs/befs/endian.h
+++ b/fs/befs/endian.h
@@ -102,22 +102,22 @@
}
static inline befs_data_stream
-fsds_to_cpu(const struct super_block *sb, befs_disk_data_stream n)
+fsds_to_cpu(const struct super_block *sb, const befs_disk_data_stream *n)
{
befs_data_stream data;
int i;
for (i = 0; i < BEFS_NUM_DIRECT_BLOCKS; ++i)
- data.direct[i] = fsrun_to_cpu(sb, n.direct[i]);
+ data.direct[i] = fsrun_to_cpu(sb, n->direct[i]);
- data.max_direct_range = fs64_to_cpu(sb, n.max_direct_range);
- data.indirect = fsrun_to_cpu(sb, n.indirect);
- data.max_indirect_range = fs64_to_cpu(sb, n.max_indirect_range);
- data.double_indirect = fsrun_to_cpu(sb, n.double_indirect);
+ data.max_direct_range = fs64_to_cpu(sb, n->max_direct_range);
+ data.indirect = fsrun_to_cpu(sb, n->indirect);
+ data.max_indirect_range = fs64_to_cpu(sb, n->max_indirect_range);
+ data.double_indirect = fsrun_to_cpu(sb, n->double_indirect);
data.max_double_indirect_range = fs64_to_cpu(sb,
- n.
+ n->
max_double_indirect_range);
- data.size = fs64_to_cpu(sb, n.size);
+ data.size = fs64_to_cpu(sb, n->size);
return data;
}
diff --git a/fs/befs/linuxvfs.c b/fs/befs/linuxvfs.c
index de93581..b1d0c79 100644
--- a/fs/befs/linuxvfs.c
+++ b/fs/befs/linuxvfs.c
@@ -390,7 +390,7 @@
int num_blks;
befs_ino->i_data.ds =
- fsds_to_cpu(sb, raw_inode->data.datastream);
+ fsds_to_cpu(sb, &raw_inode->data.datastream);
num_blks = befs_count_blocks(sb, &befs_ino->i_data.ds);
inode->i_blocks =
diff --git a/fs/binfmt_elf.c b/fs/binfmt_elf.c
index 6884e19..d5b640b 100644
--- a/fs/binfmt_elf.c
+++ b/fs/binfmt_elf.c
@@ -66,12 +66,11 @@
#define ELF_PAGEALIGN(_v) (((_v) + ELF_MIN_ALIGN - 1) & ~(ELF_MIN_ALIGN - 1))
static struct linux_binfmt elf_format = {
- .module = THIS_MODULE,
- .load_binary = load_elf_binary,
- .load_shlib = load_elf_library,
- .core_dump = elf_core_dump,
- .min_coredump = ELF_EXEC_PAGESIZE,
- .hasvdso = 1
+ .module = THIS_MODULE,
+ .load_binary = load_elf_binary,
+ .load_shlib = load_elf_library,
+ .core_dump = elf_core_dump,
+ .min_coredump = ELF_EXEC_PAGESIZE,
};
#define BAD_ADDR(x) ((unsigned long)(x) >= TASK_SIZE)
@@ -316,8 +315,6 @@
return 0;
}
-#ifndef elf_map
-
static unsigned long elf_map(struct file *filep, unsigned long addr,
struct elf_phdr *eppnt, int prot, int type,
unsigned long total_size)
@@ -354,8 +351,6 @@
return(map_addr);
}
-#endif /* !elf_map */
-
static unsigned long total_mapping_size(struct elf_phdr *cmds, int nr)
{
int i, first_idx = -1, last_idx = -1;
@@ -421,7 +416,7 @@
goto out;
retval = kernel_read(interpreter, interp_elf_ex->e_phoff,
- (char *)elf_phdata,size);
+ (char *)elf_phdata, size);
error = -EIO;
if (retval != size) {
if (retval < 0)
@@ -601,7 +596,7 @@
goto out;
if (!elf_check_arch(&loc->elf_ex))
goto out;
- if (!bprm->file->f_op||!bprm->file->f_op->mmap)
+ if (!bprm->file->f_op || !bprm->file->f_op->mmap)
goto out;
/* Now read in all of the header information */
@@ -761,8 +756,8 @@
/* There was a PT_LOAD segment with p_memsz > p_filesz
before this one. Map anonymous pages, if needed,
and clear the area. */
- retval = set_brk (elf_bss + load_bias,
- elf_brk + load_bias);
+ retval = set_brk(elf_bss + load_bias,
+ elf_brk + load_bias);
if (retval) {
send_sig(SIGKILL, current, 0);
goto out_free_dentry;
diff --git a/fs/ceph/Makefile b/fs/ceph/Makefile
index 9e6c4f2..bd35212 100644
--- a/fs/ceph/Makefile
+++ b/fs/ceph/Makefile
@@ -2,31 +2,10 @@
# Makefile for CEPH filesystem.
#
-ifneq ($(KERNELRELEASE),)
-
obj-$(CONFIG_CEPH_FS) += ceph.o
-ceph-objs := super.o inode.o dir.o file.o locks.o addr.o ioctl.o \
+ceph-y := super.o inode.o dir.o file.o locks.o addr.o ioctl.o \
export.o caps.o snap.o xattr.o \
mds_client.o mdsmap.o strings.o ceph_frag.o \
debugfs.o
-else
-#Otherwise we were called directly from the command
-# line; invoke the kernel build system.
-
-KERNELDIR ?= /lib/modules/$(shell uname -r)/build
-PWD := $(shell pwd)
-
-default: all
-
-all:
- $(MAKE) -C $(KERNELDIR) M=$(PWD) CONFIG_CEPH_FS=m modules
-
-modules_install:
- $(MAKE) -C $(KERNELDIR) M=$(PWD) CONFIG_CEPH_FS=m modules_install
-
-clean:
- $(MAKE) -C $(KERNELDIR) M=$(PWD) clean
-
-endif
diff --git a/fs/ceph/debugfs.c b/fs/ceph/debugfs.c
index 7ae1b3d..08f65fa 100644
--- a/fs/ceph/debugfs.c
+++ b/fs/ceph/debugfs.c
@@ -60,10 +60,13 @@
for (rp = rb_first(&mdsc->request_tree); rp; rp = rb_next(rp)) {
req = rb_entry(rp, struct ceph_mds_request, r_node);
- if (req->r_request)
- seq_printf(s, "%lld\tmds%d\t", req->r_tid, req->r_mds);
- else
+ if (req->r_request && req->r_session)
+ seq_printf(s, "%lld\tmds%d\t", req->r_tid,
+ req->r_session->s_mds);
+ else if (!req->r_request)
seq_printf(s, "%lld\t(no request)\t", req->r_tid);
+ else
+ seq_printf(s, "%lld\t(no session)\t", req->r_tid);
seq_printf(s, "%s", ceph_mds_op_name(req->r_op));
diff --git a/fs/ceph/dir.c b/fs/ceph/dir.c
index fa7ca04..0bc68de 100644
--- a/fs/ceph/dir.c
+++ b/fs/ceph/dir.c
@@ -1224,6 +1224,26 @@
}
}
+/*
+ * Return name hash for a given dentry. This is dependent on
+ * the parent directory's hash function.
+ */
+unsigned ceph_dentry_hash(struct dentry *dn)
+{
+ struct inode *dir = dn->d_parent->d_inode;
+ struct ceph_inode_info *dci = ceph_inode(dir);
+
+ switch (dci->i_dir_layout.dl_dir_hash) {
+ case 0: /* for backward compat */
+ case CEPH_STR_HASH_LINUX:
+ return dn->d_name.hash;
+
+ default:
+ return ceph_str_hash(dci->i_dir_layout.dl_dir_hash,
+ dn->d_name.name, dn->d_name.len);
+ }
+}
+
const struct file_operations ceph_dir_fops = {
.read = ceph_read_dir,
.readdir = ceph_readdir,
diff --git a/fs/ceph/export.c b/fs/ceph/export.c
index 2297d94..e410561 100644
--- a/fs/ceph/export.c
+++ b/fs/ceph/export.c
@@ -59,7 +59,7 @@
dout("encode_fh %p connectable\n", dentry);
cfh->ino = ceph_ino(dentry->d_inode);
cfh->parent_ino = ceph_ino(parent->d_inode);
- cfh->parent_name_hash = parent->d_name.hash;
+ cfh->parent_name_hash = ceph_dentry_hash(parent);
*max_len = connected_handle_length;
type = 2;
} else if (*max_len >= handle_length) {
diff --git a/fs/ceph/inode.c b/fs/ceph/inode.c
index e61de4f..e835eff 100644
--- a/fs/ceph/inode.c
+++ b/fs/ceph/inode.c
@@ -297,6 +297,8 @@
ci->i_release_count = 0;
ci->i_symlink = NULL;
+ memset(&ci->i_dir_layout, 0, sizeof(ci->i_dir_layout));
+
ci->i_fragtree = RB_ROOT;
mutex_init(&ci->i_fragtree_mutex);
@@ -689,6 +691,8 @@
inode->i_op = &ceph_dir_iops;
inode->i_fop = &ceph_dir_fops;
+ ci->i_dir_layout = iinfo->dir_layout;
+
ci->i_files = le64_to_cpu(info->files);
ci->i_subdirs = le64_to_cpu(info->subdirs);
ci->i_rbytes = le64_to_cpu(info->rbytes);
diff --git a/fs/ceph/mds_client.c b/fs/ceph/mds_client.c
index a50fca1..1e30d19 100644
--- a/fs/ceph/mds_client.c
+++ b/fs/ceph/mds_client.c
@@ -60,7 +60,8 @@
* parse individual inode info
*/
static int parse_reply_info_in(void **p, void *end,
- struct ceph_mds_reply_info_in *info)
+ struct ceph_mds_reply_info_in *info,
+ int features)
{
int err = -EIO;
@@ -74,6 +75,12 @@
info->symlink = *p;
*p += info->symlink_len;
+ if (features & CEPH_FEATURE_DIRLAYOUTHASH)
+ ceph_decode_copy_safe(p, end, &info->dir_layout,
+ sizeof(info->dir_layout), bad);
+ else
+ memset(&info->dir_layout, 0, sizeof(info->dir_layout));
+
ceph_decode_32_safe(p, end, info->xattr_len, bad);
ceph_decode_need(p, end, info->xattr_len, bad);
info->xattr_data = *p;
@@ -88,12 +95,13 @@
* target inode.
*/
static int parse_reply_info_trace(void **p, void *end,
- struct ceph_mds_reply_info_parsed *info)
+ struct ceph_mds_reply_info_parsed *info,
+ int features)
{
int err;
if (info->head->is_dentry) {
- err = parse_reply_info_in(p, end, &info->diri);
+ err = parse_reply_info_in(p, end, &info->diri, features);
if (err < 0)
goto out_bad;
@@ -114,7 +122,7 @@
}
if (info->head->is_target) {
- err = parse_reply_info_in(p, end, &info->targeti);
+ err = parse_reply_info_in(p, end, &info->targeti, features);
if (err < 0)
goto out_bad;
}
@@ -134,7 +142,8 @@
* parse readdir results
*/
static int parse_reply_info_dir(void **p, void *end,
- struct ceph_mds_reply_info_parsed *info)
+ struct ceph_mds_reply_info_parsed *info,
+ int features)
{
u32 num, i = 0;
int err;
@@ -182,7 +191,7 @@
*p += sizeof(struct ceph_mds_reply_lease);
/* inode */
- err = parse_reply_info_in(p, end, &info->dir_in[i]);
+ err = parse_reply_info_in(p, end, &info->dir_in[i], features);
if (err < 0)
goto out_bad;
i++;
@@ -205,7 +214,8 @@
* parse fcntl F_GETLK results
*/
static int parse_reply_info_filelock(void **p, void *end,
- struct ceph_mds_reply_info_parsed *info)
+ struct ceph_mds_reply_info_parsed *info,
+ int features)
{
if (*p + sizeof(*info->filelock_reply) > end)
goto bad;
@@ -225,19 +235,21 @@
* parse extra results
*/
static int parse_reply_info_extra(void **p, void *end,
- struct ceph_mds_reply_info_parsed *info)
+ struct ceph_mds_reply_info_parsed *info,
+ int features)
{
if (info->head->op == CEPH_MDS_OP_GETFILELOCK)
- return parse_reply_info_filelock(p, end, info);
+ return parse_reply_info_filelock(p, end, info, features);
else
- return parse_reply_info_dir(p, end, info);
+ return parse_reply_info_dir(p, end, info, features);
}
/*
* parse entire mds reply
*/
static int parse_reply_info(struct ceph_msg *msg,
- struct ceph_mds_reply_info_parsed *info)
+ struct ceph_mds_reply_info_parsed *info,
+ int features)
{
void *p, *end;
u32 len;
@@ -250,7 +262,7 @@
/* trace */
ceph_decode_32_safe(&p, end, len, bad);
if (len > 0) {
- err = parse_reply_info_trace(&p, p+len, info);
+ err = parse_reply_info_trace(&p, p+len, info, features);
if (err < 0)
goto out_bad;
}
@@ -258,7 +270,7 @@
/* extra */
ceph_decode_32_safe(&p, end, len, bad);
if (len > 0) {
- err = parse_reply_info_extra(&p, p+len, info);
+ err = parse_reply_info_extra(&p, p+len, info, features);
if (err < 0)
goto out_bad;
}
@@ -654,7 +666,7 @@
} else {
/* dir + name */
inode = dir;
- hash = req->r_dentry->d_name.hash;
+ hash = ceph_dentry_hash(req->r_dentry);
is_hash = true;
}
}
@@ -1693,7 +1705,6 @@
struct ceph_msg *msg;
int flags = 0;
- req->r_mds = mds;
req->r_attempts++;
if (req->r_inode) {
struct ceph_cap *cap =
@@ -1780,6 +1791,8 @@
goto finish;
}
+ put_request_session(req);
+
mds = __choose_mds(mdsc, req);
if (mds < 0 ||
ceph_mdsmap_get_state(mdsc->mdsmap, mds) < CEPH_MDS_STATE_ACTIVE) {
@@ -1797,6 +1810,8 @@
goto finish;
}
}
+ req->r_session = get_session(session);
+
dout("do_request mds%d session %p state %s\n", mds, session,
session_state_name(session->s_state));
if (session->s_state != CEPH_MDS_SESSION_OPEN &&
@@ -1809,7 +1824,6 @@
}
/* send request */
- req->r_session = get_session(session);
req->r_resend_mds = -1; /* forget any previous mds hint */
if (req->r_request_started == 0) /* note request start time */
@@ -1863,7 +1877,6 @@
if (req->r_session &&
req->r_session->s_mds == mds) {
dout(" kicking tid %llu\n", req->r_tid);
- put_request_session(req);
__do_request(mdsc, req);
}
}
@@ -2056,8 +2069,11 @@
goto out;
} else {
struct ceph_inode_info *ci = ceph_inode(req->r_inode);
- struct ceph_cap *cap =
- ceph_get_cap_for_mds(ci, req->r_mds);;
+ struct ceph_cap *cap = NULL;
+
+ if (req->r_session)
+ cap = ceph_get_cap_for_mds(ci,
+ req->r_session->s_mds);
dout("already using auth");
if ((!cap || cap != ci->i_auth_cap) ||
@@ -2101,7 +2117,7 @@
dout("handle_reply tid %lld result %d\n", tid, result);
rinfo = &req->r_reply_info;
- err = parse_reply_info(msg, rinfo);
+ err = parse_reply_info(msg, rinfo, session->s_con.peer_features);
mutex_unlock(&mdsc->mutex);
mutex_lock(&session->s_mutex);
diff --git a/fs/ceph/mds_client.h b/fs/ceph/mds_client.h
index aabe563..4e3a9cc 100644
--- a/fs/ceph/mds_client.h
+++ b/fs/ceph/mds_client.h
@@ -35,6 +35,7 @@
*/
struct ceph_mds_reply_info_in {
struct ceph_mds_reply_inode *in;
+ struct ceph_dir_layout dir_layout;
u32 symlink_len;
char *symlink;
u32 xattr_len;
@@ -165,7 +166,6 @@
struct ceph_mds_client *r_mdsc;
int r_op; /* mds op code */
- int r_mds;
/* operation on what? */
struct inode *r_inode; /* arg1 */
diff --git a/fs/ceph/super.c b/fs/ceph/super.c
index 08b460a..bf6f0f3 100644
--- a/fs/ceph/super.c
+++ b/fs/ceph/super.c
@@ -428,7 +428,8 @@
goto fail;
}
fsc->client->extra_mon_dispatch = extra_mon_dispatch;
- fsc->client->supported_features |= CEPH_FEATURE_FLOCK;
+ fsc->client->supported_features |= CEPH_FEATURE_FLOCK |
+ CEPH_FEATURE_DIRLAYOUTHASH;
fsc->client->monc.want_mdsmap = 1;
fsc->mount_options = fsopt;
@@ -443,13 +444,17 @@
goto fail_client;
err = -ENOMEM;
- fsc->wb_wq = create_workqueue("ceph-writeback");
+ /*
+ * The number of concurrent works can be high but they don't need
+ * to be processed in parallel, limit concurrency.
+ */
+ fsc->wb_wq = alloc_workqueue("ceph-writeback", 0, 1);
if (fsc->wb_wq == NULL)
goto fail_bdi;
- fsc->pg_inv_wq = create_singlethread_workqueue("ceph-pg-invalid");
+ fsc->pg_inv_wq = alloc_workqueue("ceph-pg-invalid", 0, 1);
if (fsc->pg_inv_wq == NULL)
goto fail_wb_wq;
- fsc->trunc_wq = create_singlethread_workqueue("ceph-trunc");
+ fsc->trunc_wq = alloc_workqueue("ceph-trunc", 0, 1);
if (fsc->trunc_wq == NULL)
goto fail_pg_inv_wq;
diff --git a/fs/ceph/super.h b/fs/ceph/super.h
index 4553d88..20b907d 100644
--- a/fs/ceph/super.h
+++ b/fs/ceph/super.h
@@ -239,6 +239,7 @@
unsigned i_ceph_flags;
unsigned long i_release_count;
+ struct ceph_dir_layout i_dir_layout;
struct ceph_file_layout i_layout;
char *i_symlink;
@@ -768,6 +769,7 @@
extern void ceph_dentry_lru_touch(struct dentry *dn);
extern void ceph_dentry_lru_del(struct dentry *dn);
extern void ceph_invalidate_dentry_lease(struct dentry *dentry);
+extern unsigned ceph_dentry_hash(struct dentry *dn);
/*
* our d_ops vary depending on whether the inode is live,
diff --git a/fs/char_dev.c b/fs/char_dev.c
index e5b9df9..6e99b9d 100644
--- a/fs/char_dev.c
+++ b/fs/char_dev.c
@@ -417,18 +417,6 @@
return ret;
}
-int cdev_index(struct inode *inode)
-{
- int idx;
- struct kobject *kobj;
-
- kobj = kobj_lookup(cdev_map, inode->i_rdev, &idx);
- if (!kobj)
- return -1;
- kobject_put(kobj);
- return idx;
-}
-
void cd_forget(struct inode *inode)
{
spin_lock(&cdev_lock);
@@ -582,7 +570,6 @@
EXPORT_SYMBOL(cdev_alloc);
EXPORT_SYMBOL(cdev_del);
EXPORT_SYMBOL(cdev_add);
-EXPORT_SYMBOL(cdev_index);
EXPORT_SYMBOL(__register_chrdev);
EXPORT_SYMBOL(__unregister_chrdev);
EXPORT_SYMBOL(directly_mappable_cdev_bdi);
diff --git a/fs/coda/inode.c b/fs/coda/inode.c
index 261d86f..871b277 100644
--- a/fs/coda/inode.c
+++ b/fs/coda/inode.c
@@ -44,7 +44,7 @@
static struct inode *coda_alloc_inode(struct super_block *sb)
{
struct coda_inode_info *ei;
- ei = (struct coda_inode_info *)kmem_cache_alloc(coda_inode_cachep, GFP_KERNEL);
+ ei = kmem_cache_alloc(coda_inode_cachep, GFP_KERNEL);
if (!ei)
return NULL;
memset(&ei->c_fid, 0, sizeof(struct CodaFid));
diff --git a/fs/cramfs/inode.c b/fs/cramfs/inode.c
index 32fd5fe..e141939 100644
--- a/fs/cramfs/inode.c
+++ b/fs/cramfs/inode.c
@@ -34,57 +34,81 @@
static DEFINE_MUTEX(read_mutex);
-/* These two macros may change in future, to provide better st_ino
- semantics. */
-#define CRAMINO(x) (((x)->offset && (x)->size)?(x)->offset<<2:1)
+/* These macros may change in future, to provide better st_ino semantics. */
#define OFFSET(x) ((x)->i_ino)
-static void setup_inode(struct inode *inode, struct cramfs_inode * cramfs_inode)
+static unsigned long cramino(struct cramfs_inode *cino, unsigned int offset)
{
+ if (!cino->offset)
+ return offset + 1;
+ if (!cino->size)
+ return offset + 1;
+
+ /*
+ * The file mode test fixes buggy mkcramfs implementations where
+ * cramfs_inode->offset is set to a non zero value for entries
+ * which did not contain data, like devices node and fifos.
+ */
+ switch (cino->mode & S_IFMT) {
+ case S_IFREG:
+ case S_IFDIR:
+ case S_IFLNK:
+ return cino->offset << 2;
+ default:
+ break;
+ }
+ return offset + 1;
+}
+
+static struct inode *get_cramfs_inode(struct super_block *sb,
+ struct cramfs_inode *cramfs_inode, unsigned int offset)
+{
+ struct inode *inode;
static struct timespec zerotime;
+
+ inode = iget_locked(sb, cramino(cramfs_inode, offset));
+ if (!inode)
+ return ERR_PTR(-ENOMEM);
+ if (!(inode->i_state & I_NEW))
+ return inode;
+
+ switch (cramfs_inode->mode & S_IFMT) {
+ case S_IFREG:
+ inode->i_fop = &generic_ro_fops;
+ inode->i_data.a_ops = &cramfs_aops;
+ break;
+ case S_IFDIR:
+ inode->i_op = &cramfs_dir_inode_operations;
+ inode->i_fop = &cramfs_directory_operations;
+ break;
+ case S_IFLNK:
+ inode->i_op = &page_symlink_inode_operations;
+ inode->i_data.a_ops = &cramfs_aops;
+ break;
+ default:
+ init_special_inode(inode, cramfs_inode->mode,
+ old_decode_dev(cramfs_inode->size));
+ }
+
inode->i_mode = cramfs_inode->mode;
inode->i_uid = cramfs_inode->uid;
- inode->i_size = cramfs_inode->size;
- inode->i_blocks = (cramfs_inode->size - 1) / 512 + 1;
inode->i_gid = cramfs_inode->gid;
+
+ /* if the lower 2 bits are zero, the inode contains data */
+ if (!(inode->i_ino & 3)) {
+ inode->i_size = cramfs_inode->size;
+ inode->i_blocks = (cramfs_inode->size - 1) / 512 + 1;
+ }
+
/* Struct copy intentional */
inode->i_mtime = inode->i_atime = inode->i_ctime = zerotime;
/* inode->i_nlink is left 1 - arguably wrong for directories,
but it's the best we can do without reading the directory
contents. 1 yields the right result in GNU find, even
without -noleaf option. */
- if (S_ISREG(inode->i_mode)) {
- inode->i_fop = &generic_ro_fops;
- inode->i_data.a_ops = &cramfs_aops;
- } else if (S_ISDIR(inode->i_mode)) {
- inode->i_op = &cramfs_dir_inode_operations;
- inode->i_fop = &cramfs_directory_operations;
- } else if (S_ISLNK(inode->i_mode)) {
- inode->i_op = &page_symlink_inode_operations;
- inode->i_data.a_ops = &cramfs_aops;
- } else {
- init_special_inode(inode, inode->i_mode,
- old_decode_dev(cramfs_inode->size));
- }
-}
-static struct inode *get_cramfs_inode(struct super_block *sb,
- struct cramfs_inode * cramfs_inode)
-{
- struct inode *inode;
- if (CRAMINO(cramfs_inode) == 1) {
- inode = new_inode(sb);
- if (inode) {
- inode->i_ino = 1;
- setup_inode(inode, cramfs_inode);
- }
- } else {
- inode = iget_locked(sb, CRAMINO(cramfs_inode));
- if (inode && (inode->i_state & I_NEW)) {
- setup_inode(inode, cramfs_inode);
- unlock_new_inode(inode);
- }
- }
+ unlock_new_inode(inode);
+
return inode;
}
@@ -265,6 +289,9 @@
printk(KERN_ERR "cramfs: root is not a directory\n");
goto out;
}
+ /* correct strange, hard-coded permissions of mkcramfs */
+ super.root.mode |= (S_IRUSR | S_IXUSR | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH);
+
root_offset = super.root.offset << 2;
if (super.flags & CRAMFS_FLAG_FSID_VERSION_2) {
sbi->size=super.size;
@@ -289,7 +316,7 @@
/* Set it all up.. */
sb->s_op = &cramfs_ops;
- root = get_cramfs_inode(sb, &super.root);
+ root = get_cramfs_inode(sb, &super.root, 0);
if (!root)
goto out;
sb->s_root = d_alloc_root(root);
@@ -365,7 +392,7 @@
*/
namelen = de->namelen << 2;
memcpy(buf, name, namelen);
- ino = CRAMINO(de);
+ ino = cramino(de, OFFSET(inode) + offset);
mode = de->mode;
mutex_unlock(&read_mutex);
nextoffset = offset + sizeof(*de) + namelen;
@@ -404,8 +431,9 @@
struct cramfs_inode *de;
char *name;
int namelen, retval;
+ int dir_off = OFFSET(dir) + offset;
- de = cramfs_read(dir->i_sb, OFFSET(dir) + offset, sizeof(*de)+CRAMFS_MAXPATHLEN);
+ de = cramfs_read(dir->i_sb, dir_off, sizeof(*de)+CRAMFS_MAXPATHLEN);
name = (char *)(de+1);
/* Try to take advantage of sorted directories */
@@ -436,7 +464,7 @@
if (!retval) {
struct cramfs_inode entry = *de;
mutex_unlock(&read_mutex);
- d_add(dentry, get_cramfs_inode(dir->i_sb, &entry));
+ d_add(dentry, get_cramfs_inode(dir->i_sb, &entry, dir_off));
return NULL;
}
/* else (retval < 0) */
diff --git a/fs/eventpoll.c b/fs/eventpoll.c
index 8cf0724..cc8a9b7 100644
--- a/fs/eventpoll.c
+++ b/fs/eventpoll.c
@@ -217,7 +217,7 @@
* Configuration options available inside /proc/sys/fs/epoll/
*/
/* Maximum number of epoll watched descriptors, per user */
-static int max_user_watches __read_mostly;
+static long max_user_watches __read_mostly;
/*
* This mutex is used to serialize ep_free() and eventpoll_release_file().
@@ -240,16 +240,18 @@
#include <linux/sysctl.h>
-static int zero;
+static long zero;
+static long long_max = LONG_MAX;
ctl_table epoll_table[] = {
{
.procname = "max_user_watches",
.data = &max_user_watches,
- .maxlen = sizeof(int),
+ .maxlen = sizeof(max_user_watches),
.mode = 0644,
- .proc_handler = proc_dointvec_minmax,
+ .proc_handler = proc_doulongvec_minmax,
.extra1 = &zero,
+ .extra2 = &long_max,
},
{ }
};
@@ -561,7 +563,7 @@
/* At this point it is safe to free the eventpoll item */
kmem_cache_free(epi_cache, epi);
- atomic_dec(&ep->user->epoll_watches);
+ atomic_long_dec(&ep->user->epoll_watches);
return 0;
}
@@ -898,11 +900,12 @@
{
int error, revents, pwake = 0;
unsigned long flags;
+ long user_watches;
struct epitem *epi;
struct ep_pqueue epq;
- if (unlikely(atomic_read(&ep->user->epoll_watches) >=
- max_user_watches))
+ user_watches = atomic_long_read(&ep->user->epoll_watches);
+ if (unlikely(user_watches >= max_user_watches))
return -ENOSPC;
if (!(epi = kmem_cache_alloc(epi_cache, GFP_KERNEL)))
return -ENOMEM;
@@ -966,7 +969,7 @@
spin_unlock_irqrestore(&ep->lock, flags);
- atomic_inc(&ep->user->epoll_watches);
+ atomic_long_inc(&ep->user->epoll_watches);
/* We have to call this outside the lock */
if (pwake)
@@ -1426,6 +1429,7 @@
*/
max_user_watches = (((si.totalram - si.totalhigh) / 25) << PAGE_SHIFT) /
EP_ITEM_COST;
+ BUG_ON(max_user_watches < 0);
/* Initialize the structure used to perform safe poll wait head wake ups */
ep_nested_calls_init(&poll_safewake_ncalls);
diff --git a/fs/ext4/ext4.h b/fs/ext4/ext4.h
index bab2387..1de65f5 100644
--- a/fs/ext4/ext4.h
+++ b/fs/ext4/ext4.h
@@ -561,7 +561,7 @@
#define EXT4_IOC32_SETVERSION_OLD FS_IOC32_SETVERSION
#endif
-/* Max physical block we can addres w/o extents */
+/* Max physical block we can address w/o extents */
#define EXT4_MAX_BLOCK_FILE_PHYS 0xFFFFFFFF
/*
diff --git a/fs/ext4/extents.c b/fs/ext4/extents.c
index 2e061df..c4068f6 100644
--- a/fs/ext4/extents.c
+++ b/fs/ext4/extents.c
@@ -2845,14 +2845,14 @@
* to an uninitialized extent.
*
* Writing to an uninitized extent may result in splitting the uninitialized
- * extent into multiple /intialized unintialized extents (up to three)
+ * extent into multiple /initialized uninitialized extents (up to three)
* There are three possibilities:
* a> There is no split required: Entire extent should be uninitialized
* b> Splits in two extents: Write is happening at either end of the extent
* c> Splits in three extents: Somone is writing in middle of the extent
*
* One of more index blocks maybe needed if the extent tree grow after
- * the unintialized extent split. To prevent ENOSPC occur at the IO
+ * the uninitialized extent split. To prevent ENOSPC occur at the IO
* complete, we need to split the uninitialized extent before DIO submit
* the IO. The uninitialized extent called at this time will be split
* into three uninitialized extent(at most). After IO complete, the part
diff --git a/fs/ext4/inode.c b/fs/ext4/inode.c
index e80fc51..9f7f9e4 100644
--- a/fs/ext4/inode.c
+++ b/fs/ext4/inode.c
@@ -39,6 +39,7 @@
#include <linux/bio.h>
#include <linux/workqueue.h>
#include <linux/kernel.h>
+#include <linux/printk.h>
#include <linux/slab.h>
#include <linux/ratelimit.h>
@@ -3379,7 +3380,7 @@
* doing I/O at all.
*
* We could call write_cache_pages(), and then redirty all of
- * the pages by calling redirty_page_for_writeback() but that
+ * the pages by calling redirty_page_for_writepage() but that
* would be ugly in the extreme. So instead we would need to
* replicate parts of the code in the above functions,
* simplifying them becuase we wouldn't actually intend to
@@ -3737,7 +3738,7 @@
retry:
io_end = ext4_init_io_end(inode, GFP_ATOMIC);
if (!io_end) {
- pr_warning_ratelimited("%s: allocation fail\n", __func__);
+ pr_warn_ratelimited("%s: allocation fail\n", __func__);
schedule();
goto retry;
}
@@ -3761,9 +3762,9 @@
* preallocated extents, and those write extend the file, no need to
* fall back to buffered IO.
*
- * For holes, we fallocate those blocks, mark them as unintialized
+ * For holes, we fallocate those blocks, mark them as uninitialized
* If those blocks were preallocated, we mark sure they are splited, but
- * still keep the range to write as unintialized.
+ * still keep the range to write as uninitialized.
*
* The unwrritten extents will be converted to written when DIO is completed.
* For async direct IO, since the IO may still pending when return, we
diff --git a/fs/jbd/transaction.c b/fs/jbd/transaction.c
index 846a3f3..5b2e4c3 100644
--- a/fs/jbd/transaction.c
+++ b/fs/jbd/transaction.c
@@ -207,7 +207,7 @@
* the committing transaction. Really, we only need to give it
* committing_transaction->t_outstanding_credits plus "enough" for
* the log control blocks.
- * Also, this test is inconsitent with the matching one in
+ * Also, this test is inconsistent with the matching one in
* journal_extend().
*/
if (__log_space_left(journal) < jbd_space_needed(journal)) {
diff --git a/fs/jbd2/transaction.c b/fs/jbd2/transaction.c
index 3948932..faad2bd 100644
--- a/fs/jbd2/transaction.c
+++ b/fs/jbd2/transaction.c
@@ -251,7 +251,7 @@
* the committing transaction. Really, we only need to give it
* committing_transaction->t_outstanding_credits plus "enough" for
* the log control blocks.
- * Also, this test is inconsitent with the matching one in
+ * Also, this test is inconsistent with the matching one in
* jbd2_journal_extend().
*/
if (__jbd2_log_space_left(journal) < jbd_space_needed(journal)) {
diff --git a/fs/notify/fanotify/Kconfig b/fs/notify/fanotify/Kconfig
index 3ac36b7b..7dceff0 100644
--- a/fs/notify/fanotify/Kconfig
+++ b/fs/notify/fanotify/Kconfig
@@ -6,7 +6,7 @@
---help---
Say Y here to enable fanotify suport. fanotify is a file access
notification system which differs from inotify in that it sends
- and open file descriptor to the userspace listener along with
+ an open file descriptor to the userspace listener along with
the event.
If unsure, say Y.
diff --git a/fs/ntfs/Makefile b/fs/ntfs/Makefile
index 58b6be9..4ff028f 100644
--- a/fs/ntfs/Makefile
+++ b/fs/ntfs/Makefile
@@ -6,7 +6,7 @@
index.o inode.o mft.o mst.o namei.o runlist.o super.o sysctl.o \
unistr.o upcase.o
-EXTRA_CFLAGS = -DNTFS_VERSION=\"2.1.29\"
+EXTRA_CFLAGS = -DNTFS_VERSION=\"2.1.30\"
ifeq ($(CONFIG_NTFS_DEBUG),y)
EXTRA_CFLAGS += -DDEBUG
diff --git a/fs/ntfs/file.c b/fs/ntfs/file.c
index 113ebd9..f4b1057 100644
--- a/fs/ntfs/file.c
+++ b/fs/ntfs/file.c
@@ -1,7 +1,7 @@
/*
* file.c - NTFS kernel file operations. Part of the Linux-NTFS project.
*
- * Copyright (c) 2001-2007 Anton Altaparmakov
+ * Copyright (c) 2001-2011 Anton Altaparmakov and Tuxera Inc.
*
* This program/include file is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as published
@@ -1380,15 +1380,14 @@
* pages (out to offset + bytes), to emulate ntfs_copy_from_user()'s
* single-segment behaviour.
*
- * We call the same helper (__ntfs_copy_from_user_iovec_inatomic()) both
- * when atomic and when not atomic. This is ok because
- * __ntfs_copy_from_user_iovec_inatomic() calls __copy_from_user_inatomic()
- * and it is ok to call this when non-atomic.
- * Infact, the only difference between __copy_from_user_inatomic() and
+ * We call the same helper (__ntfs_copy_from_user_iovec_inatomic()) both when
+ * atomic and when not atomic. This is ok because it calls
+ * __copy_from_user_inatomic() and it is ok to call this when non-atomic. In
+ * fact, the only difference between __copy_from_user_inatomic() and
* __copy_from_user() is that the latter calls might_sleep() and the former
- * should not zero the tail of the buffer on error. And on many
- * architectures __copy_from_user_inatomic() is just defined to
- * __copy_from_user() so it makes no difference at all on those architectures.
+ * should not zero the tail of the buffer on error. And on many architectures
+ * __copy_from_user_inatomic() is just defined to __copy_from_user() so it
+ * makes no difference at all on those architectures.
*/
static inline size_t ntfs_copy_from_user_iovec(struct page **pages,
unsigned nr_pages, unsigned ofs, const struct iovec **iov,
@@ -1409,28 +1408,28 @@
if (unlikely(copied != len)) {
/* Do it the slow way. */
addr = kmap(*pages);
- copied = __ntfs_copy_from_user_iovec_inatomic(addr + ofs,
- *iov, *iov_ofs, len);
- /*
- * Zero the rest of the target like __copy_from_user().
- */
- memset(addr + ofs + copied, 0, len - copied);
- kunmap(*pages);
+ copied = __ntfs_copy_from_user_iovec_inatomic(addr +
+ ofs, *iov, *iov_ofs, len);
if (unlikely(copied != len))
goto err_out;
+ kunmap(*pages);
}
total += len;
+ ntfs_set_next_iovec(iov, iov_ofs, len);
bytes -= len;
if (!bytes)
break;
- ntfs_set_next_iovec(iov, iov_ofs, len);
ofs = 0;
} while (++pages < last_page);
out:
return total;
err_out:
- total += copied;
+ BUG_ON(copied > len);
/* Zero the rest of the target like __copy_from_user(). */
+ memset(addr + ofs + copied, 0, len - copied);
+ kunmap(*pages);
+ total += copied;
+ ntfs_set_next_iovec(iov, iov_ofs, copied);
while (++pages < last_page) {
bytes -= len;
if (!bytes)
diff --git a/fs/ntfs/super.c b/fs/ntfs/super.c
index a30ecacc..29099a0 100644
--- a/fs/ntfs/super.c
+++ b/fs/ntfs/super.c
@@ -1,7 +1,7 @@
/*
* super.c - NTFS kernel super block handling. Part of the Linux-NTFS project.
*
- * Copyright (c) 2001-2007 Anton Altaparmakov
+ * Copyright (c) 2001-2011 Anton Altaparmakov and Tuxera Inc.
* Copyright (c) 2001,2002 Richard Russon
*
* This program/include file is free software; you can redistribute it and/or
@@ -3193,8 +3193,8 @@
ntfs_sysctl(0);
}
-MODULE_AUTHOR("Anton Altaparmakov <aia21@cantab.net>");
-MODULE_DESCRIPTION("NTFS 1.2/3.x driver - Copyright (c) 2001-2007 Anton Altaparmakov");
+MODULE_AUTHOR("Anton Altaparmakov <anton@tuxera.com>");
+MODULE_DESCRIPTION("NTFS 1.2/3.x driver - Copyright (c) 2001-2011 Anton Altaparmakov and Tuxera Inc.");
MODULE_VERSION(NTFS_VERSION);
MODULE_LICENSE("GPL");
#ifdef DEBUG
diff --git a/fs/ocfs2/inode.c b/fs/ocfs2/inode.c
index f935fd6..4068c6c 100644
--- a/fs/ocfs2/inode.c
+++ b/fs/ocfs2/inode.c
@@ -434,7 +434,7 @@
* #1 and #2 can be simply solved by never taking the lock
* here for system files (which are the only type we read
* during mount). It's a heavier approach, but our main
- * concern is user-accesible files anyway.
+ * concern is user-accessible files anyway.
*
* #3 works itself out because we'll eventually take the
* cluster lock before trusting anything anyway.
diff --git a/fs/ocfs2/suballoc.c b/fs/ocfs2/suballoc.c
index 5fed60d..71998d4 100644
--- a/fs/ocfs2/suballoc.c
+++ b/fs/ocfs2/suballoc.c
@@ -1916,7 +1916,7 @@
if (res->sr_bg_blkno) {
/* Attempt to short-circuit the usual search mechanism
* by jumping straight to the most recently used
- * allocation group. This helps us mantain some
+ * allocation group. This helps us maintain some
* contiguousness across allocations. */
status = ocfs2_search_one_group(ac, handle, bits_wanted,
min_bits, res, &bits_left);
diff --git a/fs/pipe.c b/fs/pipe.c
index 6b0255a..e2e95fb 100644
--- a/fs/pipe.c
+++ b/fs/pipe.c
@@ -441,7 +441,7 @@
break;
}
if (do_wakeup) {
- wake_up_interruptible_sync(&pipe->wait);
+ wake_up_interruptible_sync_poll(&pipe->wait, POLLOUT);
kill_fasync(&pipe->fasync_writers, SIGIO, POLL_OUT);
}
pipe_wait(pipe);
@@ -450,7 +450,7 @@
/* Signal writers asynchronously that there is more room. */
if (do_wakeup) {
- wake_up_interruptible_sync(&pipe->wait);
+ wake_up_interruptible_sync_poll(&pipe->wait, POLLOUT);
kill_fasync(&pipe->fasync_writers, SIGIO, POLL_OUT);
}
if (ret > 0)
@@ -612,7 +612,7 @@
break;
}
if (do_wakeup) {
- wake_up_interruptible_sync(&pipe->wait);
+ wake_up_interruptible_sync_poll(&pipe->wait, POLLIN);
kill_fasync(&pipe->fasync_readers, SIGIO, POLL_IN);
do_wakeup = 0;
}
@@ -623,7 +623,7 @@
out:
mutex_unlock(&inode->i_mutex);
if (do_wakeup) {
- wake_up_interruptible_sync(&pipe->wait);
+ wake_up_interruptible_sync_poll(&pipe->wait, POLLIN);
kill_fasync(&pipe->fasync_readers, SIGIO, POLL_IN);
}
if (ret > 0)
@@ -715,7 +715,7 @@
if (!pipe->readers && !pipe->writers) {
free_pipe_info(inode);
} else {
- wake_up_interruptible_sync(&pipe->wait);
+ wake_up_interruptible_sync_poll(&pipe->wait, POLLIN | POLLOUT);
kill_fasync(&pipe->fasync_readers, SIGIO, POLL_IN);
kill_fasync(&pipe->fasync_writers, SIGIO, POLL_OUT);
}
diff --git a/fs/proc/Makefile b/fs/proc/Makefile
index 288a49e0..df434c5 100644
--- a/fs/proc/Makefile
+++ b/fs/proc/Makefile
@@ -10,12 +10,12 @@
proc-y += inode.o root.o base.o generic.o array.o \
proc_tty.o
proc-y += cmdline.o
+proc-y += consoles.o
proc-y += cpuinfo.o
proc-y += devices.o
proc-y += interrupts.o
proc-y += loadavg.o
proc-y += meminfo.o
-proc-y += proc_console.o
proc-y += stat.o
proc-y += uptime.o
proc-y += version.o
diff --git a/fs/proc/array.c b/fs/proc/array.c
index fff6572..df2b703 100644
--- a/fs/proc/array.c
+++ b/fs/proc/array.c
@@ -95,7 +95,7 @@
get_task_comm(tcomm, p);
- seq_printf(m, "Name:\t");
+ seq_puts(m, "Name:\t");
end = m->buf + m->size;
buf = m->buf + m->count;
name = tcomm;
@@ -122,7 +122,7 @@
buf++;
}
m->count = buf - m->buf;
- seq_printf(m, "\n");
+ seq_putc(m, '\n');
}
/*
@@ -208,7 +208,7 @@
seq_printf(m, "%d ", GROUP_AT(group_info, g));
put_cred(cred);
- seq_printf(m, "\n");
+ seq_putc(m, '\n');
}
static void render_sigset_t(struct seq_file *m, const char *header,
@@ -216,7 +216,7 @@
{
int i;
- seq_printf(m, "%s", header);
+ seq_puts(m, header);
i = _NSIG;
do {
@@ -230,7 +230,7 @@
seq_printf(m, "%x", x);
} while (i >= 4);
- seq_printf(m, "\n");
+ seq_putc(m, '\n');
}
static void collect_sigign_sigcatch(struct task_struct *p, sigset_t *ign,
@@ -291,12 +291,12 @@
{
unsigned __capi;
- seq_printf(m, "%s", header);
+ seq_puts(m, header);
CAP_FOR_EACH_U32(__capi) {
seq_printf(m, "%08x",
a->cap[(_KERNEL_CAPABILITY_U32S-1) - __capi]);
}
- seq_printf(m, "\n");
+ seq_putc(m, '\n');
}
static inline void task_cap(struct seq_file *m, struct task_struct *p)
@@ -329,12 +329,12 @@
static void task_cpus_allowed(struct seq_file *m, struct task_struct *task)
{
- seq_printf(m, "Cpus_allowed:\t");
+ seq_puts(m, "Cpus_allowed:\t");
seq_cpumask(m, &task->cpus_allowed);
- seq_printf(m, "\n");
- seq_printf(m, "Cpus_allowed_list:\t");
+ seq_putc(m, '\n');
+ seq_puts(m, "Cpus_allowed_list:\t");
seq_cpumask_list(m, &task->cpus_allowed);
- seq_printf(m, "\n");
+ seq_putc(m, '\n');
}
int proc_pid_status(struct seq_file *m, struct pid_namespace *ns,
@@ -535,15 +535,15 @@
int proc_pid_statm(struct seq_file *m, struct pid_namespace *ns,
struct pid *pid, struct task_struct *task)
{
- int size = 0, resident = 0, shared = 0, text = 0, lib = 0, data = 0;
+ unsigned long size = 0, resident = 0, shared = 0, text = 0, data = 0;
struct mm_struct *mm = get_task_mm(task);
if (mm) {
size = task_statm(mm, &shared, &text, &data, &resident);
mmput(mm);
}
- seq_printf(m, "%d %d %d %d %d %d %d\n",
- size, resident, shared, text, lib, data, 0);
+ seq_printf(m, "%lu %lu %lu %lu 0 %lu 0\n",
+ size, resident, shared, text, data);
return 0;
}
diff --git a/fs/proc/base.c b/fs/proc/base.c
index b20962c..93f1cdd 100644
--- a/fs/proc/base.c
+++ b/fs/proc/base.c
@@ -373,26 +373,20 @@
return -ESRCH;
seq_puts(m, "Latency Top version : v0.1\n");
for (i = 0; i < 32; i++) {
- if (task->latency_record[i].backtrace[0]) {
+ struct latency_record *lr = &task->latency_record[i];
+ if (lr->backtrace[0]) {
int q;
- seq_printf(m, "%i %li %li ",
- task->latency_record[i].count,
- task->latency_record[i].time,
- task->latency_record[i].max);
+ seq_printf(m, "%i %li %li",
+ lr->count, lr->time, lr->max);
for (q = 0; q < LT_BACKTRACEDEPTH; q++) {
- char sym[KSYM_SYMBOL_LEN];
- char *c;
- if (!task->latency_record[i].backtrace[q])
+ unsigned long bt = lr->backtrace[q];
+ if (!bt)
break;
- if (task->latency_record[i].backtrace[q] == ULONG_MAX)
+ if (bt == ULONG_MAX)
break;
- sprint_symbol(sym, task->latency_record[i].backtrace[q]);
- c = strchr(sym, '+');
- if (c)
- *c = 0;
- seq_printf(m, "%s ", sym);
+ seq_printf(m, " %ps", (void *)bt);
}
- seq_printf(m, "\n");
+ seq_putc(m, '\n');
}
}
@@ -751,14 +745,7 @@
static int proc_single_open(struct inode *inode, struct file *filp)
{
- int ret;
- ret = single_open(filp, proc_single_show, NULL);
- if (!ret) {
- struct seq_file *m = filp->private_data;
-
- m->private = inode;
- }
- return ret;
+ return single_open(filp, proc_single_show, inode);
}
static const struct file_operations proc_single_file_operations = {
@@ -1386,15 +1373,7 @@
static int sched_open(struct inode *inode, struct file *filp)
{
- int ret;
-
- ret = single_open(filp, sched_show, NULL);
- if (!ret) {
- struct seq_file *m = filp->private_data;
-
- m->private = inode;
- }
- return ret;
+ return single_open(filp, sched_show, inode);
}
static const struct file_operations proc_pid_sched_operations = {
@@ -1530,15 +1509,7 @@
static int comm_open(struct inode *inode, struct file *filp)
{
- int ret;
-
- ret = single_open(filp, comm_show, NULL);
- if (!ret) {
- struct seq_file *m = filp->private_data;
-
- m->private = inode;
- }
- return ret;
+ return single_open(filp, comm_show, inode);
}
static const struct file_operations proc_pid_set_comm_operations = {
diff --git a/fs/proc/proc_console.c b/fs/proc/consoles.c
similarity index 96%
rename from fs/proc/proc_console.c
rename to fs/proc/consoles.c
index 8a70760..eafc22a 100644
--- a/fs/proc/proc_console.c
+++ b/fs/proc/consoles.c
@@ -106,9 +106,9 @@
.release = seq_release,
};
-static int register_proc_consoles(void)
+static int __init proc_consoles_init(void)
{
proc_create("consoles", 0, NULL, &proc_consoles_operations);
return 0;
}
-module_init(register_proc_consoles);
+module_init(proc_consoles_init);
diff --git a/fs/proc/devices.c b/fs/proc/devices.c
index 59ee7da..b143471 100644
--- a/fs/proc/devices.c
+++ b/fs/proc/devices.c
@@ -9,14 +9,14 @@
if (i < CHRDEV_MAJOR_HASH_SIZE) {
if (i == 0)
- seq_printf(f, "Character devices:\n");
+ seq_puts(f, "Character devices:\n");
chrdev_show(f, i);
}
#ifdef CONFIG_BLOCK
else {
i -= CHRDEV_MAJOR_HASH_SIZE;
if (i == 0)
- seq_printf(f, "\nBlock devices:\n");
+ seq_puts(f, "\nBlock devices:\n");
blkdev_show(f, i);
}
#endif
diff --git a/fs/proc/generic.c b/fs/proc/generic.c
index f766be2..01e07f2 100644
--- a/fs/proc/generic.c
+++ b/fs/proc/generic.c
@@ -425,13 +425,10 @@
if (de->namelen != dentry->d_name.len)
continue;
if (!memcmp(dentry->d_name.name, de->name, de->namelen)) {
- unsigned int ino;
-
- ino = de->low_ino;
pde_get(de);
spin_unlock(&proc_subdir_lock);
error = -EINVAL;
- inode = proc_get_inode(dir->i_sb, ino, de);
+ inode = proc_get_inode(dir->i_sb, de);
goto out_unlock;
}
}
@@ -768,12 +765,7 @@
static void free_proc_entry(struct proc_dir_entry *de)
{
- unsigned int ino = de->low_ino;
-
- if (ino < PROC_DYNAMIC_FIRST)
- return;
-
- release_inode_number(ino);
+ release_inode_number(de->low_ino);
if (S_ISLNK(de->mode))
kfree(de->data);
@@ -834,12 +826,9 @@
wait_for_completion(de->pde_unload_completion);
- goto continue_removing;
+ spin_lock(&de->pde_unload_lock);
}
- spin_unlock(&de->pde_unload_lock);
-continue_removing:
- spin_lock(&de->pde_unload_lock);
while (!list_empty(&de->pde_openers)) {
struct pde_opener *pdeo;
diff --git a/fs/proc/inode.c b/fs/proc/inode.c
index 6bcb926..176ce4c 100644
--- a/fs/proc/inode.c
+++ b/fs/proc/inode.c
@@ -416,12 +416,11 @@
};
#endif
-struct inode *proc_get_inode(struct super_block *sb, unsigned int ino,
- struct proc_dir_entry *de)
+struct inode *proc_get_inode(struct super_block *sb, struct proc_dir_entry *de)
{
struct inode * inode;
- inode = iget_locked(sb, ino);
+ inode = iget_locked(sb, de->low_ino);
if (!inode)
return NULL;
if (inode->i_state & I_NEW) {
@@ -471,7 +470,7 @@
s->s_time_gran = 1;
pde_get(&proc_root);
- root_inode = proc_get_inode(s, PROC_ROOT_INO, &proc_root);
+ root_inode = proc_get_inode(s, &proc_root);
if (!root_inode)
goto out_no_root;
root_inode->i_uid = 0;
diff --git a/fs/proc/internal.h b/fs/proc/internal.h
index 1f24a3e..9ad561d 100644
--- a/fs/proc/internal.h
+++ b/fs/proc/internal.h
@@ -96,7 +96,8 @@
struct dentry *proc_pid_lookup(struct inode *dir, struct dentry * dentry, struct nameidata *);
int proc_pid_readdir(struct file * filp, void * dirent, filldir_t filldir);
unsigned long task_vsize(struct mm_struct *);
-int task_statm(struct mm_struct *, int *, int *, int *, int *);
+unsigned long task_statm(struct mm_struct *,
+ unsigned long *, unsigned long *, unsigned long *, unsigned long *);
void task_mem(struct seq_file *, struct mm_struct *);
static inline struct proc_dir_entry *pde_get(struct proc_dir_entry *pde)
@@ -108,7 +109,7 @@
extern struct vfsmount *proc_mnt;
int proc_fill_super(struct super_block *);
-struct inode *proc_get_inode(struct super_block *, unsigned int, struct proc_dir_entry *);
+struct inode *proc_get_inode(struct super_block *, struct proc_dir_entry *);
/*
* These are generic /proc routines that use the internal
diff --git a/fs/proc/kcore.c b/fs/proc/kcore.c
index 6f37c39..d245cb2 100644
--- a/fs/proc/kcore.c
+++ b/fs/proc/kcore.c
@@ -558,7 +558,7 @@
static const struct file_operations proc_kcore_operations = {
.read = read_kcore,
.open = open_kcore,
- .llseek = generic_file_llseek,
+ .llseek = default_llseek,
};
#ifdef CONFIG_MEMORY_HOTPLUG
diff --git a/fs/proc/page.c b/fs/proc/page.c
index 3b8b4566..b06c674 100644
--- a/fs/proc/page.c
+++ b/fs/proc/page.c
@@ -40,7 +40,7 @@
ppage = pfn_to_page(pfn);
else
ppage = NULL;
- if (!ppage)
+ if (!ppage || PageSlab(ppage))
pcount = 0;
else
pcount = page_mapcount(ppage);
diff --git a/fs/proc/proc_tty.c b/fs/proc/proc_tty.c
index 83adcc8..cb761f0 100644
--- a/fs/proc/proc_tty.c
+++ b/fs/proc/proc_tty.c
@@ -36,27 +36,27 @@
}
switch (p->type) {
case TTY_DRIVER_TYPE_SYSTEM:
- seq_printf(m, "system");
+ seq_puts(m, "system");
if (p->subtype == SYSTEM_TYPE_TTY)
- seq_printf(m, ":/dev/tty");
+ seq_puts(m, ":/dev/tty");
else if (p->subtype == SYSTEM_TYPE_SYSCONS)
- seq_printf(m, ":console");
+ seq_puts(m, ":console");
else if (p->subtype == SYSTEM_TYPE_CONSOLE)
- seq_printf(m, ":vtmaster");
+ seq_puts(m, ":vtmaster");
break;
case TTY_DRIVER_TYPE_CONSOLE:
- seq_printf(m, "console");
+ seq_puts(m, "console");
break;
case TTY_DRIVER_TYPE_SERIAL:
- seq_printf(m, "serial");
+ seq_puts(m, "serial");
break;
case TTY_DRIVER_TYPE_PTY:
if (p->subtype == PTY_TYPE_MASTER)
- seq_printf(m, "pty:master");
+ seq_puts(m, "pty:master");
else if (p->subtype == PTY_TYPE_SLAVE)
- seq_printf(m, "pty:slave");
+ seq_puts(m, "pty:slave");
else
- seq_printf(m, "pty");
+ seq_puts(m, "pty");
break;
default:
seq_printf(m, "type:%d.%d", p->type, p->subtype);
@@ -74,19 +74,19 @@
/* pseudo-drivers first */
seq_printf(m, "%-20s /dev/%-8s ", "/dev/tty", "tty");
seq_printf(m, "%3d %7d ", TTYAUX_MAJOR, 0);
- seq_printf(m, "system:/dev/tty\n");
+ seq_puts(m, "system:/dev/tty\n");
seq_printf(m, "%-20s /dev/%-8s ", "/dev/console", "console");
seq_printf(m, "%3d %7d ", TTYAUX_MAJOR, 1);
- seq_printf(m, "system:console\n");
+ seq_puts(m, "system:console\n");
#ifdef CONFIG_UNIX98_PTYS
seq_printf(m, "%-20s /dev/%-8s ", "/dev/ptmx", "ptmx");
seq_printf(m, "%3d %7d ", TTYAUX_MAJOR, 2);
- seq_printf(m, "system\n");
+ seq_puts(m, "system\n");
#endif
#ifdef CONFIG_VT
seq_printf(m, "%-20s /dev/%-8s ", "/dev/vc/0", "vc/0");
seq_printf(m, "%3d %7d ", TTY_MAJOR, 0);
- seq_printf(m, "system:vtmaster\n");
+ seq_puts(m, "system:vtmaster\n");
#endif
}
diff --git a/fs/proc/softirqs.c b/fs/proc/softirqs.c
index 3799473..62604be 100644
--- a/fs/proc/softirqs.c
+++ b/fs/proc/softirqs.c
@@ -10,16 +10,16 @@
{
int i, j;
- seq_printf(p, " ");
+ seq_puts(p, " ");
for_each_possible_cpu(i)
seq_printf(p, "CPU%-8d", i);
- seq_printf(p, "\n");
+ seq_putc(p, '\n');
for (i = 0; i < NR_SOFTIRQS; i++) {
seq_printf(p, "%12s:", softirq_to_name[i]);
for_each_possible_cpu(j)
seq_printf(p, " %10u", kstat_softirqs_cpu(i, j));
- seq_printf(p, "\n");
+ seq_putc(p, '\n');
}
return 0;
}
diff --git a/fs/proc/stat.c b/fs/proc/stat.c
index e15a19c..1cffa2b 100644
--- a/fs/proc/stat.c
+++ b/fs/proc/stat.c
@@ -126,7 +126,7 @@
for (i = 0; i < NR_SOFTIRQS; i++)
seq_printf(p, " %u", per_softirq_sums[i]);
- seq_printf(p, "\n");
+ seq_putc(p, '\n');
return 0;
}
diff --git a/fs/proc/task_mmu.c b/fs/proc/task_mmu.c
index c126c83..c3755bd 100644
--- a/fs/proc/task_mmu.c
+++ b/fs/proc/task_mmu.c
@@ -66,8 +66,9 @@
return PAGE_SIZE * mm->total_vm;
}
-int task_statm(struct mm_struct *mm, int *shared, int *text,
- int *data, int *resident)
+unsigned long task_statm(struct mm_struct *mm,
+ unsigned long *shared, unsigned long *text,
+ unsigned long *data, unsigned long *resident)
{
*shared = get_mm_counter(mm, MM_FILEPAGES);
*text = (PAGE_ALIGN(mm->end_code) - (mm->start_code & PAGE_MASK))
diff --git a/fs/proc/task_nommu.c b/fs/proc/task_nommu.c
index cb6306e..b535d3e 100644
--- a/fs/proc/task_nommu.c
+++ b/fs/proc/task_nommu.c
@@ -92,13 +92,14 @@
return vsize;
}
-int task_statm(struct mm_struct *mm, int *shared, int *text,
- int *data, int *resident)
+unsigned long task_statm(struct mm_struct *mm,
+ unsigned long *shared, unsigned long *text,
+ unsigned long *data, unsigned long *resident)
{
struct vm_area_struct *vma;
struct vm_region *region;
struct rb_node *p;
- int size = kobjsize(mm);
+ unsigned long size = kobjsize(mm);
down_read(&mm->mmap_sem);
for (p = rb_first(&mm->mm_rb); p; p = rb_next(p)) {
diff --git a/fs/reiserfs/prints.c b/fs/reiserfs/prints.c
index adbc6f5..45de98b 100644
--- a/fs/reiserfs/prints.c
+++ b/fs/reiserfs/prints.c
@@ -586,13 +586,13 @@
va_list args;
int mode, first, last;
- va_start(args, bh);
-
if (!bh) {
printk("print_block: buffer is NULL\n");
return;
}
+ va_start(args, bh);
+
mode = va_arg(args, int);
first = va_arg(args, int);
last = va_arg(args, int);
diff --git a/fs/select.c b/fs/select.c
index b7b10aa..e56560d 100644
--- a/fs/select.c
+++ b/fs/select.c
@@ -306,6 +306,8 @@
rts.tv_sec = rts.tv_nsec = 0;
if (timeval) {
+ if (sizeof(rtv) > sizeof(rtv.tv_sec) + sizeof(rtv.tv_usec))
+ memset(&rtv, 0, sizeof(rtv));
rtv.tv_sec = rts.tv_sec;
rtv.tv_usec = rts.tv_nsec / NSEC_PER_USEC;
diff --git a/fs/xfs/linux-2.6/xfs_super.c b/fs/xfs/linux-2.6/xfs_super.c
index c51faaa..a10f641 100644
--- a/fs/xfs/linux-2.6/xfs_super.c
+++ b/fs/xfs/linux-2.6/xfs_super.c
@@ -947,7 +947,7 @@
* Slab object creation initialisation for the XFS inode.
* This covers only the idempotent fields in the XFS inode;
* all other fields need to be initialised on allocation
- * from the slab. This avoids the need to repeatedly intialise
+ * from the slab. This avoids the need to repeatedly initialise
* fields in the xfs inode that left in the initialise state
* when freeing the inode.
*/
diff --git a/include/acpi/actbl1.h b/include/acpi/actbl1.h
index c637b75..cd77aa75c 100644
--- a/include/acpi/actbl1.h
+++ b/include/acpi/actbl1.h
@@ -119,7 +119,7 @@
struct acpi_table_bert {
struct acpi_table_header header; /* Common ACPI table header */
u32 region_length; /* Length of the boot error region */
- u64 address; /* Physical addresss of the error region */
+ u64 address; /* Physical address of the error region */
};
/* Boot Error Region (not a subtable, pointed to by Address field above) */
diff --git a/include/asm-generic/gpio.h b/include/asm-generic/gpio.h
index ff5c660..6098cae 100644
--- a/include/asm-generic/gpio.h
+++ b/include/asm-generic/gpio.h
@@ -147,11 +147,11 @@
/* Always use the library code for GPIO management calls,
* or when sleeping may be involved.
*/
-extern int gpio_request(unsigned gpio, const char *label);
+extern int __must_check gpio_request(unsigned gpio, const char *label);
extern void gpio_free(unsigned gpio);
-extern int gpio_direction_input(unsigned gpio);
-extern int gpio_direction_output(unsigned gpio, int value);
+extern int __must_check gpio_direction_input(unsigned gpio);
+extern int __must_check gpio_direction_output(unsigned gpio, int value);
extern int gpio_set_debounce(unsigned gpio, unsigned debounce);
@@ -192,8 +192,8 @@
const char *label;
};
-extern int gpio_request_one(unsigned gpio, unsigned long flags, const char *label);
-extern int gpio_request_array(struct gpio *array, size_t num);
+extern int __must_check gpio_request_one(unsigned gpio, unsigned long flags, const char *label);
+extern int __must_check gpio_request_array(struct gpio *array, size_t num);
extern void gpio_free_array(struct gpio *array, size_t num);
#ifdef CONFIG_GPIO_SYSFS
diff --git a/include/asm-generic/vmlinux.lds.h b/include/asm-generic/vmlinux.lds.h
index 05cbad0..6864933 100644
--- a/include/asm-generic/vmlinux.lds.h
+++ b/include/asm-generic/vmlinux.lds.h
@@ -200,7 +200,8 @@
#define READ_MOSTLY_DATA(align) \
. = ALIGN(align); \
- *(.data..read_mostly)
+ *(.data..read_mostly) \
+ . = ALIGN(align);
#define CACHELINE_ALIGNED_DATA(align) \
. = ALIGN(align); \
diff --git a/include/crypto/if_alg.h b/include/crypto/if_alg.h
new file mode 100644
index 0000000..c5813c8
--- /dev/null
+++ b/include/crypto/if_alg.h
@@ -0,0 +1,92 @@
+/*
+ * if_alg: User-space algorithm interface
+ *
+ * Copyright (c) 2010 Herbert Xu <herbert@gondor.apana.org.au>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ */
+
+#ifndef _CRYPTO_IF_ALG_H
+#define _CRYPTO_IF_ALG_H
+
+#include <linux/compiler.h>
+#include <linux/completion.h>
+#include <linux/if_alg.h>
+#include <linux/types.h>
+#include <net/sock.h>
+
+#define ALG_MAX_PAGES 16
+
+struct crypto_async_request;
+
+struct alg_sock {
+ /* struct sock must be the first member of struct alg_sock */
+ struct sock sk;
+
+ struct sock *parent;
+
+ const struct af_alg_type *type;
+ void *private;
+};
+
+struct af_alg_completion {
+ struct completion completion;
+ int err;
+};
+
+struct af_alg_control {
+ struct af_alg_iv *iv;
+ int op;
+};
+
+struct af_alg_type {
+ void *(*bind)(const char *name, u32 type, u32 mask);
+ void (*release)(void *private);
+ int (*setkey)(void *private, const u8 *key, unsigned int keylen);
+ int (*accept)(void *private, struct sock *sk);
+
+ struct proto_ops *ops;
+ struct module *owner;
+ char name[14];
+};
+
+struct af_alg_sgl {
+ struct scatterlist sg[ALG_MAX_PAGES];
+ struct page *pages[ALG_MAX_PAGES];
+};
+
+int af_alg_register_type(const struct af_alg_type *type);
+int af_alg_unregister_type(const struct af_alg_type *type);
+
+int af_alg_release(struct socket *sock);
+int af_alg_accept(struct sock *sk, struct socket *newsock);
+
+int af_alg_make_sg(struct af_alg_sgl *sgl, void __user *addr, int len,
+ int write);
+void af_alg_free_sg(struct af_alg_sgl *sgl);
+
+int af_alg_cmsg_send(struct msghdr *msg, struct af_alg_control *con);
+
+int af_alg_wait_for_completion(int err, struct af_alg_completion *completion);
+void af_alg_complete(struct crypto_async_request *req, int err);
+
+static inline struct alg_sock *alg_sk(struct sock *sk)
+{
+ return (struct alg_sock *)sk;
+}
+
+static inline void af_alg_release_parent(struct sock *sk)
+{
+ sock_put(alg_sk(sk)->parent);
+}
+
+static inline void af_alg_init_completion(struct af_alg_completion *completion)
+{
+ init_completion(&completion->completion);
+}
+
+#endif /* _CRYPTO_IF_ALG_H */
diff --git a/drivers/crypto/padlock.h b/include/crypto/padlock.h
similarity index 82%
rename from drivers/crypto/padlock.h
rename to include/crypto/padlock.h
index b728e45..d2cfa2e 100644
--- a/drivers/crypto/padlock.h
+++ b/include/crypto/padlock.h
@@ -15,9 +15,15 @@
#define PADLOCK_ALIGNMENT 16
-#define PFX "padlock: "
+#define PFX KBUILD_MODNAME ": "
#define PADLOCK_CRA_PRIORITY 300
#define PADLOCK_COMPOSITE_PRIORITY 400
+#ifdef CONFIG_64BIT
+#define STACK_ALIGN 16
+#else
+#define STACK_ALIGN 4
+#endif
+
#endif /* _CRYPTO_PADLOCK_H */
diff --git a/include/crypto/scatterwalk.h b/include/crypto/scatterwalk.h
index 833d208..4fd95a3 100644
--- a/include/crypto/scatterwalk.h
+++ b/include/crypto/scatterwalk.h
@@ -68,6 +68,21 @@
return (++sg)->length ? sg : (void *)sg_page(sg);
}
+static inline void scatterwalk_crypto_chain(struct scatterlist *head,
+ struct scatterlist *sg,
+ int chain, int num)
+{
+ if (chain) {
+ head->length += sg->length;
+ sg = scatterwalk_sg_next(sg);
+ }
+
+ if (sg)
+ scatterwalk_sg_chain(head, num, sg);
+ else
+ sg_mark_end(head);
+}
+
static inline unsigned long scatterwalk_samebuf(struct scatter_walk *walk_in,
struct scatter_walk *walk_out)
{
diff --git a/include/linux/Kbuild b/include/linux/Kbuild
index d1580c1..2296d8b 100644
--- a/include/linux/Kbuild
+++ b/include/linux/Kbuild
@@ -158,6 +158,7 @@
header-y += if.h
header-y += if_addr.h
header-y += if_addrlabel.h
+header-y += if_alg.h
header-y += if_arcnet.h
header-y += if_arp.h
header-y += if_bonding.h
diff --git a/include/linux/binfmts.h b/include/linux/binfmts.h
index 64a7114..c3d6512 100644
--- a/include/linux/binfmts.h
+++ b/include/linux/binfmts.h
@@ -25,7 +25,7 @@
/*
* This structure is used to hold the arguments that are used when loading binaries.
*/
-struct linux_binprm{
+struct linux_binprm {
char buf[BINPRM_BUF_SIZE];
#ifdef CONFIG_MMU
struct vm_area_struct *vma;
@@ -93,7 +93,6 @@
int (*load_shlib)(struct file *);
int (*core_dump)(struct coredump_params *cprm);
unsigned long min_coredump; /* minimal dump size */
- int hasvdso;
};
extern int __register_binfmt(struct linux_binfmt *fmt, int insert);
@@ -113,7 +112,7 @@
extern int prepare_binprm(struct linux_binprm *);
extern int __must_check remove_arg_zero(struct linux_binprm *);
-extern int search_binary_handler(struct linux_binprm *,struct pt_regs *);
+extern int search_binary_handler(struct linux_binprm *, struct pt_regs *);
extern int flush_old_exec(struct linux_binprm * bprm);
extern void setup_new_exec(struct linux_binprm * bprm);
diff --git a/include/linux/cdev.h b/include/linux/cdev.h
index f389e31..fb45919 100644
--- a/include/linux/cdev.h
+++ b/include/linux/cdev.h
@@ -28,8 +28,6 @@
void cdev_del(struct cdev *);
-int cdev_index(struct inode *inode);
-
void cd_forget(struct inode *);
extern struct backing_dev_info directly_mappable_cdev_bdi;
diff --git a/include/linux/ceph/ceph_fs.h b/include/linux/ceph/ceph_fs.h
index c3c74ae..09dcc0c 100644
--- a/include/linux/ceph/ceph_fs.h
+++ b/include/linux/ceph/ceph_fs.h
@@ -43,6 +43,10 @@
#define CEPH_FEATURE_NOSRCADDR (1<<1)
#define CEPH_FEATURE_MONCLOCKCHECK (1<<2)
#define CEPH_FEATURE_FLOCK (1<<3)
+#define CEPH_FEATURE_SUBSCRIBE2 (1<<4)
+#define CEPH_FEATURE_MONNAMES (1<<5)
+#define CEPH_FEATURE_RECONNECT_SEQ (1<<6)
+#define CEPH_FEATURE_DIRLAYOUTHASH (1<<7)
/*
@@ -55,10 +59,10 @@
__le32 fl_stripe_count; /* over this many objects */
__le32 fl_object_size; /* until objects are this big, then move to
new objects */
- __le32 fl_cas_hash; /* 0 = none; 1 = sha256 */
+ __le32 fl_cas_hash; /* UNUSED. 0 = none; 1 = sha256 */
/* pg -> disk layout */
- __le32 fl_object_stripe_unit; /* for per-object parity, if any */
+ __le32 fl_object_stripe_unit; /* UNUSED. for per-object parity, if any */
/* object -> pg layout */
__le32 fl_pg_preferred; /* preferred primary for pg (-1 for none) */
@@ -69,6 +73,12 @@
int ceph_file_layout_is_valid(const struct ceph_file_layout *layout);
+struct ceph_dir_layout {
+ __u8 dl_dir_hash; /* see ceph_hash.h for ids */
+ __u8 dl_unused1;
+ __u16 dl_unused2;
+ __u32 dl_unused3;
+} __attribute__ ((packed));
/* crypto algorithms */
#define CEPH_CRYPTO_NONE 0x0
@@ -457,7 +467,7 @@
struct ceph_timespec rctime;
struct ceph_frag_tree_head fragtree; /* (must be at end of struct) */
} __attribute__ ((packed));
-/* followed by frag array, then symlink string, then xattr blob */
+/* followed by frag array, symlink string, dir layout, xattr blob */
/* reply_lease follows dname, and reply_inode */
struct ceph_mds_reply_lease {
diff --git a/include/linux/ceph/messenger.h b/include/linux/ceph/messenger.h
index a108b42..c3011be 100644
--- a/include/linux/ceph/messenger.h
+++ b/include/linux/ceph/messenger.h
@@ -110,17 +110,12 @@
/*
* ceph_connection state bit flags
- *
- * QUEUED and BUSY are used together to ensure that only a single
- * thread is currently opening, reading or writing data to the socket.
*/
#define LOSSYTX 0 /* we can close channel or drop messages on errors */
#define CONNECTING 1
#define NEGOTIATING 2
#define KEEPALIVE_PENDING 3
#define WRITE_PENDING 4 /* we have data ready to send */
-#define QUEUED 5 /* there is work queued on this connection */
-#define BUSY 6 /* work is being done */
#define STANDBY 8 /* no outgoing messages, socket closed. we keep
* the ceph_connection around to maintain shared
* state with the peer. */
diff --git a/include/linux/cgroup.h b/include/linux/cgroup.h
index ed4ba11..ce104e3 100644
--- a/include/linux/cgroup.h
+++ b/include/linux/cgroup.h
@@ -564,7 +564,7 @@
/*
* To iterate across the tasks in a cgroup:
*
- * 1) call cgroup_iter_start to intialize an iterator
+ * 1) call cgroup_iter_start to initialize an iterator
*
* 2) call cgroup_iter_next() to retrieve member tasks until it
* returns NULL or until you want to end the iteration
diff --git a/include/linux/cramfs_fs.h b/include/linux/cramfs_fs.h
index 6fc2bed..0e7bf27 100644
--- a/include/linux/cramfs_fs.h
+++ b/include/linux/cramfs_fs.h
@@ -84,9 +84,11 @@
| CRAMFS_FLAG_WRONG_SIGNATURE \
| CRAMFS_FLAG_SHIFTED_ROOT_OFFSET )
+#ifdef __KERNEL__
/* Uncompression interfaces to the underlying zlib */
int cramfs_uncompress_block(void *dst, int dstlen, void *src, int srclen);
int cramfs_uncompress_init(void);
void cramfs_uncompress_exit(void);
+#endif /* __KERNEL__ */
#endif
diff --git a/include/linux/cs5535.h b/include/linux/cs5535.h
index d5a1d48..6fe2114 100644
--- a/include/linux/cs5535.h
+++ b/include/linux/cs5535.h
@@ -103,14 +103,20 @@
#define GPIO_POSITIVE_EDGE_STS 0x48
#define GPIO_NEGATIVE_EDGE_STS 0x4C
+#define GPIO_FLTR7_AMOUNT 0xD8
+
#define GPIO_MAP_X 0xE0
#define GPIO_MAP_Y 0xE4
#define GPIO_MAP_Z 0xE8
#define GPIO_MAP_W 0xEC
+#define GPIO_FE7_SEL 0xF7
+
void cs5535_gpio_set(unsigned offset, unsigned int reg);
void cs5535_gpio_clear(unsigned offset, unsigned int reg);
int cs5535_gpio_isset(unsigned offset, unsigned int reg);
+int cs5535_gpio_set_irq(unsigned group, unsigned irq);
+void cs5535_gpio_setup_event(unsigned offset, int pair, int pme);
/* MFGPTs */
diff --git a/include/linux/decompress/inflate.h b/include/linux/decompress/inflate.h
index f9b06cc..8c0aef1 100644
--- a/include/linux/decompress/inflate.h
+++ b/include/linux/decompress/inflate.h
@@ -1,9 +1,6 @@
#ifndef INFLATE_H
#define INFLATE_H
-/* Other housekeeping constants */
-#define INBUFSIZ 4096
-
int gunzip(unsigned char *inbuf, int len,
int(*fill)(void*, unsigned int),
int(*flush)(void*, unsigned int),
diff --git a/include/linux/decompress/mm.h b/include/linux/decompress/mm.h
index ad5ec1d..4cb72b9 100644
--- a/include/linux/decompress/mm.h
+++ b/include/linux/decompress/mm.h
@@ -61,8 +61,6 @@
#define large_malloc(a) malloc(a)
#define large_free(a) free(a)
-#define set_error_fn(x)
-
#define INIT
#else /* STATIC */
@@ -72,6 +70,7 @@
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/string.h>
+#include <linux/slab.h>
#include <linux/vmalloc.h>
/* Use defines rather than static inline in order to avoid spurious
@@ -84,9 +83,6 @@
#define large_malloc(a) vmalloc(a)
#define large_free(a) vfree(a)
-static void(*error)(char *m);
-#define set_error_fn(x) error = x;
-
#define INIT __init
#define STATIC
diff --git a/include/linux/decompress/unxz.h b/include/linux/decompress/unxz.h
new file mode 100644
index 0000000..41728fc
--- /dev/null
+++ b/include/linux/decompress/unxz.h
@@ -0,0 +1,19 @@
+/*
+ * Wrapper for decompressing XZ-compressed kernel, initramfs, and initrd
+ *
+ * Author: Lasse Collin <lasse.collin@tukaani.org>
+ *
+ * This file has been put into the public domain.
+ * You can do whatever you want with this file.
+ */
+
+#ifndef DECOMPRESS_UNXZ_H
+#define DECOMPRESS_UNXZ_H
+
+int unxz(unsigned char *in, int in_size,
+ int (*fill)(void *dest, unsigned int size),
+ int (*flush)(void *src, unsigned int size),
+ unsigned char *out, int *in_used,
+ void (*error)(char *x));
+
+#endif
diff --git a/include/linux/firewire-cdev.h b/include/linux/firewire-cdev.h
index 68c642d..59ea406 100644
--- a/include/linux/firewire-cdev.h
+++ b/include/linux/firewire-cdev.h
@@ -273,7 +273,7 @@
* @closure: See &fw_cdev_event_common;
* set by %FW_CDEV_CREATE_ISO_CONTEXT ioctl
* @type: %FW_CDEV_EVENT_ISO_INTERRUPT_MULTICHANNEL
- * @completed: Offset into the receive buffer; data before this offest is valid
+ * @completed: Offset into the receive buffer; data before this offset is valid
*
* This event is sent in multichannel contexts (context type
* %FW_CDEV_ISO_CONTEXT_RECEIVE_MULTICHANNEL) for &fw_cdev_iso_packet buffer
diff --git a/include/linux/gpio.h b/include/linux/gpio.h
index e41f7dd..f79d67f 100644
--- a/include/linux/gpio.h
+++ b/include/linux/gpio.h
@@ -13,6 +13,7 @@
#include <linux/errno.h>
struct device;
+struct gpio;
struct gpio_chip;
/*
@@ -29,7 +30,18 @@
return 0;
}
-static inline int gpio_request(unsigned gpio, const char *label)
+static inline int __must_check gpio_request(unsigned gpio, const char *label)
+{
+ return -ENOSYS;
+}
+
+static inline int __must_check gpio_request_one(unsigned gpio,
+ unsigned long flags, const char *label)
+{
+ return -ENOSYS;
+}
+
+static inline int __must_check gpio_request_array(struct gpio *array, size_t num)
{
return -ENOSYS;
}
@@ -42,12 +54,20 @@
WARN_ON(1);
}
-static inline int gpio_direction_input(unsigned gpio)
+static inline void gpio_free_array(struct gpio *array, size_t num)
+{
+ might_sleep();
+
+ /* GPIO can never have been requested */
+ WARN_ON(1);
+}
+
+static inline int __must_check gpio_direction_input(unsigned gpio)
{
return -ENOSYS;
}
-static inline int gpio_direction_output(unsigned gpio, int value)
+static inline int __must_check gpio_direction_output(unsigned gpio, int value)
{
return -ENOSYS;
}
diff --git a/include/linux/hid.h b/include/linux/hid.h
index 20b9801..d91c25e 100644
--- a/include/linux/hid.h
+++ b/include/linux/hid.h
@@ -402,7 +402,7 @@
__u16 dpad; /* dpad input code */
};
-#define HID_MAX_FIELDS 64
+#define HID_MAX_FIELDS 128
struct hid_report {
struct list_head list;
@@ -593,6 +593,7 @@
* @report_fixup: called before report descriptor parsing (NULL means nop)
* @input_mapping: invoked on input registering before mapping an usage
* @input_mapped: invoked on input registering after mapping an usage
+ * @feature_mapping: invoked on feature registering
* @suspend: invoked on suspend (NULL means nop)
* @resume: invoked on resume if device was not reset (NULL means nop)
* @reset_resume: invoked on resume if device was reset (NULL means nop)
@@ -636,6 +637,9 @@
int (*input_mapped)(struct hid_device *hdev,
struct hid_input *hidinput, struct hid_field *field,
struct hid_usage *usage, unsigned long **bit, int *max);
+ void (*feature_mapping)(struct hid_device *hdev,
+ struct hid_input *hidinput, struct hid_field *field,
+ struct hid_usage *usage);
#ifdef CONFIG_PM
int (*suspend)(struct hid_device *hdev, pm_message_t message);
int (*resume)(struct hid_device *hdev);
diff --git a/include/linux/if_alg.h b/include/linux/if_alg.h
new file mode 100644
index 0000000..0f9acce
--- /dev/null
+++ b/include/linux/if_alg.h
@@ -0,0 +1,40 @@
+/*
+ * if_alg: User-space algorithm interface
+ *
+ * Copyright (c) 2010 Herbert Xu <herbert@gondor.apana.org.au>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ */
+
+#ifndef _LINUX_IF_ALG_H
+#define _LINUX_IF_ALG_H
+
+#include <linux/types.h>
+
+struct sockaddr_alg {
+ __u16 salg_family;
+ __u8 salg_type[14];
+ __u32 salg_feat;
+ __u32 salg_mask;
+ __u8 salg_name[64];
+};
+
+struct af_alg_iv {
+ __u32 ivlen;
+ __u8 iv[0];
+};
+
+/* Socket options */
+#define ALG_SET_KEY 1
+#define ALG_SET_IV 2
+#define ALG_SET_OP 3
+
+/* Operations */
+#define ALG_OP_DECRYPT 0
+#define ALG_OP_ENCRYPT 1
+
+#endif /* _LINUX_IF_ALG_H */
diff --git a/include/linux/input.h b/include/linux/input.h
index c4e9d91..e428382 100644
--- a/include/linux/input.h
+++ b/include/linux/input.h
@@ -802,6 +802,7 @@
#define SW_CAMERA_LENS_COVER 0x09 /* set = lens covered */
#define SW_KEYPAD_SLIDE 0x0a /* set = keypad slide out */
#define SW_FRONT_PROXIMITY 0x0b /* set = front proximity sensor active */
+#define SW_ROTATE_LOCK 0x0c /* set = rotate locked/disabled */
#define SW_MAX 0x0f
#define SW_CNT (SW_MAX+1)
diff --git a/include/linux/input/as5011.h b/include/linux/input/as5011.h
new file mode 100644
index 0000000..1affd0d
--- /dev/null
+++ b/include/linux/input/as5011.h
@@ -0,0 +1,20 @@
+#ifndef _AS5011_H
+#define _AS5011_H
+
+/*
+ * Copyright (c) 2010, 2011 Fabien Marteau <fabien.marteau@armadeus.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ */
+
+struct as5011_platform_data {
+ unsigned int button_gpio;
+ unsigned int axis_irq; /* irq number */
+ unsigned long axis_irqflags;
+ char xp, xn; /* threshold for x axis */
+ char yp, yn; /* threshold for y axis */
+};
+
+#endif /* _AS5011_H */
diff --git a/include/linux/kernel.h b/include/linux/kernel.h
index d0fbc04..57dac70 100644
--- a/include/linux/kernel.h
+++ b/include/linux/kernel.h
@@ -143,9 +143,22 @@
#define might_sleep_if(cond) do { if (cond) might_sleep(); } while (0)
-#define abs(x) ({ \
- long __x = (x); \
- (__x < 0) ? -__x : __x; \
+/*
+ * abs() handles unsigned and signed longs, ints, shorts and chars. For all
+ * input types abs() returns a signed long.
+ * abs() should not be used for 64-bit types (s64, u64, long long) - use abs64()
+ * for those.
+ */
+#define abs(x) ({ \
+ long ret; \
+ if (sizeof(x) == sizeof(long)) { \
+ long __x = (x); \
+ ret = (__x < 0) ? -__x : __x; \
+ } else { \
+ int __x = (x); \
+ ret = (__x < 0) ? -__x : __x; \
+ } \
+ ret; \
})
#define abs64(x) ({ \
diff --git a/include/linux/kmsg_dump.h b/include/linux/kmsg_dump.h
index 24b4414..2a0d7d6 100644
--- a/include/linux/kmsg_dump.h
+++ b/include/linux/kmsg_dump.h
@@ -18,6 +18,10 @@
KMSG_DUMP_OOPS,
KMSG_DUMP_PANIC,
KMSG_DUMP_KEXEC,
+ KMSG_DUMP_RESTART,
+ KMSG_DUMP_HALT,
+ KMSG_DUMP_POWEROFF,
+ KMSG_DUMP_EMERG,
};
/**
diff --git a/include/linux/kvm.h b/include/linux/kvm.h
index 919ae53..ea2dc1a 100644
--- a/include/linux/kvm.h
+++ b/include/linux/kvm.h
@@ -540,6 +540,7 @@
#endif
#define KVM_CAP_PPC_GET_PVINFO 57
#define KVM_CAP_PPC_IRQ_LEVEL 58
+#define KVM_CAP_ASYNC_PF 59
#ifdef KVM_CAP_IRQ_ROUTING
diff --git a/include/linux/kvm_host.h b/include/linux/kvm_host.h
index a055742..b5021db 100644
--- a/include/linux/kvm_host.h
+++ b/include/linux/kvm_host.h
@@ -16,6 +16,8 @@
#include <linux/mm.h>
#include <linux/preempt.h>
#include <linux/msi.h>
+#include <linux/slab.h>
+#include <linux/rcupdate.h>
#include <asm/signal.h>
#include <linux/kvm.h>
@@ -40,6 +42,7 @@
#define KVM_REQ_KICK 9
#define KVM_REQ_DEACTIVATE_FPU 10
#define KVM_REQ_EVENT 11
+#define KVM_REQ_APF_HALT 12
#define KVM_USERSPACE_IRQ_SOURCE_ID 0
@@ -74,6 +77,27 @@
int kvm_io_bus_unregister_dev(struct kvm *kvm, enum kvm_bus bus_idx,
struct kvm_io_device *dev);
+#ifdef CONFIG_KVM_ASYNC_PF
+struct kvm_async_pf {
+ struct work_struct work;
+ struct list_head link;
+ struct list_head queue;
+ struct kvm_vcpu *vcpu;
+ struct mm_struct *mm;
+ gva_t gva;
+ unsigned long addr;
+ struct kvm_arch_async_pf arch;
+ struct page *page;
+ bool done;
+};
+
+void kvm_clear_async_pf_completion_queue(struct kvm_vcpu *vcpu);
+void kvm_check_async_pf_completion(struct kvm_vcpu *vcpu);
+int kvm_setup_async_pf(struct kvm_vcpu *vcpu, gva_t gva, gfn_t gfn,
+ struct kvm_arch_async_pf *arch);
+int kvm_async_pf_wakeup_all(struct kvm_vcpu *vcpu);
+#endif
+
struct kvm_vcpu {
struct kvm *kvm;
#ifdef CONFIG_PREEMPT_NOTIFIERS
@@ -104,6 +128,15 @@
gpa_t mmio_phys_addr;
#endif
+#ifdef CONFIG_KVM_ASYNC_PF
+ struct {
+ u32 queued;
+ struct list_head queue;
+ struct list_head done;
+ spinlock_t lock;
+ } async_pf;
+#endif
+
struct kvm_vcpu_arch arch;
};
@@ -113,16 +146,19 @@
*/
#define KVM_MEM_MAX_NR_PAGES ((1UL << 31) - 1)
+struct kvm_lpage_info {
+ unsigned long rmap_pde;
+ int write_count;
+};
+
struct kvm_memory_slot {
gfn_t base_gfn;
unsigned long npages;
unsigned long flags;
unsigned long *rmap;
unsigned long *dirty_bitmap;
- struct {
- unsigned long rmap_pde;
- int write_count;
- } *lpage_info[KVM_NR_PAGE_SIZES - 1];
+ unsigned long *dirty_bitmap_head;
+ struct kvm_lpage_info *lpage_info[KVM_NR_PAGE_SIZES - 1];
unsigned long userspace_addr;
int user_alloc;
int id;
@@ -169,6 +205,7 @@
struct kvm_memslots {
int nmemslots;
+ u64 generation;
struct kvm_memory_slot memslots[KVM_MEMORY_SLOTS +
KVM_PRIVATE_MEM_SLOTS];
};
@@ -206,6 +243,10 @@
struct mutex irq_lock;
#ifdef CONFIG_HAVE_KVM_IRQCHIP
+ /*
+ * Update side is protected by irq_lock and,
+ * if configured, irqfds.lock.
+ */
struct kvm_irq_routing_table __rcu *irq_routing;
struct hlist_head mask_notifier_list;
struct hlist_head irq_ack_notifier_list;
@@ -216,6 +257,7 @@
unsigned long mmu_notifier_seq;
long mmu_notifier_count;
#endif
+ long tlbs_dirty;
};
/* The guest did something we don't support. */
@@ -302,7 +344,11 @@
pfn_t hva_to_pfn_atomic(struct kvm *kvm, unsigned long addr);
pfn_t gfn_to_pfn_atomic(struct kvm *kvm, gfn_t gfn);
+pfn_t gfn_to_pfn_async(struct kvm *kvm, gfn_t gfn, bool *async,
+ bool write_fault, bool *writable);
pfn_t gfn_to_pfn(struct kvm *kvm, gfn_t gfn);
+pfn_t gfn_to_pfn_prot(struct kvm *kvm, gfn_t gfn, bool write_fault,
+ bool *writable);
pfn_t gfn_to_pfn_memslot(struct kvm *kvm,
struct kvm_memory_slot *slot, gfn_t gfn);
int memslot_id(struct kvm *kvm, gfn_t gfn);
@@ -321,18 +367,25 @@
int offset, int len);
int kvm_write_guest(struct kvm *kvm, gpa_t gpa, const void *data,
unsigned long len);
+int kvm_write_guest_cached(struct kvm *kvm, struct gfn_to_hva_cache *ghc,
+ void *data, unsigned long len);
+int kvm_gfn_to_hva_cache_init(struct kvm *kvm, struct gfn_to_hva_cache *ghc,
+ gpa_t gpa);
int kvm_clear_guest_page(struct kvm *kvm, gfn_t gfn, int offset, int len);
int kvm_clear_guest(struct kvm *kvm, gpa_t gpa, unsigned long len);
struct kvm_memory_slot *gfn_to_memslot(struct kvm *kvm, gfn_t gfn);
int kvm_is_visible_gfn(struct kvm *kvm, gfn_t gfn);
unsigned long kvm_host_page_size(struct kvm *kvm, gfn_t gfn);
void mark_page_dirty(struct kvm *kvm, gfn_t gfn);
+void mark_page_dirty_in_slot(struct kvm *kvm, struct kvm_memory_slot *memslot,
+ gfn_t gfn);
void kvm_vcpu_block(struct kvm_vcpu *vcpu);
void kvm_vcpu_on_spin(struct kvm_vcpu *vcpu);
void kvm_resched(struct kvm_vcpu *vcpu);
void kvm_load_guest_fpu(struct kvm_vcpu *vcpu);
void kvm_put_guest_fpu(struct kvm_vcpu *vcpu);
+
void kvm_flush_remote_tlbs(struct kvm *kvm);
void kvm_reload_remote_mmus(struct kvm *kvm);
@@ -398,7 +451,19 @@
void kvm_free_physmem(struct kvm *kvm);
-struct kvm *kvm_arch_create_vm(void);
+#ifndef __KVM_HAVE_ARCH_VM_ALLOC
+static inline struct kvm *kvm_arch_alloc_vm(void)
+{
+ return kzalloc(sizeof(struct kvm), GFP_KERNEL);
+}
+
+static inline void kvm_arch_free_vm(struct kvm *kvm)
+{
+ kfree(kvm);
+}
+#endif
+
+int kvm_arch_init_vm(struct kvm *kvm);
void kvm_arch_destroy_vm(struct kvm *kvm);
void kvm_free_all_assigned_devices(struct kvm *kvm);
void kvm_arch_sync_events(struct kvm *kvm);
@@ -414,16 +479,8 @@
void (*irq_acked)(struct kvm_irq_ack_notifier *kian);
};
-#define KVM_ASSIGNED_MSIX_PENDING 0x1
-struct kvm_guest_msix_entry {
- u32 vector;
- u16 entry;
- u16 flags;
-};
-
struct kvm_assigned_dev_kernel {
struct kvm_irq_ack_notifier ack_notifier;
- struct work_struct interrupt_work;
struct list_head list;
int assigned_dev_id;
int host_segnr;
@@ -434,13 +491,14 @@
bool host_irq_disabled;
struct msix_entry *host_msix_entries;
int guest_irq;
- struct kvm_guest_msix_entry *guest_msix_entries;
+ struct msix_entry *guest_msix_entries;
unsigned long irq_requested_type;
int irq_source_id;
int flags;
struct pci_dev *dev;
struct kvm *kvm;
- spinlock_t assigned_dev_lock;
+ spinlock_t intx_lock;
+ char irq_name[32];
};
struct kvm_irq_mask_notifier {
@@ -462,6 +520,8 @@
unsigned long *deliver_bitmask);
#endif
int kvm_set_irq(struct kvm *kvm, int irq_source_id, u32 irq, int level);
+int kvm_set_msi(struct kvm_kernel_irq_routing_entry *irq_entry, struct kvm *kvm,
+ int irq_source_id, int level);
void kvm_notify_acked_irq(struct kvm *kvm, unsigned irqchip, unsigned pin);
void kvm_register_irq_ack_notifier(struct kvm *kvm,
struct kvm_irq_ack_notifier *kian);
@@ -603,17 +663,28 @@
void kvm_eventfd_init(struct kvm *kvm);
int kvm_irqfd(struct kvm *kvm, int fd, int gsi, int flags);
void kvm_irqfd_release(struct kvm *kvm);
+void kvm_irq_routing_update(struct kvm *, struct kvm_irq_routing_table *);
int kvm_ioeventfd(struct kvm *kvm, struct kvm_ioeventfd *args);
#else
static inline void kvm_eventfd_init(struct kvm *kvm) {}
+
static inline int kvm_irqfd(struct kvm *kvm, int fd, int gsi, int flags)
{
return -EINVAL;
}
static inline void kvm_irqfd_release(struct kvm *kvm) {}
+
+#ifdef CONFIG_HAVE_KVM_IRQCHIP
+static inline void kvm_irq_routing_update(struct kvm *kvm,
+ struct kvm_irq_routing_table *irq_rt)
+{
+ rcu_assign_pointer(kvm->irq_routing, irq_rt);
+}
+#endif
+
static inline int kvm_ioeventfd(struct kvm *kvm, struct kvm_ioeventfd *args)
{
return -ENOSYS;
diff --git a/include/linux/kvm_types.h b/include/linux/kvm_types.h
index 7ac0d4ee..fa7cc72 100644
--- a/include/linux/kvm_types.h
+++ b/include/linux/kvm_types.h
@@ -67,4 +67,11 @@
u32 dest_id;
};
+struct gfn_to_hva_cache {
+ u64 generation;
+ gpa_t gpa;
+ unsigned long hva;
+ struct kvm_memory_slot *memslot;
+};
+
#endif /* __KVM_TYPES_H__ */
diff --git a/include/linux/leds-lp5521.h b/include/linux/leds-lp5521.h
index 38368d7..fd548d2 100644
--- a/include/linux/leds-lp5521.h
+++ b/include/linux/leds-lp5521.h
@@ -42,6 +42,7 @@
int (*setup_resources)(void);
void (*release_resources)(void);
void (*enable)(bool state);
+ const char *label;
};
#endif /* __LINUX_LP5521_H */
diff --git a/include/linux/leds-lp5523.h b/include/linux/leds-lp5523.h
index 7967476..2694289 100644
--- a/include/linux/leds-lp5523.h
+++ b/include/linux/leds-lp5523.h
@@ -42,6 +42,7 @@
int (*setup_resources)(void);
void (*release_resources)(void);
void (*enable)(bool state);
+ const char *label;
};
#endif /* __LINUX_LP5523_H */
diff --git a/include/linux/mfd/ab8500.h b/include/linux/mfd/ab8500.h
index d63b605..85cf2c28 100644
--- a/include/linux/mfd/ab8500.h
+++ b/include/linux/mfd/ab8500.h
@@ -99,8 +99,6 @@
#define AB8500_NR_IRQS 104
#define AB8500_NUM_IRQ_REGS 13
-#define AB8500_NUM_REGULATORS 15
-
/**
* struct ab8500 - ab8500 internal structure
* @dev: parent device
@@ -145,7 +143,8 @@
struct ab8500_platform_data {
int irq_base;
void (*init) (struct ab8500 *);
- struct regulator_init_data *regulator[AB8500_NUM_REGULATORS];
+ int num_regulator;
+ struct regulator_init_data *regulator;
};
extern int __devinit ab8500_init(struct ab8500 *ab8500);
diff --git a/include/linux/mfd/core.h b/include/linux/mfd/core.h
index cb93d80..5582ab3 100644
--- a/include/linux/mfd/core.h
+++ b/include/linux/mfd/core.h
@@ -39,7 +39,7 @@
size_t data_size;
/*
- * This resources can be specified relatievly to the parent device.
+ * This resources can be specified relatively to the parent device.
* For accessing device you should use resources from device
*/
int num_resources;
diff --git a/include/linux/mfd/mc13783.h b/include/linux/mfd/mc13783.h
index b4c741e..7d0f3d6 100644
--- a/include/linux/mfd/mc13783.h
+++ b/include/linux/mfd/mc13783.h
@@ -1,4 +1,5 @@
/*
+ * Copyright 2010 Yong Shen <yong.shen@linaro.org>
* Copyright 2009-2010 Pengutronix
* Uwe Kleine-Koenig <u.kleine-koenig@pengutronix.de>
*
@@ -122,39 +123,39 @@
unsigned int channel, unsigned int *sample);
-#define MC13783_SW_SW1A 0
-#define MC13783_SW_SW1B 1
-#define MC13783_SW_SW2A 2
-#define MC13783_SW_SW2B 3
-#define MC13783_SW_SW3 4
-#define MC13783_SW_PLL 5
-#define MC13783_REGU_VAUDIO 6
-#define MC13783_REGU_VIOHI 7
-#define MC13783_REGU_VIOLO 8
-#define MC13783_REGU_VDIG 9
-#define MC13783_REGU_VGEN 10
-#define MC13783_REGU_VRFDIG 11
-#define MC13783_REGU_VRFREF 12
-#define MC13783_REGU_VRFCP 13
-#define MC13783_REGU_VSIM 14
-#define MC13783_REGU_VESIM 15
-#define MC13783_REGU_VCAM 16
-#define MC13783_REGU_VRFBG 17
-#define MC13783_REGU_VVIB 18
-#define MC13783_REGU_VRF1 19
-#define MC13783_REGU_VRF2 20
-#define MC13783_REGU_VMMC1 21
-#define MC13783_REGU_VMMC2 22
-#define MC13783_REGU_GPO1 23
-#define MC13783_REGU_GPO2 24
-#define MC13783_REGU_GPO3 25
-#define MC13783_REGU_GPO4 26
-#define MC13783_REGU_V1 27
-#define MC13783_REGU_V2 28
-#define MC13783_REGU_V3 29
-#define MC13783_REGU_V4 30
-#define MC13783_REGU_PWGT1SPI 31
-#define MC13783_REGU_PWGT2SPI 32
+#define MC13783_REG_SW1A 0
+#define MC13783_REG_SW1B 1
+#define MC13783_REG_SW2A 2
+#define MC13783_REG_SW2B 3
+#define MC13783_REG_SW3 4
+#define MC13783_REG_PLL 5
+#define MC13783_REG_VAUDIO 6
+#define MC13783_REG_VIOHI 7
+#define MC13783_REG_VIOLO 8
+#define MC13783_REG_VDIG 9
+#define MC13783_REG_VGEN 10
+#define MC13783_REG_VRFDIG 11
+#define MC13783_REG_VRFREF 12
+#define MC13783_REG_VRFCP 13
+#define MC13783_REG_VSIM 14
+#define MC13783_REG_VESIM 15
+#define MC13783_REG_VCAM 16
+#define MC13783_REG_VRFBG 17
+#define MC13783_REG_VVIB 18
+#define MC13783_REG_VRF1 19
+#define MC13783_REG_VRF2 20
+#define MC13783_REG_VMMC1 21
+#define MC13783_REG_VMMC2 22
+#define MC13783_REG_GPO1 23
+#define MC13783_REG_GPO2 24
+#define MC13783_REG_GPO3 25
+#define MC13783_REG_GPO4 26
+#define MC13783_REG_V1 27
+#define MC13783_REG_V2 28
+#define MC13783_REG_V3 29
+#define MC13783_REG_V4 30
+#define MC13783_REG_PWGT1SPI 31
+#define MC13783_REG_PWGT2SPI 32
#define MC13783_IRQ_ADCDONE MC13XXX_IRQ_ADCDONE
#define MC13783_IRQ_ADCBISDONE MC13XXX_IRQ_ADCBISDONE
diff --git a/include/linux/mfd/mc13892.h b/include/linux/mfd/mc13892.h
new file mode 100644
index 0000000..a00f2be
--- /dev/null
+++ b/include/linux/mfd/mc13892.h
@@ -0,0 +1,39 @@
+/*
+ * Copyright 2010 Yong Shen <yong.shen@linaro.org>
+ *
+ * This program is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License version 2 as published by the
+ * Free Software Foundation.
+ */
+
+#ifndef __LINUX_MFD_MC13892_H
+#define __LINUX_MFD_MC13892_H
+
+#include <linux/mfd/mc13xxx.h>
+
+#define MC13892_SW1 0
+#define MC13892_SW2 1
+#define MC13892_SW3 2
+#define MC13892_SW4 3
+#define MC13892_SWBST 4
+#define MC13892_VIOHI 5
+#define MC13892_VPLL 6
+#define MC13892_VDIG 7
+#define MC13892_VSD 8
+#define MC13892_VUSB2 9
+#define MC13892_VVIDEO 10
+#define MC13892_VAUDIO 11
+#define MC13892_VCAM 12
+#define MC13892_VGEN1 13
+#define MC13892_VGEN2 14
+#define MC13892_VGEN3 15
+#define MC13892_VUSB 16
+#define MC13892_GPO1 17
+#define MC13892_GPO2 18
+#define MC13892_GPO3 19
+#define MC13892_GPO4 20
+#define MC13892_PWGT1SPI 21
+#define MC13892_PWGT2SPI 22
+#define MC13892_VCOINCELL 23
+
+#endif
diff --git a/include/linux/nfc/pn544.h b/include/linux/nfc/pn544.h
new file mode 100644
index 0000000..7ab8521
--- /dev/null
+++ b/include/linux/nfc/pn544.h
@@ -0,0 +1,97 @@
+/*
+ * Driver include for the PN544 NFC chip.
+ *
+ * Copyright (C) Nokia Corporation
+ *
+ * Author: Jari Vanhala <ext-jari.vanhala@nokia.com>
+ * Contact: Matti Aaltoenn <matti.j.aaltonen@nokia.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * 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 _PN544_H_
+#define _PN544_H_
+
+#include <linux/i2c.h>
+
+#define PN544_DRIVER_NAME "pn544"
+#define PN544_MAXWINDOW_SIZE 7
+#define PN544_WINDOW_SIZE 4
+#define PN544_RETRIES 10
+#define PN544_MAX_I2C_TRANSFER 0x0400
+#define PN544_MSG_MAX_SIZE 0x21 /* at normal HCI mode */
+
+/* ioctl */
+#define PN544_CHAR_BASE 'P'
+#define PN544_IOR(num, dtype) _IOR(PN544_CHAR_BASE, num, dtype)
+#define PN544_IOW(num, dtype) _IOW(PN544_CHAR_BASE, num, dtype)
+#define PN544_GET_FW_MODE PN544_IOW(1, unsigned int)
+#define PN544_SET_FW_MODE PN544_IOW(2, unsigned int)
+#define PN544_GET_DEBUG PN544_IOW(3, unsigned int)
+#define PN544_SET_DEBUG PN544_IOW(4, unsigned int)
+
+/* Timing restrictions (ms) */
+#define PN544_RESETVEN_TIME 30 /* 7 */
+#define PN544_PVDDVEN_TIME 0
+#define PN544_VBATVEN_TIME 0
+#define PN544_GPIO4VEN_TIME 0
+#define PN544_WAKEUP_ACK 5
+#define PN544_WAKEUP_GUARD (PN544_WAKEUP_ACK + 1)
+#define PN544_INACTIVITY_TIME 1000
+#define PN544_INTERFRAME_DELAY 200 /* us */
+#define PN544_BAUDRATE_CHANGE 150 /* us */
+
+/* Debug bits */
+#define PN544_DEBUG_BUF 0x01
+#define PN544_DEBUG_READ 0x02
+#define PN544_DEBUG_WRITE 0x04
+#define PN544_DEBUG_IRQ 0x08
+#define PN544_DEBUG_CALLS 0x10
+#define PN544_DEBUG_MODE 0x20
+
+/* Normal (HCI) mode */
+#define PN544_LLC_HCI_OVERHEAD 3 /* header + crc (to length) */
+#define PN544_LLC_MIN_SIZE (1 + PN544_LLC_HCI_OVERHEAD) /* length + */
+#define PN544_LLC_MAX_DATA (PN544_MSG_MAX_SIZE - 2)
+#define PN544_LLC_MAX_HCI_SIZE (PN544_LLC_MAX_DATA - 2)
+
+struct pn544_llc_packet {
+ unsigned char length; /* of rest of packet */
+ unsigned char header;
+ unsigned char data[PN544_LLC_MAX_DATA]; /* includes crc-ccitt */
+};
+
+/* Firmware upgrade mode */
+#define PN544_FW_HEADER_SIZE 3
+/* max fw transfer is 1024bytes, but I2C limits it to 0xC0 */
+#define PN544_MAX_FW_DATA (PN544_MAX_I2C_TRANSFER - PN544_FW_HEADER_SIZE)
+
+struct pn544_fw_packet {
+ unsigned char command; /* status in answer */
+ unsigned char length[2]; /* big-endian order (msf) */
+ unsigned char data[PN544_MAX_FW_DATA];
+};
+
+#ifdef __KERNEL__
+/* board config */
+struct pn544_nfc_platform_data {
+ int (*request_resources) (struct i2c_client *client);
+ void (*free_resources) (void);
+ void (*enable) (int fw);
+ int (*test) (void);
+ void (*disable) (void);
+};
+#endif /* __KERNEL__ */
+
+#endif /* _PN544_H_ */
diff --git a/include/linux/pci_ids.h b/include/linux/pci_ids.h
index ab47732..ae0dc45 100644
--- a/include/linux/pci_ids.h
+++ b/include/linux/pci_ids.h
@@ -2369,8 +2369,10 @@
#define PCI_DEVICE_ID_JMICRON_JMB38X_SD 0x2381
#define PCI_DEVICE_ID_JMICRON_JMB38X_MMC 0x2382
#define PCI_DEVICE_ID_JMICRON_JMB38X_MS 0x2383
+#define PCI_DEVICE_ID_JMICRON_JMB385_MS 0x2388
#define PCI_DEVICE_ID_JMICRON_JMB388_SD 0x2391
#define PCI_DEVICE_ID_JMICRON_JMB388_ESD 0x2392
+#define PCI_DEVICE_ID_JMICRON_JMB390_MS 0x2393
#define PCI_VENDOR_ID_KORENIX 0x1982
#define PCI_DEVICE_ID_KORENIX_JETCARDF0 0x1600
diff --git a/include/linux/poll.h b/include/linux/poll.h
index 56e76af..1a2ccd6 100644
--- a/include/linux/poll.h
+++ b/include/linux/poll.h
@@ -57,7 +57,7 @@
};
/*
- * Structures and helpers for sys_poll/sys_poll
+ * Structures and helpers for select/poll syscall
*/
struct poll_wqueues {
poll_table pt;
diff --git a/include/linux/pps.h b/include/linux/pps.h
index 0194ab0..a9bb1d9 100644
--- a/include/linux/pps.h
+++ b/include/linux/pps.h
@@ -114,11 +114,18 @@
struct pps_ktime timeout;
};
+struct pps_bind_args {
+ int tsformat; /* format of time stamps */
+ int edge; /* selected event type */
+ int consumer; /* selected kernel consumer */
+};
+
#include <linux/ioctl.h>
#define PPS_GETPARAMS _IOR('p', 0xa1, struct pps_kparams *)
#define PPS_SETPARAMS _IOW('p', 0xa2, struct pps_kparams *)
#define PPS_GETCAP _IOR('p', 0xa3, int *)
#define PPS_FETCH _IOWR('p', 0xa4, struct pps_fdata *)
+#define PPS_KC_BIND _IOW('p', 0xa5, struct pps_bind_args *)
#endif /* _PPS_H_ */
diff --git a/include/linux/pps_kernel.h b/include/linux/pps_kernel.h
index e0a193f..9404854 100644
--- a/include/linux/pps_kernel.h
+++ b/include/linux/pps_kernel.h
@@ -18,6 +18,9 @@
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
+#ifndef LINUX_PPS_KERNEL_H
+#define LINUX_PPS_KERNEL_H
+
#include <linux/pps.h>
#include <linux/cdev.h>
@@ -28,18 +31,28 @@
* Global defines
*/
+struct pps_device;
+
/* The specific PPS source info */
struct pps_source_info {
char name[PPS_MAX_NAME_LEN]; /* simbolic name */
char path[PPS_MAX_NAME_LEN]; /* path of connected device */
int mode; /* PPS's allowed mode */
- void (*echo)(int source, int event, void *data); /* PPS echo function */
+ void (*echo)(struct pps_device *pps,
+ int event, void *data); /* PPS echo function */
struct module *owner;
struct device *dev;
};
+struct pps_event_time {
+#ifdef CONFIG_NTP_PPS
+ struct timespec ts_raw;
+#endif /* CONFIG_NTP_PPS */
+ struct timespec ts_real;
+};
+
/* The main struct */
struct pps_device {
struct pps_source_info info; /* PSS source info */
@@ -52,38 +65,56 @@
struct pps_ktime clear_tu;
int current_mode; /* PPS mode at event time */
- int go; /* PPS event is arrived? */
+ unsigned int last_ev; /* last PPS event id */
wait_queue_head_t queue; /* PPS event queue */
unsigned int id; /* PPS source unique ID */
struct cdev cdev;
struct device *dev;
- int devno;
struct fasync_struct *async_queue; /* fasync method */
spinlock_t lock;
-
- atomic_t usage; /* usage count */
};
/*
* Global variables
*/
-extern spinlock_t pps_idr_lock;
-extern struct idr pps_idr;
-extern struct timespec pps_irq_ts[];
-
extern struct device_attribute pps_attrs[];
/*
* Exported functions
*/
-struct pps_device *pps_get_source(int source);
-extern void pps_put_source(struct pps_device *pps);
-extern int pps_register_source(struct pps_source_info *info,
- int default_params);
-extern void pps_unregister_source(int source);
+extern struct pps_device *pps_register_source(
+ struct pps_source_info *info, int default_params);
+extern void pps_unregister_source(struct pps_device *pps);
extern int pps_register_cdev(struct pps_device *pps);
extern void pps_unregister_cdev(struct pps_device *pps);
-extern void pps_event(int source, struct pps_ktime *ts, int event, void *data);
+extern void pps_event(struct pps_device *pps,
+ struct pps_event_time *ts, int event, void *data);
+
+static inline void timespec_to_pps_ktime(struct pps_ktime *kt,
+ struct timespec ts)
+{
+ kt->sec = ts.tv_sec;
+ kt->nsec = ts.tv_nsec;
+}
+
+#ifdef CONFIG_NTP_PPS
+
+static inline void pps_get_ts(struct pps_event_time *ts)
+{
+ getnstime_raw_and_real(&ts->ts_raw, &ts->ts_real);
+}
+
+#else /* CONFIG_NTP_PPS */
+
+static inline void pps_get_ts(struct pps_event_time *ts)
+{
+ getnstimeofday(&ts->ts_real);
+}
+
+#endif /* CONFIG_NTP_PPS */
+
+#endif /* LINUX_PPS_KERNEL_H */
+
diff --git a/include/linux/printk.h b/include/linux/printk.h
index b772ca5..ee048e7 100644
--- a/include/linux/printk.h
+++ b/include/linux/printk.h
@@ -4,14 +4,14 @@
extern const char linux_banner[];
extern const char linux_proc_banner[];
-#define KERN_EMERG "<0>" /* system is unusable */
-#define KERN_ALERT "<1>" /* action must be taken immediately */
-#define KERN_CRIT "<2>" /* critical conditions */
-#define KERN_ERR "<3>" /* error conditions */
-#define KERN_WARNING "<4>" /* warning conditions */
-#define KERN_NOTICE "<5>" /* normal but significant condition */
-#define KERN_INFO "<6>" /* informational */
-#define KERN_DEBUG "<7>" /* debug-level messages */
+#define KERN_EMERG "<0>" /* system is unusable */
+#define KERN_ALERT "<1>" /* action must be taken immediately */
+#define KERN_CRIT "<2>" /* critical conditions */
+#define KERN_ERR "<3>" /* error conditions */
+#define KERN_WARNING "<4>" /* warning conditions */
+#define KERN_NOTICE "<5>" /* normal but significant condition */
+#define KERN_INFO "<6>" /* informational */
+#define KERN_DEBUG "<7>" /* debug-level messages */
/* Use the default kernel loglevel */
#define KERN_DEFAULT "<d>"
@@ -20,7 +20,7 @@
* line that had no enclosing \n). Only to be used by core/arch code
* during early bootup (a continued line is not SMP-safe otherwise).
*/
-#define KERN_CONT "<c>"
+#define KERN_CONT "<c>"
extern int console_printk[];
@@ -29,6 +29,17 @@
#define minimum_console_loglevel (console_printk[2])
#define default_console_loglevel (console_printk[3])
+static inline void console_silent(void)
+{
+ console_loglevel = 0;
+}
+
+static inline void console_verbose(void)
+{
+ if (console_loglevel)
+ console_loglevel = 15;
+}
+
struct va_format {
const char *fmt;
va_list *va;
@@ -65,11 +76,27 @@
*/
#define HW_ERR "[Hardware Error]: "
+/*
+ * Dummy printk for disabled debugging statements to use whilst maintaining
+ * gcc's format and side-effect checking.
+ */
+static inline __attribute__ ((format (printf, 1, 2)))
+int no_printk(const char *fmt, ...)
+{
+ return 0;
+}
+
+extern asmlinkage __attribute__ ((format (printf, 1, 2)))
+void early_printk(const char *fmt, ...);
+
+extern int printk_needs_cpu(int cpu);
+extern void printk_tick(void);
+
#ifdef CONFIG_PRINTK
-asmlinkage int vprintk(const char *fmt, va_list args)
- __attribute__ ((format (printf, 1, 0)));
-asmlinkage int printk(const char * fmt, ...)
- __attribute__ ((format (printf, 1, 2))) __cold;
+asmlinkage __attribute__ ((format (printf, 1, 0)))
+int vprintk(const char *fmt, va_list args);
+asmlinkage __attribute__ ((format (printf, 1, 2))) __cold
+int printk(const char *fmt, ...);
/*
* Please don't use printk_ratelimit(), because it shares ratelimiting state
@@ -83,99 +110,56 @@
extern int printk_delay_msec;
extern int dmesg_restrict;
-
-/*
- * Print a one-time message (analogous to WARN_ONCE() et al):
- */
-#define printk_once(x...) ({ \
- static bool __print_once; \
- \
- if (!__print_once) { \
- __print_once = true; \
- printk(x); \
- } \
-})
+extern int kptr_restrict;
void log_buf_kexec_setup(void);
#else
-static inline int vprintk(const char *s, va_list args)
- __attribute__ ((format (printf, 1, 0)));
-static inline int vprintk(const char *s, va_list args) { return 0; }
-static inline int printk(const char *s, ...)
- __attribute__ ((format (printf, 1, 2)));
-static inline int __cold printk(const char *s, ...) { return 0; }
-static inline int printk_ratelimit(void) { return 0; }
-static inline bool printk_timed_ratelimit(unsigned long *caller_jiffies, \
- unsigned int interval_msec) \
- { return false; }
-
-/* No effect, but we still get type checking even in the !PRINTK case: */
-#define printk_once(x...) printk(x)
+static inline __attribute__ ((format (printf, 1, 0)))
+int vprintk(const char *s, va_list args)
+{
+ return 0;
+}
+static inline __attribute__ ((format (printf, 1, 2))) __cold
+int printk(const char *s, ...)
+{
+ return 0;
+}
+static inline int printk_ratelimit(void)
+{
+ return 0;
+}
+static inline bool printk_timed_ratelimit(unsigned long *caller_jiffies,
+ unsigned int interval_msec)
+{
+ return false;
+}
static inline void log_buf_kexec_setup(void)
{
}
#endif
-/*
- * Dummy printk for disabled debugging statements to use whilst maintaining
- * gcc's format and side-effect checking.
- */
-static inline __attribute__ ((format (printf, 1, 2)))
-int no_printk(const char *s, ...) { return 0; }
-
-extern int printk_needs_cpu(int cpu);
-extern void printk_tick(void);
-
-extern void asmlinkage __attribute__((format(printf, 1, 2)))
- early_printk(const char *fmt, ...);
-
-static inline void console_silent(void)
-{
- console_loglevel = 0;
-}
-
-static inline void console_verbose(void)
-{
- if (console_loglevel)
- console_loglevel = 15;
-}
-
extern void dump_stack(void) __cold;
-enum {
- DUMP_PREFIX_NONE,
- DUMP_PREFIX_ADDRESS,
- DUMP_PREFIX_OFFSET
-};
-extern void hex_dump_to_buffer(const void *buf, size_t len,
- int rowsize, int groupsize,
- char *linebuf, size_t linebuflen, bool ascii);
-extern void print_hex_dump(const char *level, const char *prefix_str,
- int prefix_type, int rowsize, int groupsize,
- const void *buf, size_t len, bool ascii);
-extern void print_hex_dump_bytes(const char *prefix_str, int prefix_type,
- const void *buf, size_t len);
-
#ifndef pr_fmt
#define pr_fmt(fmt) fmt
#endif
#define pr_emerg(fmt, ...) \
- printk(KERN_EMERG pr_fmt(fmt), ##__VA_ARGS__)
+ printk(KERN_EMERG pr_fmt(fmt), ##__VA_ARGS__)
#define pr_alert(fmt, ...) \
- printk(KERN_ALERT pr_fmt(fmt), ##__VA_ARGS__)
+ printk(KERN_ALERT pr_fmt(fmt), ##__VA_ARGS__)
#define pr_crit(fmt, ...) \
- printk(KERN_CRIT pr_fmt(fmt), ##__VA_ARGS__)
+ printk(KERN_CRIT pr_fmt(fmt), ##__VA_ARGS__)
#define pr_err(fmt, ...) \
- printk(KERN_ERR pr_fmt(fmt), ##__VA_ARGS__)
+ printk(KERN_ERR pr_fmt(fmt), ##__VA_ARGS__)
#define pr_warning(fmt, ...) \
- printk(KERN_WARNING pr_fmt(fmt), ##__VA_ARGS__)
+ printk(KERN_WARNING pr_fmt(fmt), ##__VA_ARGS__)
#define pr_warn pr_warning
#define pr_notice(fmt, ...) \
- printk(KERN_NOTICE pr_fmt(fmt), ##__VA_ARGS__)
+ printk(KERN_NOTICE pr_fmt(fmt), ##__VA_ARGS__)
#define pr_info(fmt, ...) \
- printk(KERN_INFO pr_fmt(fmt), ##__VA_ARGS__)
+ printk(KERN_INFO pr_fmt(fmt), ##__VA_ARGS__)
#define pr_cont(fmt, ...) \
printk(KERN_CONT fmt, ##__VA_ARGS__)
@@ -185,7 +169,7 @@
printk(KERN_DEBUG pr_fmt(fmt), ##__VA_ARGS__)
#else
#define pr_devel(fmt, ...) \
- ({ if (0) printk(KERN_DEBUG pr_fmt(fmt), ##__VA_ARGS__); 0; })
+ no_printk(KERN_DEBUG pr_fmt(fmt), ##__VA_ARGS__)
#endif
/* If you are writing a driver, please use dev_dbg instead */
@@ -198,7 +182,51 @@
dynamic_pr_debug(fmt, ##__VA_ARGS__)
#else
#define pr_debug(fmt, ...) \
- ({ if (0) printk(KERN_DEBUG pr_fmt(fmt), ##__VA_ARGS__); 0; })
+ no_printk(KERN_DEBUG pr_fmt(fmt), ##__VA_ARGS__)
+#endif
+
+/*
+ * Print a one-time message (analogous to WARN_ONCE() et al):
+ */
+
+#ifdef CONFIG_PRINTK
+#define printk_once(fmt, ...) \
+({ \
+ static bool __print_once; \
+ \
+ if (!__print_once) { \
+ __print_once = true; \
+ printk(fmt, ##__VA_ARGS__); \
+ } \
+})
+#else
+#define printk_once(fmt, ...) \
+ no_printk(fmt, ##__VA_ARGS__)
+#endif
+
+#define pr_emerg_once(fmt, ...) \
+ printk_once(KERN_EMERG pr_fmt(fmt), ##__VA_ARGS__)
+#define pr_alert_once(fmt, ...) \
+ printk_once(KERN_ALERT pr_fmt(fmt), ##__VA_ARGS__)
+#define pr_crit_once(fmt, ...) \
+ printk_once(KERN_CRIT pr_fmt(fmt), ##__VA_ARGS__)
+#define pr_err_once(fmt, ...) \
+ printk_once(KERN_ERR pr_fmt(fmt), ##__VA_ARGS__)
+#define pr_warn_once(fmt, ...) \
+ printk_once(KERN_WARNING pr_fmt(fmt), ##__VA_ARGS__)
+#define pr_notice_once(fmt, ...) \
+ printk_once(KERN_NOTICE pr_fmt(fmt), ##__VA_ARGS__)
+#define pr_info_once(fmt, ...) \
+ printk_once(KERN_INFO pr_fmt(fmt), ##__VA_ARGS__)
+#define pr_cont_once(fmt, ...) \
+ printk_once(KERN_CONT pr_fmt(fmt), ##__VA_ARGS__)
+/* If you are writing a driver, please use dev_dbg instead */
+#if defined(DEBUG)
+#define pr_debug_once(fmt, ...) \
+ printk_once(KERN_DEBUG pr_fmt(fmt), ##__VA_ARGS__)
+#else
+#define pr_debug_once(fmt, ...) \
+ no_printk(KERN_DEBUG pr_fmt(fmt), ##__VA_ARGS__)
#endif
/*
@@ -206,7 +234,8 @@
* no local ratelimit_state used in the !PRINTK case
*/
#ifdef CONFIG_PRINTK
-#define printk_ratelimited(fmt, ...) ({ \
+#define printk_ratelimited(fmt, ...) \
+({ \
static DEFINE_RATELIMIT_STATE(_rs, \
DEFAULT_RATELIMIT_INTERVAL, \
DEFAULT_RATELIMIT_BURST); \
@@ -215,34 +244,59 @@
printk(fmt, ##__VA_ARGS__); \
})
#else
-/* No effect, but we still get type checking even in the !PRINTK case: */
-#define printk_ratelimited printk
+#define printk_ratelimited(fmt, ...) \
+ no_printk(fmt, ##__VA_ARGS__)
#endif
-#define pr_emerg_ratelimited(fmt, ...) \
+#define pr_emerg_ratelimited(fmt, ...) \
printk_ratelimited(KERN_EMERG pr_fmt(fmt), ##__VA_ARGS__)
-#define pr_alert_ratelimited(fmt, ...) \
+#define pr_alert_ratelimited(fmt, ...) \
printk_ratelimited(KERN_ALERT pr_fmt(fmt), ##__VA_ARGS__)
-#define pr_crit_ratelimited(fmt, ...) \
+#define pr_crit_ratelimited(fmt, ...) \
printk_ratelimited(KERN_CRIT pr_fmt(fmt), ##__VA_ARGS__)
-#define pr_err_ratelimited(fmt, ...) \
+#define pr_err_ratelimited(fmt, ...) \
printk_ratelimited(KERN_ERR pr_fmt(fmt), ##__VA_ARGS__)
-#define pr_warning_ratelimited(fmt, ...) \
+#define pr_warn_ratelimited(fmt, ...) \
printk_ratelimited(KERN_WARNING pr_fmt(fmt), ##__VA_ARGS__)
-#define pr_warn_ratelimited pr_warning_ratelimited
-#define pr_notice_ratelimited(fmt, ...) \
+#define pr_notice_ratelimited(fmt, ...) \
printk_ratelimited(KERN_NOTICE pr_fmt(fmt), ##__VA_ARGS__)
-#define pr_info_ratelimited(fmt, ...) \
+#define pr_info_ratelimited(fmt, ...) \
printk_ratelimited(KERN_INFO pr_fmt(fmt), ##__VA_ARGS__)
/* no pr_cont_ratelimited, don't do that... */
/* If you are writing a driver, please use dev_dbg instead */
#if defined(DEBUG)
-#define pr_debug_ratelimited(fmt, ...) \
+#define pr_debug_ratelimited(fmt, ...) \
printk_ratelimited(KERN_DEBUG pr_fmt(fmt), ##__VA_ARGS__)
#else
#define pr_debug_ratelimited(fmt, ...) \
- ({ if (0) printk_ratelimited(KERN_DEBUG pr_fmt(fmt), \
- ##__VA_ARGS__); 0; })
+ no_printk(KERN_DEBUG pr_fmt(fmt), ##__VA_ARGS__)
+#endif
+
+enum {
+ DUMP_PREFIX_NONE,
+ DUMP_PREFIX_ADDRESS,
+ DUMP_PREFIX_OFFSET
+};
+extern void hex_dump_to_buffer(const void *buf, size_t len,
+ int rowsize, int groupsize,
+ char *linebuf, size_t linebuflen, bool ascii);
+#ifdef CONFIG_PRINTK
+extern void print_hex_dump(const char *level, const char *prefix_str,
+ int prefix_type, int rowsize, int groupsize,
+ const void *buf, size_t len, bool ascii);
+extern void print_hex_dump_bytes(const char *prefix_str, int prefix_type,
+ const void *buf, size_t len);
+#else
+static inline void print_hex_dump(const char *level, const char *prefix_str,
+ int prefix_type, int rowsize, int groupsize,
+ const void *buf, size_t len, bool ascii)
+{
+}
+static inline void print_hex_dump_bytes(const char *prefix_str, int prefix_type,
+ const void *buf, size_t len)
+{
+}
+
#endif
#endif
diff --git a/include/linux/regulator/ab8500.h b/include/linux/regulator/ab8500.h
index f509877..6a210f1 100644
--- a/include/linux/regulator/ab8500.h
+++ b/include/linux/regulator/ab8500.h
@@ -11,15 +11,17 @@
#define __LINUX_MFD_AB8500_REGULATOR_H
/* AB8500 regulators */
-#define AB8500_LDO_AUX1 0
-#define AB8500_LDO_AUX2 1
-#define AB8500_LDO_AUX3 2
-#define AB8500_LDO_INTCORE 3
-#define AB8500_LDO_TVOUT 4
-#define AB8500_LDO_AUDIO 5
-#define AB8500_LDO_ANAMIC1 6
-#define AB8500_LDO_ANAMIC2 7
-#define AB8500_LDO_DMIC 8
-#define AB8500_LDO_ANA 9
-
+enum ab8500_regulator_id {
+ AB8500_LDO_AUX1,
+ AB8500_LDO_AUX2,
+ AB8500_LDO_AUX3,
+ AB8500_LDO_INTCORE,
+ AB8500_LDO_TVOUT,
+ AB8500_LDO_AUDIO,
+ AB8500_LDO_ANAMIC1,
+ AB8500_LDO_ANAMIC2,
+ AB8500_LDO_DMIC,
+ AB8500_LDO_ANA,
+ AB8500_NUM_REGULATORS,
+};
#endif
diff --git a/include/linux/regulator/consumer.h b/include/linux/regulator/consumer.h
index ebd7472..7954f6b 100644
--- a/include/linux/regulator/consumer.h
+++ b/include/linux/regulator/consumer.h
@@ -154,6 +154,7 @@
int min_uV, int max_uV);
int regulator_set_voltage(struct regulator *regulator, int min_uV, int max_uV);
int regulator_get_voltage(struct regulator *regulator);
+int regulator_sync_voltage(struct regulator *regulator);
int regulator_set_current_limit(struct regulator *regulator,
int min_uA, int max_uA);
int regulator_get_current_limit(struct regulator *regulator);
diff --git a/include/linux/regulator/driver.h b/include/linux/regulator/driver.h
index 592cd7c..b8ed16a 100644
--- a/include/linux/regulator/driver.h
+++ b/include/linux/regulator/driver.h
@@ -42,7 +42,11 @@
*
* @set_voltage: Set the voltage for the regulator within the range specified.
* The driver should select the voltage closest to min_uV.
+ * @set_voltage_sel: Set the voltage for the regulator using the specified
+ * selector.
* @get_voltage: Return the currently configured voltage for the regulator.
+ * @get_voltage_sel: Return the currently configured voltage selector for the
+ * regulator.
* @list_voltage: Return one of the supported voltages, in microvolts; zero
* if the selector indicates a voltage that is unusable on this system;
* or negative errno. Selectors range from zero to one less than
@@ -79,8 +83,11 @@
int (*list_voltage) (struct regulator_dev *, unsigned selector);
/* get/set regulator voltage */
- int (*set_voltage) (struct regulator_dev *, int min_uV, int max_uV);
+ int (*set_voltage) (struct regulator_dev *, int min_uV, int max_uV,
+ unsigned *selector);
+ int (*set_voltage_sel) (struct regulator_dev *, unsigned selector);
int (*get_voltage) (struct regulator_dev *);
+ int (*get_voltage_sel) (struct regulator_dev *);
/* get/set regulator current */
int (*set_current_limit) (struct regulator_dev *,
@@ -168,9 +175,9 @@
*/
struct regulator_dev {
struct regulator_desc *desc;
- int use_count;
- int open_count;
int exclusive;
+ u32 use_count;
+ u32 open_count;
/* lists we belong to */
struct list_head list; /* list of all regulators */
@@ -188,10 +195,14 @@
struct regulator_dev *supply; /* for tree */
void *reg_data; /* regulator_dev data */
+
+#ifdef CONFIG_DEBUG_FS
+ struct dentry *debugfs;
+#endif
};
struct regulator_dev *regulator_register(struct regulator_desc *regulator_desc,
- struct device *dev, struct regulator_init_data *init_data,
+ struct device *dev, const struct regulator_init_data *init_data,
void *driver_data);
void regulator_unregister(struct regulator_dev *rdev);
diff --git a/include/linux/rio.h b/include/linux/rio.h
index 0bed941..ff681eb 100644
--- a/include/linux/rio.h
+++ b/include/linux/rio.h
@@ -66,14 +66,62 @@
#define RIO_PW_MSG_SIZE 64
+/*
+ * A component tag value (stored in the component tag CSR) is used as device's
+ * unique identifier assigned during enumeration. Besides being used for
+ * identifying switches (which do not have device ID register), it also is used
+ * by error management notification and therefore has to be assigned
+ * to endpoints as well.
+ */
+#define RIO_CTAG_RESRVD 0xfffe0000 /* Reserved */
+#define RIO_CTAG_UDEVID 0x0001ffff /* Unique device identifier */
+
extern struct bus_type rio_bus_type;
extern struct device rio_bus;
extern struct list_head rio_devices; /* list of all devices */
struct rio_mport;
+struct rio_dev;
union rio_pw_msg;
/**
+ * struct rio_switch - RIO switch info
+ * @node: Node in global list of switches
+ * @switchid: Switch ID that is unique across a network
+ * @route_table: Copy of switch routing table
+ * @port_ok: Status of each port (one bit per port) - OK=1 or UNINIT=0
+ * @add_entry: Callback for switch-specific route add function
+ * @get_entry: Callback for switch-specific route get function
+ * @clr_table: Callback for switch-specific clear route table function
+ * @set_domain: Callback for switch-specific domain setting function
+ * @get_domain: Callback for switch-specific domain get function
+ * @em_init: Callback for switch-specific error management init function
+ * @em_handle: Callback for switch-specific error management handler function
+ * @sw_sysfs: Callback that initializes switch-specific sysfs attributes
+ * @nextdev: Array of per-port pointers to the next attached device
+ */
+struct rio_switch {
+ struct list_head node;
+ u16 switchid;
+ u8 *route_table;
+ u32 port_ok;
+ int (*add_entry) (struct rio_mport *mport, u16 destid, u8 hopcount,
+ u16 table, u16 route_destid, u8 route_port);
+ int (*get_entry) (struct rio_mport *mport, u16 destid, u8 hopcount,
+ u16 table, u16 route_destid, u8 *route_port);
+ int (*clr_table) (struct rio_mport *mport, u16 destid, u8 hopcount,
+ u16 table);
+ int (*set_domain) (struct rio_mport *mport, u16 destid, u8 hopcount,
+ u8 sw_domain);
+ int (*get_domain) (struct rio_mport *mport, u16 destid, u8 hopcount,
+ u8 *sw_domain);
+ int (*em_init) (struct rio_dev *dev);
+ int (*em_handle) (struct rio_dev *dev, u8 swport);
+ int (*sw_sysfs) (struct rio_dev *dev, int create);
+ struct rio_dev *nextdev[0];
+};
+
+/**
* struct rio_dev - RIO device info
* @global_list: Node in list of all RIO devices
* @net_list: Node in list of RIO devices in a network
@@ -93,13 +141,14 @@
* @phys_efptr: RIO device extended features pointer
* @em_efptr: RIO Error Management features pointer
* @dma_mask: Mask of bits of RIO address this device implements
- * @rswitch: Pointer to &struct rio_switch if valid for this device
* @driver: Driver claiming this device
* @dev: Device model device
* @riores: RIO resources this device owns
* @pwcback: port-write callback function for this device
- * @destid: Network destination ID
+ * @destid: Network destination ID (or associated destid for switch)
+ * @hopcount: Hopcount to this device
* @prev: Previous RIO device connected to the current one
+ * @rswitch: struct rio_switch (if valid for this device)
*/
struct rio_dev {
struct list_head global_list; /* node in list of all RIO devices */
@@ -120,18 +169,20 @@
u32 phys_efptr;
u32 em_efptr;
u64 dma_mask;
- struct rio_switch *rswitch; /* RIO switch info */
struct rio_driver *driver; /* RIO driver claiming this device */
struct device dev; /* LDM device structure */
struct resource riores[RIO_MAX_DEV_RESOURCES];
int (*pwcback) (struct rio_dev *rdev, union rio_pw_msg *msg, int step);
u16 destid;
+ u8 hopcount;
struct rio_dev *prev;
+ struct rio_switch rswitch[0]; /* RIO switch info */
};
#define rio_dev_g(n) list_entry(n, struct rio_dev, global_list)
#define rio_dev_f(n) list_entry(n, struct rio_dev, net_list)
#define to_rio_dev(n) container_of(n, struct rio_dev, dev)
+#define sw_to_rio_dev(n) container_of(n, struct rio_dev, rswitch[0])
/**
* struct rio_msg - RIO message event
@@ -224,49 +275,6 @@
#define RIO_SW_SYSFS_CREATE 1 /* Create switch attributes */
#define RIO_SW_SYSFS_REMOVE 0 /* Remove switch attributes */
-/**
- * struct rio_switch - RIO switch info
- * @node: Node in global list of switches
- * @rdev: Associated RIO device structure
- * @switchid: Switch ID that is unique across a network
- * @hopcount: Hopcount to this switch
- * @destid: Associated destid in the path
- * @route_table: Copy of switch routing table
- * @port_ok: Status of each port (one bit per port) - OK=1 or UNINIT=0
- * @add_entry: Callback for switch-specific route add function
- * @get_entry: Callback for switch-specific route get function
- * @clr_table: Callback for switch-specific clear route table function
- * @set_domain: Callback for switch-specific domain setting function
- * @get_domain: Callback for switch-specific domain get function
- * @em_init: Callback for switch-specific error management initialization function
- * @em_handle: Callback for switch-specific error management handler function
- * @sw_sysfs: Callback that initializes switch-specific sysfs attributes
- * @nextdev: Array of per-port pointers to the next attached device
- */
-struct rio_switch {
- struct list_head node;
- struct rio_dev *rdev;
- u16 switchid;
- u16 hopcount;
- u16 destid;
- u8 *route_table;
- u32 port_ok;
- int (*add_entry) (struct rio_mport * mport, u16 destid, u8 hopcount,
- u16 table, u16 route_destid, u8 route_port);
- int (*get_entry) (struct rio_mport * mport, u16 destid, u8 hopcount,
- u16 table, u16 route_destid, u8 * route_port);
- int (*clr_table) (struct rio_mport *mport, u16 destid, u8 hopcount,
- u16 table);
- int (*set_domain) (struct rio_mport *mport, u16 destid, u8 hopcount,
- u8 sw_domain);
- int (*get_domain) (struct rio_mport *mport, u16 destid, u8 hopcount,
- u8 *sw_domain);
- int (*em_init) (struct rio_dev *dev);
- int (*em_handle) (struct rio_dev *dev, u8 swport);
- int (*sw_sysfs) (struct rio_dev *dev, int create);
- struct rio_dev *nextdev[0];
-};
-
/* Low-level architecture-dependent routines */
/**
diff --git a/include/linux/rio_drv.h b/include/linux/rio_drv.h
index edc55da..e09e565 100644
--- a/include/linux/rio_drv.h
+++ b/include/linux/rio_drv.h
@@ -150,16 +150,8 @@
static inline int rio_read_config_32(struct rio_dev *rdev, u32 offset,
u32 * data)
{
- u8 hopcount = 0xff;
- u16 destid = rdev->destid;
-
- if (rdev->rswitch) {
- destid = rdev->rswitch->destid;
- hopcount = rdev->rswitch->hopcount;
- }
-
- return rio_mport_read_config_32(rdev->net->hport, destid, hopcount,
- offset, data);
+ return rio_mport_read_config_32(rdev->net->hport, rdev->destid,
+ rdev->hopcount, offset, data);
};
/**
@@ -174,16 +166,8 @@
static inline int rio_write_config_32(struct rio_dev *rdev, u32 offset,
u32 data)
{
- u8 hopcount = 0xff;
- u16 destid = rdev->destid;
-
- if (rdev->rswitch) {
- destid = rdev->rswitch->destid;
- hopcount = rdev->rswitch->hopcount;
- }
-
- return rio_mport_write_config_32(rdev->net->hport, destid, hopcount,
- offset, data);
+ return rio_mport_write_config_32(rdev->net->hport, rdev->destid,
+ rdev->hopcount, offset, data);
};
/**
@@ -198,16 +182,8 @@
static inline int rio_read_config_16(struct rio_dev *rdev, u32 offset,
u16 * data)
{
- u8 hopcount = 0xff;
- u16 destid = rdev->destid;
-
- if (rdev->rswitch) {
- destid = rdev->rswitch->destid;
- hopcount = rdev->rswitch->hopcount;
- }
-
- return rio_mport_read_config_16(rdev->net->hport, destid, hopcount,
- offset, data);
+ return rio_mport_read_config_16(rdev->net->hport, rdev->destid,
+ rdev->hopcount, offset, data);
};
/**
@@ -222,16 +198,8 @@
static inline int rio_write_config_16(struct rio_dev *rdev, u32 offset,
u16 data)
{
- u8 hopcount = 0xff;
- u16 destid = rdev->destid;
-
- if (rdev->rswitch) {
- destid = rdev->rswitch->destid;
- hopcount = rdev->rswitch->hopcount;
- }
-
- return rio_mport_write_config_16(rdev->net->hport, destid, hopcount,
- offset, data);
+ return rio_mport_write_config_16(rdev->net->hport, rdev->destid,
+ rdev->hopcount, offset, data);
};
/**
@@ -245,16 +213,8 @@
*/
static inline int rio_read_config_8(struct rio_dev *rdev, u32 offset, u8 * data)
{
- u8 hopcount = 0xff;
- u16 destid = rdev->destid;
-
- if (rdev->rswitch) {
- destid = rdev->rswitch->destid;
- hopcount = rdev->rswitch->hopcount;
- }
-
- return rio_mport_read_config_8(rdev->net->hport, destid, hopcount,
- offset, data);
+ return rio_mport_read_config_8(rdev->net->hport, rdev->destid,
+ rdev->hopcount, offset, data);
};
/**
@@ -268,16 +228,8 @@
*/
static inline int rio_write_config_8(struct rio_dev *rdev, u32 offset, u8 data)
{
- u8 hopcount = 0xff;
- u16 destid = rdev->destid;
-
- if (rdev->rswitch) {
- destid = rdev->rswitch->destid;
- hopcount = rdev->rswitch->hopcount;
- }
-
- return rio_mport_write_config_8(rdev->net->hport, destid, hopcount,
- offset, data);
+ return rio_mport_write_config_8(rdev->net->hport, rdev->destid,
+ rdev->hopcount, offset, data);
};
extern int rio_mport_send_doorbell(struct rio_mport *mport, u16 destid,
diff --git a/include/linux/rio_ids.h b/include/linux/rio_ids.h
index ee7b6ad..7410d33 100644
--- a/include/linux/rio_ids.h
+++ b/include/linux/rio_ids.h
@@ -36,5 +36,7 @@
#define RIO_DID_IDTCPS10Q 0x035e
#define RIO_DID_IDTCPS1848 0x0374
#define RIO_DID_IDTCPS1616 0x0379
+#define RIO_DID_IDTVPS1616 0x0377
+#define RIO_DID_IDTSPS1616 0x0378
#endif /* LINUX_RIO_IDS_H */
diff --git a/include/linux/romfs_fs.h b/include/linux/romfs_fs.h
index c490fbc..5f57f93 100644
--- a/include/linux/romfs_fs.h
+++ b/include/linux/romfs_fs.h
@@ -1,6 +1,9 @@
#ifndef __LINUX_ROMFS_FS_H
#define __LINUX_ROMFS_FS_H
+#include <linux/types.h>
+#include <linux/fs.h>
+
/* The basic structures of the romfs filesystem */
#define ROMBSIZE BLOCK_SIZE
diff --git a/include/linux/sched.h b/include/linux/sched.h
index abc527a..96e2321 100644
--- a/include/linux/sched.h
+++ b/include/linux/sched.h
@@ -683,7 +683,7 @@
atomic_t fanotify_listeners;
#endif
#ifdef CONFIG_EPOLL
- atomic_t epoll_watches; /* The number of file descriptors currently watched */
+ atomic_long_t epoll_watches; /* The number of file descriptors currently watched */
#endif
#ifdef CONFIG_POSIX_MQUEUE
/* protected by mq_lock */
diff --git a/include/linux/serial_core.h b/include/linux/serial_core.h
index a23fa29..758c5b0 100644
--- a/include/linux/serial_core.h
+++ b/include/linux/serial_core.h
@@ -212,6 +212,7 @@
#include <linux/tty.h>
#include <linux/mutex.h>
#include <linux/sysrq.h>
+#include <linux/pps_kernel.h>
struct uart_port;
struct serial_struct;
@@ -528,10 +529,10 @@
struct uart_state *state = uport->state;
struct tty_port *port = &state->port;
struct tty_ldisc *ld = tty_ldisc_ref(port->tty);
- struct timespec ts;
+ struct pps_event_time ts;
if (ld && ld->ops->dcd_change)
- getnstimeofday(&ts);
+ pps_get_ts(&ts);
uport->icount.dcd++;
#ifdef CONFIG_HARD_PPS
diff --git a/include/linux/socket.h b/include/linux/socket.h
index 5f65f14..edbb1d0 100644
--- a/include/linux/socket.h
+++ b/include/linux/socket.h
@@ -191,7 +191,8 @@
#define AF_PHONET 35 /* Phonet sockets */
#define AF_IEEE802154 36 /* IEEE802154 sockets */
#define AF_CAIF 37 /* CAIF sockets */
-#define AF_MAX 38 /* For now.. */
+#define AF_ALG 38 /* Algorithm sockets */
+#define AF_MAX 39 /* For now.. */
/* Protocol families, same as address families. */
#define PF_UNSPEC AF_UNSPEC
@@ -232,6 +233,7 @@
#define PF_PHONET AF_PHONET
#define PF_IEEE802154 AF_IEEE802154
#define PF_CAIF AF_CAIF
+#define PF_ALG AF_ALG
#define PF_MAX AF_MAX
/* Maximum queue length specifiable by listen. */
@@ -305,6 +307,7 @@
#define SOL_RDS 276
#define SOL_IUCV 277
#define SOL_CAIF 278
+#define SOL_ALG 279
/* IPX options */
#define IPX_TYPE 1
diff --git a/include/linux/suspend.h b/include/linux/suspend.h
index 144b34b..c1f4998 100644
--- a/include/linux/suspend.h
+++ b/include/linux/suspend.h
@@ -122,7 +122,7 @@
* suspend_set_ops - set platform dependent suspend operations
* @ops: The new suspend operations to set.
*/
-extern void suspend_set_ops(struct platform_suspend_ops *ops);
+extern void suspend_set_ops(const struct platform_suspend_ops *ops);
extern int suspend_valid_only_mem(suspend_state_t state);
/**
@@ -147,7 +147,7 @@
#else /* !CONFIG_SUSPEND */
#define suspend_valid_only_mem NULL
-static inline void suspend_set_ops(struct platform_suspend_ops *ops) {}
+static inline void suspend_set_ops(const struct platform_suspend_ops *ops) {}
static inline int pm_suspend(suspend_state_t state) { return -ENOSYS; }
#endif /* !CONFIG_SUSPEND */
@@ -245,7 +245,7 @@
extern void swsusp_unset_page_free(struct page *);
extern unsigned long get_safe_page(gfp_t gfp_mask);
-extern void hibernation_set_ops(struct platform_hibernation_ops *ops);
+extern void hibernation_set_ops(const struct platform_hibernation_ops *ops);
extern int hibernate(void);
extern bool system_entering_hibernation(void);
#else /* CONFIG_HIBERNATION */
@@ -253,7 +253,7 @@
static inline void swsusp_set_page_free(struct page *p) {}
static inline void swsusp_unset_page_free(struct page *p) {}
-static inline void hibernation_set_ops(struct platform_hibernation_ops *ops) {}
+static inline void hibernation_set_ops(const struct platform_hibernation_ops *ops) {}
static inline int hibernate(void) { return -ENOSYS; }
static inline bool system_entering_hibernation(void) { return false; }
#endif /* CONFIG_HIBERNATION */
diff --git a/include/linux/time.h b/include/linux/time.h
index 9f15ac7..1e6d3b5 100644
--- a/include/linux/time.h
+++ b/include/linux/time.h
@@ -158,6 +158,8 @@
extern int do_getitimer(int which, struct itimerval *value);
extern void getnstimeofday(struct timespec *tv);
extern void getrawmonotonic(struct timespec *ts);
+extern void getnstime_raw_and_real(struct timespec *ts_raw,
+ struct timespec *ts_real);
extern void getboottime(struct timespec *ts);
extern void monotonic_to_bootbased(struct timespec *ts);
diff --git a/include/linux/timex.h b/include/linux/timex.h
index 32d852f..d23999f 100644
--- a/include/linux/timex.h
+++ b/include/linux/timex.h
@@ -268,6 +268,7 @@
extern void second_overflow(void);
extern void update_ntp_one_tick(void);
extern int do_adjtimex(struct timex *);
+extern void hardpps(const struct timespec *, const struct timespec *);
int read_current_timer(unsigned long *timer_val);
diff --git a/include/linux/toshiba.h b/include/linux/toshiba.h
index 6a7c4ed..772dedb 100644
--- a/include/linux/toshiba.h
+++ b/include/linux/toshiba.h
@@ -33,6 +33,8 @@
unsigned int edi __attribute__ ((packed));
} SMMRegisters;
+#ifdef __KERNEL__
int tosh_smm(SMMRegisters *regs);
+#endif /* __KERNEL__ */
#endif
diff --git a/include/linux/tty_ldisc.h b/include/linux/tty_ldisc.h
index 526d66f..ff7dc08 100644
--- a/include/linux/tty_ldisc.h
+++ b/include/linux/tty_ldisc.h
@@ -101,14 +101,15 @@
* any pending driver I/O is completed.
*
* void (*dcd_change)(struct tty_struct *tty, unsigned int status,
- * struct timespec *ts)
+ * struct pps_event_time *ts)
*
* Tells the discipline that the DCD pin has changed its status and
- * the relative timestamp. Pointer ts can be NULL.
+ * the relative timestamp. Pointer ts cannot be NULL.
*/
#include <linux/fs.h>
#include <linux/wait.h>
+#include <linux/pps_kernel.h>
struct tty_ldisc_ops {
int magic;
@@ -143,7 +144,7 @@
char *fp, int count);
void (*write_wakeup)(struct tty_struct *);
void (*dcd_change)(struct tty_struct *, unsigned int,
- struct timespec *);
+ struct pps_event_time *);
struct module *owner;
diff --git a/include/linux/u64_stats_sync.h b/include/linux/u64_stats_sync.h
index fa261a0..8da8c4e 100644
--- a/include/linux/u64_stats_sync.h
+++ b/include/linux/u64_stats_sync.h
@@ -67,21 +67,21 @@
#endif
};
-static void inline u64_stats_update_begin(struct u64_stats_sync *syncp)
+static inline void u64_stats_update_begin(struct u64_stats_sync *syncp)
{
#if BITS_PER_LONG==32 && defined(CONFIG_SMP)
write_seqcount_begin(&syncp->seq);
#endif
}
-static void inline u64_stats_update_end(struct u64_stats_sync *syncp)
+static inline void u64_stats_update_end(struct u64_stats_sync *syncp)
{
#if BITS_PER_LONG==32 && defined(CONFIG_SMP)
write_seqcount_end(&syncp->seq);
#endif
}
-static unsigned int inline u64_stats_fetch_begin(const struct u64_stats_sync *syncp)
+static inline unsigned int u64_stats_fetch_begin(const struct u64_stats_sync *syncp)
{
#if BITS_PER_LONG==32 && defined(CONFIG_SMP)
return read_seqcount_begin(&syncp->seq);
@@ -93,7 +93,7 @@
#endif
}
-static bool inline u64_stats_fetch_retry(const struct u64_stats_sync *syncp,
+static inline bool u64_stats_fetch_retry(const struct u64_stats_sync *syncp,
unsigned int start)
{
#if BITS_PER_LONG==32 && defined(CONFIG_SMP)
@@ -112,7 +112,7 @@
* - UP 32bit must disable BH.
* - 64bit have no problem atomically reading u64 values, irq safe.
*/
-static unsigned int inline u64_stats_fetch_begin_bh(const struct u64_stats_sync *syncp)
+static inline unsigned int u64_stats_fetch_begin_bh(const struct u64_stats_sync *syncp)
{
#if BITS_PER_LONG==32 && defined(CONFIG_SMP)
return read_seqcount_begin(&syncp->seq);
@@ -124,7 +124,7 @@
#endif
}
-static bool inline u64_stats_fetch_retry_bh(const struct u64_stats_sync *syncp,
+static inline bool u64_stats_fetch_retry_bh(const struct u64_stats_sync *syncp,
unsigned int start)
{
#if BITS_PER_LONG==32 && defined(CONFIG_SMP)
diff --git a/include/linux/unaligned/packed_struct.h b/include/linux/unaligned/packed_struct.h
index c9a6abd..c0d817d 100644
--- a/include/linux/unaligned/packed_struct.h
+++ b/include/linux/unaligned/packed_struct.h
@@ -3,9 +3,9 @@
#include <linux/kernel.h>
-struct __una_u16 { u16 x; } __attribute__((packed));
-struct __una_u32 { u32 x; } __attribute__((packed));
-struct __una_u64 { u64 x; } __attribute__((packed));
+struct __una_u16 { u16 x; } __packed;
+struct __una_u32 { u32 x; } __packed;
+struct __una_u64 { u64 x; } __packed;
static inline u16 __get_unaligned_cpu16(const void *p)
{
diff --git a/include/linux/user_namespace.h b/include/linux/user_namespace.h
index 8178156..faf4679 100644
--- a/include/linux/user_namespace.h
+++ b/include/linux/user_namespace.h
@@ -6,7 +6,7 @@
#include <linux/sched.h>
#include <linux/err.h>
-#define UIDHASH_BITS (CONFIG_BASE_SMALL ? 3 : 8)
+#define UIDHASH_BITS (CONFIG_BASE_SMALL ? 3 : 7)
#define UIDHASH_SZ (1 << UIDHASH_BITS)
struct user_namespace {
diff --git a/include/linux/xz.h b/include/linux/xz.h
new file mode 100644
index 0000000..64cffa6
--- /dev/null
+++ b/include/linux/xz.h
@@ -0,0 +1,264 @@
+/*
+ * XZ decompressor
+ *
+ * Authors: Lasse Collin <lasse.collin@tukaani.org>
+ * Igor Pavlov <http://7-zip.org/>
+ *
+ * This file has been put into the public domain.
+ * You can do whatever you want with this file.
+ */
+
+#ifndef XZ_H
+#define XZ_H
+
+#ifdef __KERNEL__
+# include <linux/stddef.h>
+# include <linux/types.h>
+#else
+# include <stddef.h>
+# include <stdint.h>
+#endif
+
+/* In Linux, this is used to make extern functions static when needed. */
+#ifndef XZ_EXTERN
+# define XZ_EXTERN extern
+#endif
+
+/**
+ * enum xz_mode - Operation mode
+ *
+ * @XZ_SINGLE: Single-call mode. This uses less RAM than
+ * than multi-call modes, because the LZMA2
+ * dictionary doesn't need to be allocated as
+ * part of the decoder state. All required data
+ * structures are allocated at initialization,
+ * so xz_dec_run() cannot return XZ_MEM_ERROR.
+ * @XZ_PREALLOC: Multi-call mode with preallocated LZMA2
+ * dictionary buffer. All data structures are
+ * allocated at initialization, so xz_dec_run()
+ * cannot return XZ_MEM_ERROR.
+ * @XZ_DYNALLOC: Multi-call mode. The LZMA2 dictionary is
+ * allocated once the required size has been
+ * parsed from the stream headers. If the
+ * allocation fails, xz_dec_run() will return
+ * XZ_MEM_ERROR.
+ *
+ * It is possible to enable support only for a subset of the above
+ * modes at compile time by defining XZ_DEC_SINGLE, XZ_DEC_PREALLOC,
+ * or XZ_DEC_DYNALLOC. The xz_dec kernel module is always compiled
+ * with support for all operation modes, but the preboot code may
+ * be built with fewer features to minimize code size.
+ */
+enum xz_mode {
+ XZ_SINGLE,
+ XZ_PREALLOC,
+ XZ_DYNALLOC
+};
+
+/**
+ * enum xz_ret - Return codes
+ * @XZ_OK: Everything is OK so far. More input or more
+ * output space is required to continue. This
+ * return code is possible only in multi-call mode
+ * (XZ_PREALLOC or XZ_DYNALLOC).
+ * @XZ_STREAM_END: Operation finished successfully.
+ * @XZ_UNSUPPORTED_CHECK: Integrity check type is not supported. Decoding
+ * is still possible in multi-call mode by simply
+ * calling xz_dec_run() again.
+ * Note that this return value is used only if
+ * XZ_DEC_ANY_CHECK was defined at build time,
+ * which is not used in the kernel. Unsupported
+ * check types return XZ_OPTIONS_ERROR if
+ * XZ_DEC_ANY_CHECK was not defined at build time.
+ * @XZ_MEM_ERROR: Allocating memory failed. This return code is
+ * possible only if the decoder was initialized
+ * with XZ_DYNALLOC. The amount of memory that was
+ * tried to be allocated was no more than the
+ * dict_max argument given to xz_dec_init().
+ * @XZ_MEMLIMIT_ERROR: A bigger LZMA2 dictionary would be needed than
+ * allowed by the dict_max argument given to
+ * xz_dec_init(). This return value is possible
+ * only in multi-call mode (XZ_PREALLOC or
+ * XZ_DYNALLOC); the single-call mode (XZ_SINGLE)
+ * ignores the dict_max argument.
+ * @XZ_FORMAT_ERROR: File format was not recognized (wrong magic
+ * bytes).
+ * @XZ_OPTIONS_ERROR: This implementation doesn't support the requested
+ * compression options. In the decoder this means
+ * that the header CRC32 matches, but the header
+ * itself specifies something that we don't support.
+ * @XZ_DATA_ERROR: Compressed data is corrupt.
+ * @XZ_BUF_ERROR: Cannot make any progress. Details are slightly
+ * different between multi-call and single-call
+ * mode; more information below.
+ *
+ * In multi-call mode, XZ_BUF_ERROR is returned when two consecutive calls
+ * to XZ code cannot consume any input and cannot produce any new output.
+ * This happens when there is no new input available, or the output buffer
+ * is full while at least one output byte is still pending. Assuming your
+ * code is not buggy, you can get this error only when decoding a compressed
+ * stream that is truncated or otherwise corrupt.
+ *
+ * In single-call mode, XZ_BUF_ERROR is returned only when the output buffer
+ * is too small or the compressed input is corrupt in a way that makes the
+ * decoder produce more output than the caller expected. When it is
+ * (relatively) clear that the compressed input is truncated, XZ_DATA_ERROR
+ * is used instead of XZ_BUF_ERROR.
+ */
+enum xz_ret {
+ XZ_OK,
+ XZ_STREAM_END,
+ XZ_UNSUPPORTED_CHECK,
+ XZ_MEM_ERROR,
+ XZ_MEMLIMIT_ERROR,
+ XZ_FORMAT_ERROR,
+ XZ_OPTIONS_ERROR,
+ XZ_DATA_ERROR,
+ XZ_BUF_ERROR
+};
+
+/**
+ * struct xz_buf - Passing input and output buffers to XZ code
+ * @in: Beginning of the input buffer. This may be NULL if and only
+ * if in_pos is equal to in_size.
+ * @in_pos: Current position in the input buffer. This must not exceed
+ * in_size.
+ * @in_size: Size of the input buffer
+ * @out: Beginning of the output buffer. This may be NULL if and only
+ * if out_pos is equal to out_size.
+ * @out_pos: Current position in the output buffer. This must not exceed
+ * out_size.
+ * @out_size: Size of the output buffer
+ *
+ * Only the contents of the output buffer from out[out_pos] onward, and
+ * the variables in_pos and out_pos are modified by the XZ code.
+ */
+struct xz_buf {
+ const uint8_t *in;
+ size_t in_pos;
+ size_t in_size;
+
+ uint8_t *out;
+ size_t out_pos;
+ size_t out_size;
+};
+
+/**
+ * struct xz_dec - Opaque type to hold the XZ decoder state
+ */
+struct xz_dec;
+
+/**
+ * xz_dec_init() - Allocate and initialize a XZ decoder state
+ * @mode: Operation mode
+ * @dict_max: Maximum size of the LZMA2 dictionary (history buffer) for
+ * multi-call decoding. This is ignored in single-call mode
+ * (mode == XZ_SINGLE). LZMA2 dictionary is always 2^n bytes
+ * or 2^n + 2^(n-1) bytes (the latter sizes are less common
+ * in practice), so other values for dict_max don't make sense.
+ * In the kernel, dictionary sizes of 64 KiB, 128 KiB, 256 KiB,
+ * 512 KiB, and 1 MiB are probably the only reasonable values,
+ * except for kernel and initramfs images where a bigger
+ * dictionary can be fine and useful.
+ *
+ * Single-call mode (XZ_SINGLE): xz_dec_run() decodes the whole stream at
+ * once. The caller must provide enough output space or the decoding will
+ * fail. The output space is used as the dictionary buffer, which is why
+ * there is no need to allocate the dictionary as part of the decoder's
+ * internal state.
+ *
+ * Because the output buffer is used as the workspace, streams encoded using
+ * a big dictionary are not a problem in single-call mode. It is enough that
+ * the output buffer is big enough to hold the actual uncompressed data; it
+ * can be smaller than the dictionary size stored in the stream headers.
+ *
+ * Multi-call mode with preallocated dictionary (XZ_PREALLOC): dict_max bytes
+ * of memory is preallocated for the LZMA2 dictionary. This way there is no
+ * risk that xz_dec_run() could run out of memory, since xz_dec_run() will
+ * never allocate any memory. Instead, if the preallocated dictionary is too
+ * small for decoding the given input stream, xz_dec_run() will return
+ * XZ_MEMLIMIT_ERROR. Thus, it is important to know what kind of data will be
+ * decoded to avoid allocating excessive amount of memory for the dictionary.
+ *
+ * Multi-call mode with dynamically allocated dictionary (XZ_DYNALLOC):
+ * dict_max specifies the maximum allowed dictionary size that xz_dec_run()
+ * may allocate once it has parsed the dictionary size from the stream
+ * headers. This way excessive allocations can be avoided while still
+ * limiting the maximum memory usage to a sane value to prevent running the
+ * system out of memory when decompressing streams from untrusted sources.
+ *
+ * On success, xz_dec_init() returns a pointer to struct xz_dec, which is
+ * ready to be used with xz_dec_run(). If memory allocation fails,
+ * xz_dec_init() returns NULL.
+ */
+XZ_EXTERN struct xz_dec *xz_dec_init(enum xz_mode mode, uint32_t dict_max);
+
+/**
+ * xz_dec_run() - Run the XZ decoder
+ * @s: Decoder state allocated using xz_dec_init()
+ * @b: Input and output buffers
+ *
+ * The possible return values depend on build options and operation mode.
+ * See enum xz_ret for details.
+ *
+ * Note that if an error occurs in single-call mode (return value is not
+ * XZ_STREAM_END), b->in_pos and b->out_pos are not modified and the
+ * contents of the output buffer from b->out[b->out_pos] onward are
+ * undefined. This is true even after XZ_BUF_ERROR, because with some filter
+ * chains, there may be a second pass over the output buffer, and this pass
+ * cannot be properly done if the output buffer is truncated. Thus, you
+ * cannot give the single-call decoder a too small buffer and then expect to
+ * get that amount valid data from the beginning of the stream. You must use
+ * the multi-call decoder if you don't want to uncompress the whole stream.
+ */
+XZ_EXTERN enum xz_ret xz_dec_run(struct xz_dec *s, struct xz_buf *b);
+
+/**
+ * xz_dec_reset() - Reset an already allocated decoder state
+ * @s: Decoder state allocated using xz_dec_init()
+ *
+ * This function can be used to reset the multi-call decoder state without
+ * freeing and reallocating memory with xz_dec_end() and xz_dec_init().
+ *
+ * In single-call mode, xz_dec_reset() is always called in the beginning of
+ * xz_dec_run(). Thus, explicit call to xz_dec_reset() is useful only in
+ * multi-call mode.
+ */
+XZ_EXTERN void xz_dec_reset(struct xz_dec *s);
+
+/**
+ * xz_dec_end() - Free the memory allocated for the decoder state
+ * @s: Decoder state allocated using xz_dec_init(). If s is NULL,
+ * this function does nothing.
+ */
+XZ_EXTERN void xz_dec_end(struct xz_dec *s);
+
+/*
+ * Standalone build (userspace build or in-kernel build for boot time use)
+ * needs a CRC32 implementation. For normal in-kernel use, kernel's own
+ * CRC32 module is used instead, and users of this module don't need to
+ * care about the functions below.
+ */
+#ifndef XZ_INTERNAL_CRC32
+# ifdef __KERNEL__
+# define XZ_INTERNAL_CRC32 0
+# else
+# define XZ_INTERNAL_CRC32 1
+# endif
+#endif
+
+#if XZ_INTERNAL_CRC32
+/*
+ * This must be called before any other xz_* function to initialize
+ * the CRC32 lookup table.
+ */
+XZ_EXTERN void xz_crc32_init(void);
+
+/*
+ * Update CRC32 value using the polynomial from IEEE-802.3. To start a new
+ * calculation, the third argument must be zero. To continue the calculation,
+ * the previously returned value is passed as the third argument.
+ */
+XZ_EXTERN uint32_t xz_crc32(const uint8_t *buf, size_t size, uint32_t crc);
+#endif
+#endif
diff --git a/include/media/davinci/vpss.h b/include/media/davinci/vpss.h
index c59cc02..b586495 100644
--- a/include/media/davinci/vpss.h
+++ b/include/media/davinci/vpss.h
@@ -44,7 +44,7 @@
short pplen;
};
-/* Used for enable/diable VPSS Clock */
+/* Used for enable/disable VPSS Clock */
enum vpss_clock_sel {
/* DM355/DM365 */
VPSS_CCDC_CLOCK,
diff --git a/include/net/sctp/user.h b/include/net/sctp/user.h
index 2b2769c..2a128c8 100644
--- a/include/net/sctp/user.h
+++ b/include/net/sctp/user.h
@@ -99,8 +99,8 @@
#define SCTP_SOCKOPT_PEELOFF 102 /* peel off association. */
/* Options 104-106 are deprecated and removed. Do not use this space */
#define SCTP_SOCKOPT_CONNECTX_OLD 107 /* CONNECTX old requests. */
-#define SCTP_GET_PEER_ADDRS 108 /* Get all peer addresss. */
-#define SCTP_GET_LOCAL_ADDRS 109 /* Get all local addresss. */
+#define SCTP_GET_PEER_ADDRS 108 /* Get all peer address. */
+#define SCTP_GET_LOCAL_ADDRS 109 /* Get all local address. */
#define SCTP_SOCKOPT_CONNECTX 110 /* CONNECTX requests. */
#define SCTP_SOCKOPT_CONNECTX3 111 /* CONNECTX requests (updated) */
diff --git a/include/scsi/fc/fc_fcp.h b/include/scsi/fc/fc_fcp.h
index 8e9b222..8a143ca 100644
--- a/include/scsi/fc/fc_fcp.h
+++ b/include/scsi/fc/fc_fcp.h
@@ -46,7 +46,7 @@
*/
struct fcp_cmnd {
__u8 fc_lun[8]; /* logical unit number */
- __u8 fc_cmdref; /* commmand reference number */
+ __u8 fc_cmdref; /* command reference number */
__u8 fc_pri_ta; /* priority and task attribute */
__u8 fc_tm_flags; /* task management flags */
__u8 fc_flags; /* additional len & flags */
@@ -58,7 +58,7 @@
struct fcp_cmnd32 {
__u8 fc_lun[8]; /* logical unit number */
- __u8 fc_cmdref; /* commmand reference number */
+ __u8 fc_cmdref; /* command reference number */
__u8 fc_pri_ta; /* priority and task attribute */
__u8 fc_tm_flags; /* task management flags */
__u8 fc_flags; /* additional len & flags */
diff --git a/include/trace/events/kvm.h b/include/trace/events/kvm.h
index 6dd3a51..46e3cd8 100644
--- a/include/trace/events/kvm.h
+++ b/include/trace/events/kvm.h
@@ -6,6 +6,36 @@
#undef TRACE_SYSTEM
#define TRACE_SYSTEM kvm
+#define ERSN(x) { KVM_EXIT_##x, "KVM_EXIT_" #x }
+
+#define kvm_trace_exit_reason \
+ ERSN(UNKNOWN), ERSN(EXCEPTION), ERSN(IO), ERSN(HYPERCALL), \
+ ERSN(DEBUG), ERSN(HLT), ERSN(MMIO), ERSN(IRQ_WINDOW_OPEN), \
+ ERSN(SHUTDOWN), ERSN(FAIL_ENTRY), ERSN(INTR), ERSN(SET_TPR), \
+ ERSN(TPR_ACCESS), ERSN(S390_SIEIC), ERSN(S390_RESET), ERSN(DCR),\
+ ERSN(NMI), ERSN(INTERNAL_ERROR), ERSN(OSI)
+
+TRACE_EVENT(kvm_userspace_exit,
+ TP_PROTO(__u32 reason, int errno),
+ TP_ARGS(reason, errno),
+
+ TP_STRUCT__entry(
+ __field( __u32, reason )
+ __field( int, errno )
+ ),
+
+ TP_fast_assign(
+ __entry->reason = reason;
+ __entry->errno = errno;
+ ),
+
+ TP_printk("reason %s (%d)",
+ __entry->errno < 0 ?
+ (__entry->errno == -EINTR ? "restart" : "error") :
+ __print_symbolic(__entry->reason, kvm_trace_exit_reason),
+ __entry->errno < 0 ? -__entry->errno : __entry->reason)
+);
+
#if defined(__KVM_HAVE_IOAPIC)
TRACE_EVENT(kvm_set_irq,
TP_PROTO(unsigned int gsi, int level, int irq_source_id),
@@ -185,6 +215,97 @@
__entry->referenced ? "YOUNG" : "OLD")
);
+#ifdef CONFIG_KVM_ASYNC_PF
+DECLARE_EVENT_CLASS(kvm_async_get_page_class,
+
+ TP_PROTO(u64 gva, u64 gfn),
+
+ TP_ARGS(gva, gfn),
+
+ TP_STRUCT__entry(
+ __field(__u64, gva)
+ __field(u64, gfn)
+ ),
+
+ TP_fast_assign(
+ __entry->gva = gva;
+ __entry->gfn = gfn;
+ ),
+
+ TP_printk("gva = %#llx, gfn = %#llx", __entry->gva, __entry->gfn)
+);
+
+DEFINE_EVENT(kvm_async_get_page_class, kvm_try_async_get_page,
+
+ TP_PROTO(u64 gva, u64 gfn),
+
+ TP_ARGS(gva, gfn)
+);
+
+DEFINE_EVENT(kvm_async_get_page_class, kvm_async_pf_doublefault,
+
+ TP_PROTO(u64 gva, u64 gfn),
+
+ TP_ARGS(gva, gfn)
+);
+
+DECLARE_EVENT_CLASS(kvm_async_pf_nopresent_ready,
+
+ TP_PROTO(u64 token, u64 gva),
+
+ TP_ARGS(token, gva),
+
+ TP_STRUCT__entry(
+ __field(__u64, token)
+ __field(__u64, gva)
+ ),
+
+ TP_fast_assign(
+ __entry->token = token;
+ __entry->gva = gva;
+ ),
+
+ TP_printk("token %#llx gva %#llx", __entry->token, __entry->gva)
+
+);
+
+DEFINE_EVENT(kvm_async_pf_nopresent_ready, kvm_async_pf_not_present,
+
+ TP_PROTO(u64 token, u64 gva),
+
+ TP_ARGS(token, gva)
+);
+
+DEFINE_EVENT(kvm_async_pf_nopresent_ready, kvm_async_pf_ready,
+
+ TP_PROTO(u64 token, u64 gva),
+
+ TP_ARGS(token, gva)
+);
+
+TRACE_EVENT(
+ kvm_async_pf_completed,
+ TP_PROTO(unsigned long address, struct page *page, u64 gva),
+ TP_ARGS(address, page, gva),
+
+ TP_STRUCT__entry(
+ __field(unsigned long, address)
+ __field(pfn_t, pfn)
+ __field(u64, gva)
+ ),
+
+ TP_fast_assign(
+ __entry->address = address;
+ __entry->pfn = page ? page_to_pfn(page) : 0;
+ __entry->gva = gva;
+ ),
+
+ TP_printk("gva %#llx address %#lx pfn %#llx", __entry->gva,
+ __entry->address, __entry->pfn)
+);
+
+#endif
+
#endif /* _TRACE_KVM_MAIN_H */
/* This part must be outside protection */
diff --git a/include/trace/events/regulator.h b/include/trace/events/regulator.h
new file mode 100644
index 0000000..37502a7
--- /dev/null
+++ b/include/trace/events/regulator.h
@@ -0,0 +1,141 @@
+#undef TRACE_SYSTEM
+#define TRACE_SYSTEM regulator
+
+#if !defined(_TRACE_REGULATOR_H) || defined(TRACE_HEADER_MULTI_READ)
+#define _TRACE_REGULATOR_H
+
+#include <linux/ktime.h>
+#include <linux/tracepoint.h>
+
+/*
+ * Events which just log themselves and the regulator name for enable/disable
+ * type tracking.
+ */
+DECLARE_EVENT_CLASS(regulator_basic,
+
+ TP_PROTO(const char *name),
+
+ TP_ARGS(name),
+
+ TP_STRUCT__entry(
+ __string( name, name )
+ ),
+
+ TP_fast_assign(
+ __assign_str(name, name);
+ ),
+
+ TP_printk("name=%s", __get_str(name))
+
+);
+
+DEFINE_EVENT(regulator_basic, regulator_enable,
+
+ TP_PROTO(const char *name),
+
+ TP_ARGS(name)
+
+);
+
+DEFINE_EVENT(regulator_basic, regulator_enable_delay,
+
+ TP_PROTO(const char *name),
+
+ TP_ARGS(name)
+
+);
+
+DEFINE_EVENT(regulator_basic, regulator_enable_complete,
+
+ TP_PROTO(const char *name),
+
+ TP_ARGS(name)
+
+);
+
+DEFINE_EVENT(regulator_basic, regulator_disable,
+
+ TP_PROTO(const char *name),
+
+ TP_ARGS(name)
+
+);
+
+DEFINE_EVENT(regulator_basic, regulator_disable_complete,
+
+ TP_PROTO(const char *name),
+
+ TP_ARGS(name)
+
+);
+
+/*
+ * Events that take a range of numerical values, mostly for voltages
+ * and so on.
+ */
+DECLARE_EVENT_CLASS(regulator_range,
+
+ TP_PROTO(const char *name, int min, int max),
+
+ TP_ARGS(name, min, max),
+
+ TP_STRUCT__entry(
+ __string( name, name )
+ __field( int, min )
+ __field( int, max )
+ ),
+
+ TP_fast_assign(
+ __assign_str(name, name);
+ __entry->min = min;
+ __entry->max = max;
+ ),
+
+ TP_printk("name=%s (%d-%d)", __get_str(name),
+ (int)__entry->min, (int)__entry->max)
+);
+
+DEFINE_EVENT(regulator_range, regulator_set_voltage,
+
+ TP_PROTO(const char *name, int min, int max),
+
+ TP_ARGS(name, min, max)
+
+);
+
+
+/*
+ * Events that take a single value, mostly for readback and refcounts.
+ */
+DECLARE_EVENT_CLASS(regulator_value,
+
+ TP_PROTO(const char *name, unsigned int val),
+
+ TP_ARGS(name, val),
+
+ TP_STRUCT__entry(
+ __string( name, name )
+ __field( unsigned int, val )
+ ),
+
+ TP_fast_assign(
+ __assign_str(name, name);
+ __entry->val = val;
+ ),
+
+ TP_printk("name=%s, val=%u", __get_str(name),
+ (int)__entry->val)
+);
+
+DEFINE_EVENT(regulator_value, regulator_set_voltage_complete,
+
+ TP_PROTO(const char *name, unsigned int value),
+
+ TP_ARGS(name, value)
+
+);
+
+#endif /* _TRACE_POWER_H */
+
+/* This part must be outside protection */
+#include <trace/define_trace.h>
diff --git a/include/xen/xenbus.h b/include/xen/xenbus.h
index 43e2d7d..7a1d15f 100644
--- a/include/xen/xenbus.h
+++ b/include/xen/xenbus.h
@@ -94,7 +94,7 @@
int (*remove)(struct xenbus_device *dev);
int (*suspend)(struct xenbus_device *dev, pm_message_t state);
int (*resume)(struct xenbus_device *dev);
- int (*uevent)(struct xenbus_device *, char **, int, char *, int);
+ int (*uevent)(struct xenbus_device *, struct kobj_uevent_env *);
struct device_driver driver;
int (*read_otherend_details)(struct xenbus_device *dev);
int (*is_ready)(struct xenbus_device *dev);
diff --git a/init/Kconfig b/init/Kconfig
index 8dfd094..4f6cdbf 100644
--- a/init/Kconfig
+++ b/init/Kconfig
@@ -130,13 +130,16 @@
config HAVE_KERNEL_LZMA
bool
+config HAVE_KERNEL_XZ
+ bool
+
config HAVE_KERNEL_LZO
bool
choice
prompt "Kernel compression mode"
default KERNEL_GZIP
- depends on HAVE_KERNEL_GZIP || HAVE_KERNEL_BZIP2 || HAVE_KERNEL_LZMA || HAVE_KERNEL_LZO
+ depends on HAVE_KERNEL_GZIP || HAVE_KERNEL_BZIP2 || HAVE_KERNEL_LZMA || HAVE_KERNEL_XZ || HAVE_KERNEL_LZO
help
The linux kernel is a kind of self-extracting executable.
Several compression algorithms are available, which differ
@@ -181,6 +184,21 @@
two. Compression is slowest. The kernel size is about 33%
smaller with LZMA in comparison to gzip.
+config KERNEL_XZ
+ bool "XZ"
+ depends on HAVE_KERNEL_XZ
+ help
+ XZ uses the LZMA2 algorithm and instruction set specific
+ BCJ filters which can improve compression ratio of executable
+ code. The size of the kernel is about 30% smaller with XZ in
+ comparison to gzip. On architectures for which there is a BCJ
+ filter (i386, x86_64, ARM, IA-64, PowerPC, and SPARC), XZ
+ will create a few percent smaller kernel than plain LZMA.
+
+ The speed is about the same as with LZMA: The decompression
+ speed of XZ is better than that of bzip2 but worse than gzip
+ and LZO. Compression is slow.
+
config KERNEL_LZO
bool "LZO"
depends on HAVE_KERNEL_LZO
@@ -673,7 +691,7 @@
help
Memory Resource Controller Swap Extension comes with its price in
a bigger memory consumption. General purpose distribution kernels
- which want to enable the feautre but keep it disabled by default
+ which want to enable the feature but keep it disabled by default
and let the user enable it by swapaccount boot command line
parameter should have this option unselected.
For those who want to have the feature enabled by default should
diff --git a/kernel/Makefile b/kernel/Makefile
index 5669f71..353d3fe 100644
--- a/kernel/Makefile
+++ b/kernel/Makefile
@@ -43,7 +43,7 @@
obj-$(CONFIG_DEBUG_RT_MUTEXES) += rtmutex-debug.o
obj-$(CONFIG_RT_MUTEX_TESTER) += rtmutex-tester.o
obj-$(CONFIG_GENERIC_ISA_DMA) += dma.o
-obj-$(CONFIG_USE_GENERIC_SMP_HELPERS) += smp.o
+obj-$(CONFIG_SMP) += smp.o
ifneq ($(CONFIG_SMP),y)
obj-y += up.o
endif
diff --git a/kernel/audit.c b/kernel/audit.c
index 77770a0..e495624 100644
--- a/kernel/audit.c
+++ b/kernel/audit.c
@@ -400,7 +400,7 @@
if (err < 0) {
BUG_ON(err != -ECONNREFUSED); /* Shouldn't happen */
printk(KERN_ERR "audit: *NO* daemon at audit_pid=%d\n", audit_pid);
- audit_log_lost("auditd dissapeared\n");
+ audit_log_lost("auditd disappeared\n");
audit_pid = 0;
/* we might get lucky and get this in the next auditd */
audit_hold_skb(skb);
diff --git a/kernel/debug/kdb/kdb_main.c b/kernel/debug/kdb/kdb_main.c
index a6e7297..bd3e8e2 100644
--- a/kernel/debug/kdb/kdb_main.c
+++ b/kernel/debug/kdb/kdb_main.c
@@ -2914,7 +2914,7 @@
}
}
-/* Intialize kdb_printf, breakpoint tables and kdb state */
+/* Initialize kdb_printf, breakpoint tables and kdb state */
void __init kdb_init(int lvl)
{
static int kdb_init_lvl = KDB_NOT_INITIALIZED;
diff --git a/kernel/hrtimer.c b/kernel/hrtimer.c
index 45da2b6..0c8d7c0 100644
--- a/kernel/hrtimer.c
+++ b/kernel/hrtimer.c
@@ -1745,7 +1745,7 @@
}
/*
- * A NULL parameter means "inifinte"
+ * A NULL parameter means "infinite"
*/
if (!expires) {
schedule();
diff --git a/kernel/kexec.c b/kernel/kexec.c
index b55045b..ec19b92 100644
--- a/kernel/kexec.c
+++ b/kernel/kexec.c
@@ -163,7 +163,7 @@
* just verifies it is an address we can use.
*
* Since the kernel does everything in page size chunks ensure
- * the destination addreses are page aligned. Too many
+ * the destination addresses are page aligned. Too many
* special cases crop of when we don't do this. The most
* insidious is getting overlapping destination addresses
* simply because addresses are changed to page size
diff --git a/kernel/latencytop.c b/kernel/latencytop.c
index 17110a4..ee74b35 100644
--- a/kernel/latencytop.c
+++ b/kernel/latencytop.c
@@ -241,24 +241,19 @@
seq_puts(m, "Latency Top version : v0.1\n");
for (i = 0; i < MAXLR; i++) {
- if (latency_record[i].backtrace[0]) {
+ struct latency_record *lr = &latency_record[i];
+
+ if (lr->backtrace[0]) {
int q;
- seq_printf(m, "%i %lu %lu ",
- latency_record[i].count,
- latency_record[i].time,
- latency_record[i].max);
+ seq_printf(m, "%i %lu %lu",
+ lr->count, lr->time, lr->max);
for (q = 0; q < LT_BACKTRACEDEPTH; q++) {
- char sym[KSYM_SYMBOL_LEN];
- char *c;
- if (!latency_record[i].backtrace[q])
+ unsigned long bt = lr->backtrace[q];
+ if (!bt)
break;
- if (latency_record[i].backtrace[q] == ULONG_MAX)
+ if (bt == ULONG_MAX)
break;
- sprint_symbol(sym, latency_record[i].backtrace[q]);
- c = strchr(sym, '+');
- if (c)
- *c = 0;
- seq_printf(m, "%s ", sym);
+ seq_printf(m, " %ps", (void *)bt);
}
seq_printf(m, "\n");
}
diff --git a/kernel/perf_event.c b/kernel/perf_event.c
index b782b7a..05ebe84 100644
--- a/kernel/perf_event.c
+++ b/kernel/perf_event.c
@@ -4664,7 +4664,7 @@
}
EXPORT_SYMBOL_GPL(perf_swevent_get_recursion_context);
-void inline perf_swevent_put_recursion_context(int rctx)
+inline void perf_swevent_put_recursion_context(int rctx)
{
struct swevent_htable *swhash = &__get_cpu_var(swevent_htable);
diff --git a/kernel/power/hibernate.c b/kernel/power/hibernate.c
index 870f72b..1832bd2 100644
--- a/kernel/power/hibernate.c
+++ b/kernel/power/hibernate.c
@@ -51,14 +51,14 @@
static int hibernation_mode = HIBERNATION_SHUTDOWN;
-static struct platform_hibernation_ops *hibernation_ops;
+static const struct platform_hibernation_ops *hibernation_ops;
/**
* hibernation_set_ops - set the global hibernate operations
* @ops: the hibernation operations to use in subsequent hibernation transitions
*/
-void hibernation_set_ops(struct platform_hibernation_ops *ops)
+void hibernation_set_ops(const struct platform_hibernation_ops *ops)
{
if (ops && !(ops->begin && ops->end && ops->pre_snapshot
&& ops->prepare && ops->finish && ops->enter && ops->pre_restore
diff --git a/kernel/power/suspend.c b/kernel/power/suspend.c
index 8850df6..de6f86b 100644
--- a/kernel/power/suspend.c
+++ b/kernel/power/suspend.c
@@ -31,13 +31,13 @@
[PM_SUSPEND_MEM] = "mem",
};
-static struct platform_suspend_ops *suspend_ops;
+static const struct platform_suspend_ops *suspend_ops;
/**
* suspend_set_ops - Set the global suspend method table.
* @ops: Pointer to ops structure.
*/
-void suspend_set_ops(struct platform_suspend_ops *ops)
+void suspend_set_ops(const struct platform_suspend_ops *ops)
{
mutex_lock(&pm_mutex);
suspend_ops = ops;
diff --git a/kernel/power/swap.c b/kernel/power/swap.c
index 8c7e483..6942588 100644
--- a/kernel/power/swap.c
+++ b/kernel/power/swap.c
@@ -888,7 +888,7 @@
/**
* swsusp_read - read the hibernation image.
* @flags_p: flags passed by the "frozen" kernel in the image header should
- * be written into this memeory location
+ * be written into this memory location
*/
int swsusp_read(unsigned int *flags_p)
diff --git a/kernel/printk.c b/kernel/printk.c
index f64b899..53d9a9e 100644
--- a/kernel/printk.c
+++ b/kernel/printk.c
@@ -39,6 +39,7 @@
#include <linux/syslog.h>
#include <linux/cpu.h>
#include <linux/notifier.h>
+#include <linux/rculist.h>
#include <asm/uaccess.h>
@@ -1502,7 +1503,7 @@
/* Don't allow registering multiple times */
if (!dumper->registered) {
dumper->registered = 1;
- list_add_tail(&dumper->list, &dump_list);
+ list_add_tail_rcu(&dumper->list, &dump_list);
err = 0;
}
spin_unlock_irqrestore(&dump_list_lock, flags);
@@ -1526,29 +1527,16 @@
spin_lock_irqsave(&dump_list_lock, flags);
if (dumper->registered) {
dumper->registered = 0;
- list_del(&dumper->list);
+ list_del_rcu(&dumper->list);
err = 0;
}
spin_unlock_irqrestore(&dump_list_lock, flags);
+ synchronize_rcu();
return err;
}
EXPORT_SYMBOL_GPL(kmsg_dump_unregister);
-static const char * const kmsg_reasons[] = {
- [KMSG_DUMP_OOPS] = "oops",
- [KMSG_DUMP_PANIC] = "panic",
- [KMSG_DUMP_KEXEC] = "kexec",
-};
-
-static const char *kmsg_to_str(enum kmsg_dump_reason reason)
-{
- if (reason >= ARRAY_SIZE(kmsg_reasons) || reason < 0)
- return "unknown";
-
- return kmsg_reasons[reason];
-}
-
/**
* kmsg_dump - dump kernel log to kernel message dumpers.
* @reason: the reason (oops, panic etc) for dumping
@@ -1587,13 +1575,9 @@
l2 = chars;
}
- if (!spin_trylock_irqsave(&dump_list_lock, flags)) {
- printk(KERN_ERR "dump_kmsg: dump list lock is held during %s, skipping dump\n",
- kmsg_to_str(reason));
- return;
- }
- list_for_each_entry(dumper, &dump_list, list)
+ rcu_read_lock();
+ list_for_each_entry_rcu(dumper, &dump_list, list)
dumper->dump(dumper, reason, s1, l1, s2, l2);
- spin_unlock_irqrestore(&dump_list_lock, flags);
+ rcu_read_unlock();
}
#endif
diff --git a/kernel/sched.c b/kernel/sched.c
index a0eb094..ea3e5ef 100644
--- a/kernel/sched.c
+++ b/kernel/sched.c
@@ -2505,7 +2505,7 @@
* try_to_wake_up_local - try to wake up a local task with rq lock held
* @p: the thread to be awakened
*
- * Put @p on the run-queue if it's not alredy there. The caller must
+ * Put @p on the run-queue if it's not already there. The caller must
* ensure that this_rq() is locked, @p is bound to this_rq() and not
* the current task. this_rq() stays locked over invocation.
*/
diff --git a/kernel/smp.c b/kernel/smp.c
index 12ed8b0..4ec30e0 100644
--- a/kernel/smp.c
+++ b/kernel/smp.c
@@ -13,6 +13,7 @@
#include <linux/smp.h>
#include <linux/cpu.h>
+#ifdef CONFIG_USE_GENERIC_SMP_HELPERS
static struct {
struct list_head queue;
raw_spinlock_t lock;
@@ -529,3 +530,21 @@
{
raw_spin_unlock_irq(&call_function.lock);
}
+#endif /* USE_GENERIC_SMP_HELPERS */
+
+/*
+ * Call a function on all processors
+ */
+int on_each_cpu(void (*func) (void *info), void *info, int wait)
+{
+ int ret = 0;
+
+ preempt_disable();
+ ret = smp_call_function(func, info, wait);
+ local_irq_disable();
+ func(info);
+ local_irq_enable();
+ preempt_enable();
+ return ret;
+}
+EXPORT_SYMBOL(on_each_cpu);
diff --git a/kernel/softirq.c b/kernel/softirq.c
index 0823778..68eb5ef 100644
--- a/kernel/softirq.c
+++ b/kernel/softirq.c
@@ -885,25 +885,6 @@
}
early_initcall(spawn_ksoftirqd);
-#ifdef CONFIG_SMP
-/*
- * Call a function on all processors
- */
-int on_each_cpu(void (*func) (void *info), void *info, int wait)
-{
- int ret = 0;
-
- preempt_disable();
- ret = smp_call_function(func, info, wait);
- local_irq_disable();
- func(info);
- local_irq_enable();
- preempt_enable();
- return ret;
-}
-EXPORT_SYMBOL(on_each_cpu);
-#endif
-
/*
* [ These __weak aliases are kept in a separate compilation unit, so that
* GCC does not inline them incorrectly. ]
diff --git a/kernel/sys.c b/kernel/sys.c
index 2745dcd..31b71a2 100644
--- a/kernel/sys.c
+++ b/kernel/sys.c
@@ -43,6 +43,8 @@
#include <linux/kprobes.h>
#include <linux/user_namespace.h>
+#include <linux/kmsg_dump.h>
+
#include <asm/uaccess.h>
#include <asm/io.h>
#include <asm/unistd.h>
@@ -285,6 +287,7 @@
*/
void emergency_restart(void)
{
+ kmsg_dump(KMSG_DUMP_EMERG);
machine_emergency_restart();
}
EXPORT_SYMBOL_GPL(emergency_restart);
@@ -312,6 +315,7 @@
printk(KERN_EMERG "Restarting system.\n");
else
printk(KERN_EMERG "Restarting system with command '%s'.\n", cmd);
+ kmsg_dump(KMSG_DUMP_RESTART);
machine_restart(cmd);
}
EXPORT_SYMBOL_GPL(kernel_restart);
@@ -333,6 +337,7 @@
kernel_shutdown_prepare(SYSTEM_HALT);
sysdev_shutdown();
printk(KERN_EMERG "System halted.\n");
+ kmsg_dump(KMSG_DUMP_HALT);
machine_halt();
}
@@ -351,6 +356,7 @@
disable_nonboot_cpus();
sysdev_shutdown();
printk(KERN_EMERG "Power down.\n");
+ kmsg_dump(KMSG_DUMP_POWEROFF);
machine_power_off();
}
EXPORT_SYMBOL_GPL(kernel_power_off);
diff --git a/kernel/sysctl.c b/kernel/sysctl.c
index ae5cbb1..bc86bb3 100644
--- a/kernel/sysctl.c
+++ b/kernel/sysctl.c
@@ -24,6 +24,7 @@
#include <linux/slab.h>
#include <linux/sysctl.h>
#include <linux/signal.h>
+#include <linux/printk.h>
#include <linux/proc_fs.h>
#include <linux/security.h>
#include <linux/ctype.h>
@@ -245,10 +246,6 @@
.mode = 0555,
.child = dev_table,
},
-/*
- * NOTE: do not add new entries to this table unless you have read
- * Documentation/sysctl/ctl_unnumbered.txt
- */
{ }
};
@@ -710,6 +707,15 @@
.extra1 = &zero,
.extra2 = &one,
},
+ {
+ .procname = "kptr_restrict",
+ .data = &kptr_restrict,
+ .maxlen = sizeof(int),
+ .mode = 0644,
+ .proc_handler = proc_dointvec_minmax,
+ .extra1 = &zero,
+ .extra2 = &two,
+ },
#endif
{
.procname = "ngroups_max",
@@ -962,10 +968,6 @@
.proc_handler = proc_dointvec,
},
#endif
-/*
- * NOTE: do not add new entries to this table unless you have read
- * Documentation/sysctl/ctl_unnumbered.txt
- */
{ }
};
@@ -1326,11 +1328,6 @@
.extra2 = &one,
},
#endif
-
-/*
- * NOTE: do not add new entries to this table unless you have read
- * Documentation/sysctl/ctl_unnumbered.txt
- */
{ }
};
@@ -1486,10 +1483,6 @@
.proc_handler = &pipe_proc_fn,
.extra1 = &pipe_min_size,
},
-/*
- * NOTE: do not add new entries to this table unless you have read
- * Documentation/sysctl/ctl_unnumbered.txt
- */
{ }
};
@@ -2899,7 +2892,7 @@
}
}
-#else /* CONFIG_PROC_FS */
+#else /* CONFIG_PROC_SYSCTL */
int proc_dostring(struct ctl_table *table, int write,
void __user *buffer, size_t *lenp, loff_t *ppos)
@@ -2951,7 +2944,7 @@
}
-#endif /* CONFIG_PROC_FS */
+#endif /* CONFIG_PROC_SYSCTL */
/*
* No sense putting this after each symbol definition, twice,
diff --git a/kernel/sysctl_binary.c b/kernel/sysctl_binary.c
index 4b2545a..b875bed 100644
--- a/kernel/sysctl_binary.c
+++ b/kernel/sysctl_binary.c
@@ -1192,7 +1192,7 @@
buf[result] = '\0';
- /* Convert the decnet addresss to binary */
+ /* Convert the decnet address to binary */
result = -EIO;
nodep = strchr(buf, '.') + 1;
if (!nodep)
diff --git a/kernel/taskstats.c b/kernel/taskstats.c
index 69691eb..3971c6b 100644
--- a/kernel/taskstats.c
+++ b/kernel/taskstats.c
@@ -348,7 +348,7 @@
return ret;
}
-#ifdef CONFIG_IA64
+#if defined(CONFIG_64BIT) && !defined(CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS)
#define TASKSTATS_NEEDS_PADDING 1
#endif
diff --git a/kernel/time.c b/kernel/time.c
index ba9b338..3217435 100644
--- a/kernel/time.c
+++ b/kernel/time.c
@@ -238,7 +238,7 @@
* Avoid unnecessary multiplications/divisions in the
* two most common HZ cases:
*/
-unsigned int inline jiffies_to_msecs(const unsigned long j)
+inline unsigned int jiffies_to_msecs(const unsigned long j)
{
#if HZ <= MSEC_PER_SEC && !(MSEC_PER_SEC % HZ)
return (MSEC_PER_SEC / HZ) * j;
@@ -254,7 +254,7 @@
}
EXPORT_SYMBOL(jiffies_to_msecs);
-unsigned int inline jiffies_to_usecs(const unsigned long j)
+inline unsigned int jiffies_to_usecs(const unsigned long j)
{
#if HZ <= USEC_PER_SEC && !(USEC_PER_SEC % HZ)
return (USEC_PER_SEC / HZ) * j;
diff --git a/kernel/time/clocksource.c b/kernel/time/clocksource.c
index df140cd..c50a034 100644
--- a/kernel/time/clocksource.c
+++ b/kernel/time/clocksource.c
@@ -679,7 +679,7 @@
int __clocksource_register_scale(struct clocksource *cs, u32 scale, u32 freq)
{
- /* Intialize mult/shift and max_idle_ns */
+ /* Initialize mult/shift and max_idle_ns */
__clocksource_updatefreq_scale(cs, scale, freq);
/* Add clocksource to the clcoksource list */
diff --git a/kernel/time/ntp.c b/kernel/time/ntp.c
index d232189..5c00242 100644
--- a/kernel/time/ntp.c
+++ b/kernel/time/ntp.c
@@ -14,6 +14,7 @@
#include <linux/timex.h>
#include <linux/time.h>
#include <linux/mm.h>
+#include <linux/module.h>
/*
* NTP timekeeping variables:
@@ -74,6 +75,162 @@
/* constant (boot-param configurable) NTP tick adjustment (upscaled) */
static s64 ntp_tick_adj;
+#ifdef CONFIG_NTP_PPS
+
+/*
+ * The following variables are used when a pulse-per-second (PPS) signal
+ * is available. They establish the engineering parameters of the clock
+ * discipline loop when controlled by the PPS signal.
+ */
+#define PPS_VALID 10 /* PPS signal watchdog max (s) */
+#define PPS_POPCORN 4 /* popcorn spike threshold (shift) */
+#define PPS_INTMIN 2 /* min freq interval (s) (shift) */
+#define PPS_INTMAX 8 /* max freq interval (s) (shift) */
+#define PPS_INTCOUNT 4 /* number of consecutive good intervals to
+ increase pps_shift or consecutive bad
+ intervals to decrease it */
+#define PPS_MAXWANDER 100000 /* max PPS freq wander (ns/s) */
+
+static int pps_valid; /* signal watchdog counter */
+static long pps_tf[3]; /* phase median filter */
+static long pps_jitter; /* current jitter (ns) */
+static struct timespec pps_fbase; /* beginning of the last freq interval */
+static int pps_shift; /* current interval duration (s) (shift) */
+static int pps_intcnt; /* interval counter */
+static s64 pps_freq; /* frequency offset (scaled ns/s) */
+static long pps_stabil; /* current stability (scaled ns/s) */
+
+/*
+ * PPS signal quality monitors
+ */
+static long pps_calcnt; /* calibration intervals */
+static long pps_jitcnt; /* jitter limit exceeded */
+static long pps_stbcnt; /* stability limit exceeded */
+static long pps_errcnt; /* calibration errors */
+
+
+/* PPS kernel consumer compensates the whole phase error immediately.
+ * Otherwise, reduce the offset by a fixed factor times the time constant.
+ */
+static inline s64 ntp_offset_chunk(s64 offset)
+{
+ if (time_status & STA_PPSTIME && time_status & STA_PPSSIGNAL)
+ return offset;
+ else
+ return shift_right(offset, SHIFT_PLL + time_constant);
+}
+
+static inline void pps_reset_freq_interval(void)
+{
+ /* the PPS calibration interval may end
+ surprisingly early */
+ pps_shift = PPS_INTMIN;
+ pps_intcnt = 0;
+}
+
+/**
+ * pps_clear - Clears the PPS state variables
+ *
+ * Must be called while holding a write on the xtime_lock
+ */
+static inline void pps_clear(void)
+{
+ pps_reset_freq_interval();
+ pps_tf[0] = 0;
+ pps_tf[1] = 0;
+ pps_tf[2] = 0;
+ pps_fbase.tv_sec = pps_fbase.tv_nsec = 0;
+ pps_freq = 0;
+}
+
+/* Decrease pps_valid to indicate that another second has passed since
+ * the last PPS signal. When it reaches 0, indicate that PPS signal is
+ * missing.
+ *
+ * Must be called while holding a write on the xtime_lock
+ */
+static inline void pps_dec_valid(void)
+{
+ if (pps_valid > 0)
+ pps_valid--;
+ else {
+ time_status &= ~(STA_PPSSIGNAL | STA_PPSJITTER |
+ STA_PPSWANDER | STA_PPSERROR);
+ pps_clear();
+ }
+}
+
+static inline void pps_set_freq(s64 freq)
+{
+ pps_freq = freq;
+}
+
+static inline int is_error_status(int status)
+{
+ return (time_status & (STA_UNSYNC|STA_CLOCKERR))
+ /* PPS signal lost when either PPS time or
+ * PPS frequency synchronization requested
+ */
+ || ((time_status & (STA_PPSFREQ|STA_PPSTIME))
+ && !(time_status & STA_PPSSIGNAL))
+ /* PPS jitter exceeded when
+ * PPS time synchronization requested */
+ || ((time_status & (STA_PPSTIME|STA_PPSJITTER))
+ == (STA_PPSTIME|STA_PPSJITTER))
+ /* PPS wander exceeded or calibration error when
+ * PPS frequency synchronization requested
+ */
+ || ((time_status & STA_PPSFREQ)
+ && (time_status & (STA_PPSWANDER|STA_PPSERROR)));
+}
+
+static inline void pps_fill_timex(struct timex *txc)
+{
+ txc->ppsfreq = shift_right((pps_freq >> PPM_SCALE_INV_SHIFT) *
+ PPM_SCALE_INV, NTP_SCALE_SHIFT);
+ txc->jitter = pps_jitter;
+ if (!(time_status & STA_NANO))
+ txc->jitter /= NSEC_PER_USEC;
+ txc->shift = pps_shift;
+ txc->stabil = pps_stabil;
+ txc->jitcnt = pps_jitcnt;
+ txc->calcnt = pps_calcnt;
+ txc->errcnt = pps_errcnt;
+ txc->stbcnt = pps_stbcnt;
+}
+
+#else /* !CONFIG_NTP_PPS */
+
+static inline s64 ntp_offset_chunk(s64 offset)
+{
+ return shift_right(offset, SHIFT_PLL + time_constant);
+}
+
+static inline void pps_reset_freq_interval(void) {}
+static inline void pps_clear(void) {}
+static inline void pps_dec_valid(void) {}
+static inline void pps_set_freq(s64 freq) {}
+
+static inline int is_error_status(int status)
+{
+ return status & (STA_UNSYNC|STA_CLOCKERR);
+}
+
+static inline void pps_fill_timex(struct timex *txc)
+{
+ /* PPS is not implemented, so these are zero */
+ txc->ppsfreq = 0;
+ txc->jitter = 0;
+ txc->shift = 0;
+ txc->stabil = 0;
+ txc->jitcnt = 0;
+ txc->calcnt = 0;
+ txc->errcnt = 0;
+ txc->stbcnt = 0;
+}
+
+#endif /* CONFIG_NTP_PPS */
+
/*
* NTP methods:
*/
@@ -185,6 +342,9 @@
tick_length = tick_length_base;
time_offset = 0;
+
+ /* Clear PPS state variables */
+ pps_clear();
}
/*
@@ -250,16 +410,16 @@
time_status |= STA_UNSYNC;
}
- /*
- * Compute the phase adjustment for the next second. The offset is
- * reduced by a fixed factor times the time constant.
- */
+ /* Compute the phase adjustment for the next second */
tick_length = tick_length_base;
- delta = shift_right(time_offset, SHIFT_PLL + time_constant);
+ delta = ntp_offset_chunk(time_offset);
time_offset -= delta;
tick_length += delta;
+ /* Check PPS signal */
+ pps_dec_valid();
+
if (!time_adjust)
return;
@@ -369,6 +529,8 @@
if ((time_status & STA_PLL) && !(txc->status & STA_PLL)) {
time_state = TIME_OK;
time_status = STA_UNSYNC;
+ /* restart PPS frequency calibration */
+ pps_reset_freq_interval();
}
/*
@@ -418,6 +580,8 @@
time_freq = txc->freq * PPM_SCALE;
time_freq = min(time_freq, MAXFREQ_SCALED);
time_freq = max(time_freq, -MAXFREQ_SCALED);
+ /* update pps_freq */
+ pps_set_freq(time_freq);
}
if (txc->modes & ADJ_MAXERROR)
@@ -508,7 +672,8 @@
}
result = time_state; /* mostly `TIME_OK' */
- if (time_status & (STA_UNSYNC|STA_CLOCKERR))
+ /* check for errors */
+ if (is_error_status(time_status))
result = TIME_ERROR;
txc->freq = shift_right((time_freq >> PPM_SCALE_INV_SHIFT) *
@@ -522,15 +687,8 @@
txc->tick = tick_usec;
txc->tai = time_tai;
- /* PPS is not implemented, so these are zero */
- txc->ppsfreq = 0;
- txc->jitter = 0;
- txc->shift = 0;
- txc->stabil = 0;
- txc->jitcnt = 0;
- txc->calcnt = 0;
- txc->errcnt = 0;
- txc->stbcnt = 0;
+ /* fill PPS status fields */
+ pps_fill_timex(txc);
write_sequnlock_irq(&xtime_lock);
@@ -544,6 +702,243 @@
return result;
}
+#ifdef CONFIG_NTP_PPS
+
+/* actually struct pps_normtime is good old struct timespec, but it is
+ * semantically different (and it is the reason why it was invented):
+ * pps_normtime.nsec has a range of ( -NSEC_PER_SEC / 2, NSEC_PER_SEC / 2 ]
+ * while timespec.tv_nsec has a range of [0, NSEC_PER_SEC) */
+struct pps_normtime {
+ __kernel_time_t sec; /* seconds */
+ long nsec; /* nanoseconds */
+};
+
+/* normalize the timestamp so that nsec is in the
+ ( -NSEC_PER_SEC / 2, NSEC_PER_SEC / 2 ] interval */
+static inline struct pps_normtime pps_normalize_ts(struct timespec ts)
+{
+ struct pps_normtime norm = {
+ .sec = ts.tv_sec,
+ .nsec = ts.tv_nsec
+ };
+
+ if (norm.nsec > (NSEC_PER_SEC >> 1)) {
+ norm.nsec -= NSEC_PER_SEC;
+ norm.sec++;
+ }
+
+ return norm;
+}
+
+/* get current phase correction and jitter */
+static inline long pps_phase_filter_get(long *jitter)
+{
+ *jitter = pps_tf[0] - pps_tf[1];
+ if (*jitter < 0)
+ *jitter = -*jitter;
+
+ /* TODO: test various filters */
+ return pps_tf[0];
+}
+
+/* add the sample to the phase filter */
+static inline void pps_phase_filter_add(long err)
+{
+ pps_tf[2] = pps_tf[1];
+ pps_tf[1] = pps_tf[0];
+ pps_tf[0] = err;
+}
+
+/* decrease frequency calibration interval length.
+ * It is halved after four consecutive unstable intervals.
+ */
+static inline void pps_dec_freq_interval(void)
+{
+ if (--pps_intcnt <= -PPS_INTCOUNT) {
+ pps_intcnt = -PPS_INTCOUNT;
+ if (pps_shift > PPS_INTMIN) {
+ pps_shift--;
+ pps_intcnt = 0;
+ }
+ }
+}
+
+/* increase frequency calibration interval length.
+ * It is doubled after four consecutive stable intervals.
+ */
+static inline void pps_inc_freq_interval(void)
+{
+ if (++pps_intcnt >= PPS_INTCOUNT) {
+ pps_intcnt = PPS_INTCOUNT;
+ if (pps_shift < PPS_INTMAX) {
+ pps_shift++;
+ pps_intcnt = 0;
+ }
+ }
+}
+
+/* update clock frequency based on MONOTONIC_RAW clock PPS signal
+ * timestamps
+ *
+ * At the end of the calibration interval the difference between the
+ * first and last MONOTONIC_RAW clock timestamps divided by the length
+ * of the interval becomes the frequency update. If the interval was
+ * too long, the data are discarded.
+ * Returns the difference between old and new frequency values.
+ */
+static long hardpps_update_freq(struct pps_normtime freq_norm)
+{
+ long delta, delta_mod;
+ s64 ftemp;
+
+ /* check if the frequency interval was too long */
+ if (freq_norm.sec > (2 << pps_shift)) {
+ time_status |= STA_PPSERROR;
+ pps_errcnt++;
+ pps_dec_freq_interval();
+ pr_err("hardpps: PPSERROR: interval too long - %ld s\n",
+ freq_norm.sec);
+ return 0;
+ }
+
+ /* here the raw frequency offset and wander (stability) is
+ * calculated. If the wander is less than the wander threshold
+ * the interval is increased; otherwise it is decreased.
+ */
+ ftemp = div_s64(((s64)(-freq_norm.nsec)) << NTP_SCALE_SHIFT,
+ freq_norm.sec);
+ delta = shift_right(ftemp - pps_freq, NTP_SCALE_SHIFT);
+ pps_freq = ftemp;
+ if (delta > PPS_MAXWANDER || delta < -PPS_MAXWANDER) {
+ pr_warning("hardpps: PPSWANDER: change=%ld\n", delta);
+ time_status |= STA_PPSWANDER;
+ pps_stbcnt++;
+ pps_dec_freq_interval();
+ } else { /* good sample */
+ pps_inc_freq_interval();
+ }
+
+ /* the stability metric is calculated as the average of recent
+ * frequency changes, but is used only for performance
+ * monitoring
+ */
+ delta_mod = delta;
+ if (delta_mod < 0)
+ delta_mod = -delta_mod;
+ pps_stabil += (div_s64(((s64)delta_mod) <<
+ (NTP_SCALE_SHIFT - SHIFT_USEC),
+ NSEC_PER_USEC) - pps_stabil) >> PPS_INTMIN;
+
+ /* if enabled, the system clock frequency is updated */
+ if ((time_status & STA_PPSFREQ) != 0 &&
+ (time_status & STA_FREQHOLD) == 0) {
+ time_freq = pps_freq;
+ ntp_update_frequency();
+ }
+
+ return delta;
+}
+
+/* correct REALTIME clock phase error against PPS signal */
+static void hardpps_update_phase(long error)
+{
+ long correction = -error;
+ long jitter;
+
+ /* add the sample to the median filter */
+ pps_phase_filter_add(correction);
+ correction = pps_phase_filter_get(&jitter);
+
+ /* Nominal jitter is due to PPS signal noise. If it exceeds the
+ * threshold, the sample is discarded; otherwise, if so enabled,
+ * the time offset is updated.
+ */
+ if (jitter > (pps_jitter << PPS_POPCORN)) {
+ pr_warning("hardpps: PPSJITTER: jitter=%ld, limit=%ld\n",
+ jitter, (pps_jitter << PPS_POPCORN));
+ time_status |= STA_PPSJITTER;
+ pps_jitcnt++;
+ } else if (time_status & STA_PPSTIME) {
+ /* correct the time using the phase offset */
+ time_offset = div_s64(((s64)correction) << NTP_SCALE_SHIFT,
+ NTP_INTERVAL_FREQ);
+ /* cancel running adjtime() */
+ time_adjust = 0;
+ }
+ /* update jitter */
+ pps_jitter += (jitter - pps_jitter) >> PPS_INTMIN;
+}
+
+/*
+ * hardpps() - discipline CPU clock oscillator to external PPS signal
+ *
+ * This routine is called at each PPS signal arrival in order to
+ * discipline the CPU clock oscillator to the PPS signal. It takes two
+ * parameters: REALTIME and MONOTONIC_RAW clock timestamps. The former
+ * is used to correct clock phase error and the latter is used to
+ * correct the frequency.
+ *
+ * This code is based on David Mills's reference nanokernel
+ * implementation. It was mostly rewritten but keeps the same idea.
+ */
+void hardpps(const struct timespec *phase_ts, const struct timespec *raw_ts)
+{
+ struct pps_normtime pts_norm, freq_norm;
+ unsigned long flags;
+
+ pts_norm = pps_normalize_ts(*phase_ts);
+
+ write_seqlock_irqsave(&xtime_lock, flags);
+
+ /* clear the error bits, they will be set again if needed */
+ time_status &= ~(STA_PPSJITTER | STA_PPSWANDER | STA_PPSERROR);
+
+ /* indicate signal presence */
+ time_status |= STA_PPSSIGNAL;
+ pps_valid = PPS_VALID;
+
+ /* when called for the first time,
+ * just start the frequency interval */
+ if (unlikely(pps_fbase.tv_sec == 0)) {
+ pps_fbase = *raw_ts;
+ write_sequnlock_irqrestore(&xtime_lock, flags);
+ return;
+ }
+
+ /* ok, now we have a base for frequency calculation */
+ freq_norm = pps_normalize_ts(timespec_sub(*raw_ts, pps_fbase));
+
+ /* check that the signal is in the range
+ * [1s - MAXFREQ us, 1s + MAXFREQ us], otherwise reject it */
+ if ((freq_norm.sec == 0) ||
+ (freq_norm.nsec > MAXFREQ * freq_norm.sec) ||
+ (freq_norm.nsec < -MAXFREQ * freq_norm.sec)) {
+ time_status |= STA_PPSJITTER;
+ /* restart the frequency calibration interval */
+ pps_fbase = *raw_ts;
+ write_sequnlock_irqrestore(&xtime_lock, flags);
+ pr_err("hardpps: PPSJITTER: bad pulse\n");
+ return;
+ }
+
+ /* signal is ok */
+
+ /* check if the current frequency interval is finished */
+ if (freq_norm.sec >= (1 << pps_shift)) {
+ pps_calcnt++;
+ /* restart the frequency calibration interval */
+ pps_fbase = *raw_ts;
+ hardpps_update_freq(freq_norm);
+ }
+
+ hardpps_update_phase(pts_norm.nsec);
+
+ write_sequnlock_irqrestore(&xtime_lock, flags);
+}
+EXPORT_SYMBOL(hardpps);
+
+#endif /* CONFIG_NTP_PPS */
+
static int __init ntp_tick_adj_setup(char *str)
{
ntp_tick_adj = simple_strtol(str, NULL, 0);
diff --git a/kernel/time/timekeeping.c b/kernel/time/timekeeping.c
index 5bb86da..5536aaf 100644
--- a/kernel/time/timekeeping.c
+++ b/kernel/time/timekeeping.c
@@ -288,6 +288,49 @@
}
EXPORT_SYMBOL_GPL(ktime_get_ts);
+#ifdef CONFIG_NTP_PPS
+
+/**
+ * getnstime_raw_and_real - get day and raw monotonic time in timespec format
+ * @ts_raw: pointer to the timespec to be set to raw monotonic time
+ * @ts_real: pointer to the timespec to be set to the time of day
+ *
+ * This function reads both the time of day and raw monotonic time at the
+ * same time atomically and stores the resulting timestamps in timespec
+ * format.
+ */
+void getnstime_raw_and_real(struct timespec *ts_raw, struct timespec *ts_real)
+{
+ unsigned long seq;
+ s64 nsecs_raw, nsecs_real;
+
+ WARN_ON_ONCE(timekeeping_suspended);
+
+ do {
+ u32 arch_offset;
+
+ seq = read_seqbegin(&xtime_lock);
+
+ *ts_raw = raw_time;
+ *ts_real = xtime;
+
+ nsecs_raw = timekeeping_get_ns_raw();
+ nsecs_real = timekeeping_get_ns();
+
+ /* If arch requires, add in gettimeoffset() */
+ arch_offset = arch_gettimeoffset();
+ nsecs_raw += arch_offset;
+ nsecs_real += arch_offset;
+
+ } while (read_seqretry(&xtime_lock, seq));
+
+ timespec_add_ns(ts_raw, nsecs_raw);
+ timespec_add_ns(ts_real, nsecs_real);
+}
+EXPORT_SYMBOL(getnstime_raw_and_real);
+
+#endif /* CONFIG_NTP_PPS */
+
/**
* do_gettimeofday - Returns the time of day in a timeval
* @tv: pointer to the timeval to be set
diff --git a/kernel/trace/trace_entries.h b/kernel/trace/trace_entries.h
index e3dfeca..6cf2237 100644
--- a/kernel/trace/trace_entries.h
+++ b/kernel/trace/trace_entries.h
@@ -53,7 +53,7 @@
*/
/*
- * Function trace entry - function address and parent function addres:
+ * Function trace entry - function address and parent function address:
*/
FTRACE_ENTRY(function, ftrace_entry,
diff --git a/kernel/user_namespace.c b/kernel/user_namespace.c
index 2591583..9da289c 100644
--- a/kernel/user_namespace.c
+++ b/kernel/user_namespace.c
@@ -12,6 +12,8 @@
#include <linux/highuid.h>
#include <linux/cred.h>
+static struct kmem_cache *user_ns_cachep __read_mostly;
+
/*
* Create a new user namespace, deriving the creator from the user in the
* passed credentials, and replacing that user with the new root user for the
@@ -26,7 +28,7 @@
struct user_struct *root_user;
int n;
- ns = kmalloc(sizeof(struct user_namespace), GFP_KERNEL);
+ ns = kmem_cache_alloc(user_ns_cachep, GFP_KERNEL);
if (!ns)
return -ENOMEM;
@@ -38,7 +40,7 @@
/* Alloc new root user. */
root_user = alloc_uid(ns, 0);
if (!root_user) {
- kfree(ns);
+ kmem_cache_free(user_ns_cachep, ns);
return -ENOMEM;
}
@@ -71,7 +73,7 @@
struct user_namespace *ns =
container_of(work, struct user_namespace, destroyer);
free_uid(ns->creator);
- kfree(ns);
+ kmem_cache_free(user_ns_cachep, ns);
}
void free_user_ns(struct kref *kref)
@@ -126,3 +128,10 @@
/* No useful relationship so no mapping */
return overflowgid;
}
+
+static __init int user_namespaces_init(void)
+{
+ user_ns_cachep = KMEM_CACHE(user_namespace, SLAB_PANIC);
+ return 0;
+}
+module_init(user_namespaces_init);
diff --git a/lib/Kconfig b/lib/Kconfig
index 3116aa6..0ee67e0 100644
--- a/lib/Kconfig
+++ b/lib/Kconfig
@@ -106,6 +106,8 @@
config LZO_DECOMPRESS
tristate
+source "lib/xz/Kconfig"
+
#
# These all provide a common interface (hence the apparent duplication with
# ZLIB_INFLATE; DECOMPRESS_GZIP is just a wrapper.)
@@ -120,6 +122,10 @@
config DECOMPRESS_LZMA
tristate
+config DECOMPRESS_XZ
+ select XZ_DEC
+ tristate
+
config DECOMPRESS_LZO
select LZO_DECOMPRESS
tristate
diff --git a/lib/Makefile b/lib/Makefile
index d7b6e30a..cbb774f 100644
--- a/lib/Makefile
+++ b/lib/Makefile
@@ -12,7 +12,7 @@
idr.o int_sqrt.o extable.o prio_tree.o \
sha1.o irq_regs.o reciprocal_div.o argv_split.o \
proportions.o prio_heap.o ratelimit.o show_mem.o \
- is_single_threaded.o plist.o decompress.o flex_array.o
+ is_single_threaded.o plist.o decompress.o
lib-$(CONFIG_MMU) += ioremap.o
lib-$(CONFIG_SMP) += cpumask.o
@@ -21,7 +21,7 @@
obj-y += bcd.o div64.o sort.o parser.o halfmd4.o debug_locks.o random32.o \
bust_spinlocks.o hexdump.o kasprintf.o bitmap.o scatterlist.o \
- string_helpers.o gcd.o lcm.o list_sort.o uuid.o
+ string_helpers.o gcd.o lcm.o list_sort.o uuid.o flex_array.o
ifeq ($(CONFIG_DEBUG_KOBJECT),y)
CFLAGS_kobject.o += -DDEBUG
@@ -69,11 +69,13 @@
obj-$(CONFIG_REED_SOLOMON) += reed_solomon/
obj-$(CONFIG_LZO_COMPRESS) += lzo/
obj-$(CONFIG_LZO_DECOMPRESS) += lzo/
+obj-$(CONFIG_XZ_DEC) += xz/
obj-$(CONFIG_RAID6_PQ) += raid6/
lib-$(CONFIG_DECOMPRESS_GZIP) += decompress_inflate.o
lib-$(CONFIG_DECOMPRESS_BZIP2) += decompress_bunzip2.o
lib-$(CONFIG_DECOMPRESS_LZMA) += decompress_unlzma.o
+lib-$(CONFIG_DECOMPRESS_XZ) += decompress_unxz.o
lib-$(CONFIG_DECOMPRESS_LZO) += decompress_unlzo.o
obj-$(CONFIG_TEXTSEARCH) += textsearch.o
diff --git a/lib/decompress.c b/lib/decompress.c
index a760681..3d766b7 100644
--- a/lib/decompress.c
+++ b/lib/decompress.c
@@ -8,6 +8,7 @@
#include <linux/decompress/bunzip2.h>
#include <linux/decompress/unlzma.h>
+#include <linux/decompress/unxz.h>
#include <linux/decompress/inflate.h>
#include <linux/decompress/unlzo.h>
@@ -23,6 +24,9 @@
#ifndef CONFIG_DECOMPRESS_LZMA
# define unlzma NULL
#endif
+#ifndef CONFIG_DECOMPRESS_XZ
+# define unxz NULL
+#endif
#ifndef CONFIG_DECOMPRESS_LZO
# define unlzo NULL
#endif
@@ -36,6 +40,7 @@
{ {037, 0236}, "gzip", gunzip },
{ {0x42, 0x5a}, "bzip2", bunzip2 },
{ {0x5d, 0x00}, "lzma", unlzma },
+ { {0xfd, 0x37}, "xz", unxz },
{ {0x89, 0x4c}, "lzo", unlzo },
{ {0, 0}, NULL, NULL }
};
diff --git a/lib/decompress_bunzip2.c b/lib/decompress_bunzip2.c
index 81c8bb1..a7b80c1 100644
--- a/lib/decompress_bunzip2.c
+++ b/lib/decompress_bunzip2.c
@@ -49,7 +49,6 @@
#define PREBOOT
#else
#include <linux/decompress/bunzip2.h>
-#include <linux/slab.h>
#endif /* STATIC */
#include <linux/decompress/mm.h>
@@ -682,13 +681,12 @@
int(*flush)(void*, unsigned int),
unsigned char *outbuf,
int *pos,
- void(*error_fn)(char *x))
+ void(*error)(char *x))
{
struct bunzip_data *bd;
int i = -1;
unsigned char *inbuf;
- set_error_fn(error_fn);
if (flush)
outbuf = malloc(BZIP2_IOBUF_SIZE);
@@ -751,8 +749,8 @@
int(*flush)(void*, unsigned int),
unsigned char *outbuf,
int *pos,
- void(*error_fn)(char *x))
+ void(*error)(char *x))
{
- return bunzip2(buf, len - 4, fill, flush, outbuf, pos, error_fn);
+ return bunzip2(buf, len - 4, fill, flush, outbuf, pos, error);
}
#endif
diff --git a/lib/decompress_inflate.c b/lib/decompress_inflate.c
index fc686c7..19ff89e 100644
--- a/lib/decompress_inflate.c
+++ b/lib/decompress_inflate.c
@@ -19,7 +19,6 @@
#include "zlib_inflate/inflate.h"
#include "zlib_inflate/infutil.h"
-#include <linux/slab.h>
#endif /* STATIC */
@@ -27,7 +26,7 @@
#define GZIP_IOBUF_SIZE (16*1024)
-static int nofill(void *buffer, unsigned int len)
+static int INIT nofill(void *buffer, unsigned int len)
{
return -1;
}
@@ -38,13 +37,12 @@
int(*flush)(void*, unsigned int),
unsigned char *out_buf,
int *pos,
- void(*error_fn)(char *x)) {
+ void(*error)(char *x)) {
u8 *zbuf;
struct z_stream_s *strm;
int rc;
size_t out_len;
- set_error_fn(error_fn);
rc = -1;
if (flush) {
out_len = 0x8000; /* 32 K */
@@ -100,13 +98,22 @@
* possible asciz filename)
*/
strm->next_in = zbuf + 10;
+ strm->avail_in = len - 10;
/* skip over asciz filename */
if (zbuf[3] & 0x8) {
- while (strm->next_in[0])
- strm->next_in++;
- strm->next_in++;
+ do {
+ /*
+ * If the filename doesn't fit into the buffer,
+ * the file is very probably corrupt. Don't try
+ * to read more data.
+ */
+ if (strm->avail_in == 0) {
+ error("header error");
+ goto gunzip_5;
+ }
+ --strm->avail_in;
+ } while (*strm->next_in++);
}
- strm->avail_in = len - (strm->next_in - zbuf);
strm->next_out = out_buf;
strm->avail_out = out_len;
diff --git a/lib/decompress_unlzma.c b/lib/decompress_unlzma.c
index ca82fde..476c65a 100644
--- a/lib/decompress_unlzma.c
+++ b/lib/decompress_unlzma.c
@@ -33,7 +33,6 @@
#define PREBOOT
#else
#include <linux/decompress/unlzma.h>
-#include <linux/slab.h>
#endif /* STATIC */
#include <linux/decompress/mm.h>
@@ -74,6 +73,7 @@
uint32_t code;
uint32_t range;
uint32_t bound;
+ void (*error)(char *);
};
@@ -82,7 +82,7 @@
#define RC_MODEL_TOTAL_BITS 11
-static int nofill(void *buffer, unsigned int len)
+static int INIT nofill(void *buffer, unsigned int len)
{
return -1;
}
@@ -92,7 +92,7 @@
{
rc->buffer_size = rc->fill((char *)rc->buffer, LZMA_IOBUF_SIZE);
if (rc->buffer_size <= 0)
- error("unexpected EOF");
+ rc->error("unexpected EOF");
rc->ptr = rc->buffer;
rc->buffer_end = rc->buffer + rc->buffer_size;
}
@@ -127,12 +127,6 @@
}
-/* Called once. TODO: bb_maybe_free() */
-static inline void INIT rc_free(struct rc *rc)
-{
- free(rc->buffer);
-}
-
/* Called twice, but one callsite is in inline'd rc_is_bit_0_helper() */
static void INIT rc_do_normalize(struct rc *rc)
{
@@ -169,7 +163,7 @@
rc->range = rc->bound;
*p += ((1 << RC_MODEL_TOTAL_BITS) - *p) >> RC_MOVE_BITS;
}
-static inline void rc_update_bit_1(struct rc *rc, uint16_t *p)
+static inline void INIT rc_update_bit_1(struct rc *rc, uint16_t *p)
{
rc->range -= rc->bound;
rc->code -= rc->bound;
@@ -319,32 +313,38 @@
}
-static inline void INIT write_byte(struct writer *wr, uint8_t byte)
+static inline int INIT write_byte(struct writer *wr, uint8_t byte)
{
wr->buffer[wr->buffer_pos++] = wr->previous_byte = byte;
if (wr->flush && wr->buffer_pos == wr->header->dict_size) {
wr->buffer_pos = 0;
wr->global_pos += wr->header->dict_size;
- wr->flush((char *)wr->buffer, wr->header->dict_size);
+ if (wr->flush((char *)wr->buffer, wr->header->dict_size)
+ != wr->header->dict_size)
+ return -1;
}
+ return 0;
}
-static inline void INIT copy_byte(struct writer *wr, uint32_t offs)
+static inline int INIT copy_byte(struct writer *wr, uint32_t offs)
{
- write_byte(wr, peek_old_byte(wr, offs));
+ return write_byte(wr, peek_old_byte(wr, offs));
}
-static inline void INIT copy_bytes(struct writer *wr,
+static inline int INIT copy_bytes(struct writer *wr,
uint32_t rep0, int len)
{
do {
- copy_byte(wr, rep0);
+ if (copy_byte(wr, rep0))
+ return -1;
len--;
} while (len != 0 && wr->buffer_pos < wr->header->dst_size);
+
+ return len;
}
-static inline void INIT process_bit0(struct writer *wr, struct rc *rc,
+static inline int INIT process_bit0(struct writer *wr, struct rc *rc,
struct cstate *cst, uint16_t *p,
int pos_state, uint16_t *prob,
int lc, uint32_t literal_pos_mask) {
@@ -378,16 +378,17 @@
uint16_t *prob_lit = prob + mi;
rc_get_bit(rc, prob_lit, &mi);
}
- write_byte(wr, mi);
if (cst->state < 4)
cst->state = 0;
else if (cst->state < 10)
cst->state -= 3;
else
cst->state -= 6;
+
+ return write_byte(wr, mi);
}
-static inline void INIT process_bit1(struct writer *wr, struct rc *rc,
+static inline int INIT process_bit1(struct writer *wr, struct rc *rc,
struct cstate *cst, uint16_t *p,
int pos_state, uint16_t *prob) {
int offset;
@@ -418,8 +419,7 @@
cst->state = cst->state < LZMA_NUM_LIT_STATES ?
9 : 11;
- copy_byte(wr, cst->rep0);
- return;
+ return copy_byte(wr, cst->rep0);
} else {
rc_update_bit_1(rc, prob);
}
@@ -521,12 +521,15 @@
} else
cst->rep0 = pos_slot;
if (++(cst->rep0) == 0)
- return;
+ return 0;
+ if (cst->rep0 > wr->header->dict_size
+ || cst->rep0 > get_pos(wr))
+ return -1;
}
len += LZMA_MATCH_MIN_LEN;
- copy_bytes(wr, cst->rep0, len);
+ return copy_bytes(wr, cst->rep0, len);
}
@@ -536,7 +539,7 @@
int(*flush)(void*, unsigned int),
unsigned char *output,
int *posp,
- void(*error_fn)(char *x)
+ void(*error)(char *x)
)
{
struct lzma_header header;
@@ -552,7 +555,7 @@
unsigned char *inbuf;
int ret = -1;
- set_error_fn(error_fn);
+ rc.error = error;
if (buf)
inbuf = buf;
@@ -580,8 +583,10 @@
((unsigned char *)&header)[i] = *rc.ptr++;
}
- if (header.pos >= (9 * 5 * 5))
+ if (header.pos >= (9 * 5 * 5)) {
error("bad header");
+ goto exit_1;
+ }
mi = 0;
lc = header.pos;
@@ -627,21 +632,29 @@
int pos_state = get_pos(&wr) & pos_state_mask;
uint16_t *prob = p + LZMA_IS_MATCH +
(cst.state << LZMA_NUM_POS_BITS_MAX) + pos_state;
- if (rc_is_bit_0(&rc, prob))
- process_bit0(&wr, &rc, &cst, p, pos_state, prob,
- lc, literal_pos_mask);
- else {
- process_bit1(&wr, &rc, &cst, p, pos_state, prob);
+ if (rc_is_bit_0(&rc, prob)) {
+ if (process_bit0(&wr, &rc, &cst, p, pos_state, prob,
+ lc, literal_pos_mask)) {
+ error("LZMA data is corrupt");
+ goto exit_3;
+ }
+ } else {
+ if (process_bit1(&wr, &rc, &cst, p, pos_state, prob)) {
+ error("LZMA data is corrupt");
+ goto exit_3;
+ }
if (cst.rep0 == 0)
break;
}
+ if (rc.buffer_size <= 0)
+ goto exit_3;
}
if (posp)
*posp = rc.ptr-rc.buffer;
- if (wr.flush)
- wr.flush(wr.buffer, wr.buffer_pos);
- ret = 0;
+ if (!wr.flush || wr.flush(wr.buffer, wr.buffer_pos) == wr.buffer_pos)
+ ret = 0;
+exit_3:
large_free(p);
exit_2:
if (!output)
@@ -659,9 +672,9 @@
int(*flush)(void*, unsigned int),
unsigned char *output,
int *posp,
- void(*error_fn)(char *x)
+ void(*error)(char *x)
)
{
- return unlzma(buf, in_len - 4, fill, flush, output, posp, error_fn);
+ return unlzma(buf, in_len - 4, fill, flush, output, posp, error);
}
#endif
diff --git a/lib/decompress_unlzo.c b/lib/decompress_unlzo.c
index bcb3a4b..5a7a2ad 100644
--- a/lib/decompress_unlzo.c
+++ b/lib/decompress_unlzo.c
@@ -33,7 +33,6 @@
#ifdef STATIC
#include "lzo/lzo1x_decompress.c"
#else
-#include <linux/slab.h>
#include <linux/decompress/unlzo.h>
#endif
@@ -49,14 +48,25 @@
#define LZO_BLOCK_SIZE (256*1024l)
#define HEADER_HAS_FILTER 0x00000800L
+#define HEADER_SIZE_MIN (9 + 7 + 4 + 8 + 1 + 4)
+#define HEADER_SIZE_MAX (9 + 7 + 1 + 8 + 8 + 4 + 1 + 255 + 4)
-STATIC inline int INIT parse_header(u8 *input, u8 *skip)
+STATIC inline int INIT parse_header(u8 *input, int *skip, int in_len)
{
int l;
u8 *parse = input;
+ u8 *end = input + in_len;
u8 level = 0;
u16 version;
+ /*
+ * Check that there's enough input to possibly have a valid header.
+ * Then it is possible to parse several fields until the minimum
+ * size may have been used.
+ */
+ if (in_len < HEADER_SIZE_MIN)
+ return 0;
+
/* read magic: 9 first bits */
for (l = 0; l < 9; l++) {
if (*parse++ != lzop_magic[l])
@@ -74,6 +84,15 @@
else
parse += 4; /* flags */
+ /*
+ * At least mode, mtime_low, filename length, and checksum must
+ * be left to be parsed. If also mtime_high is present, it's OK
+ * because the next input buffer check is after reading the
+ * filename length.
+ */
+ if (end - parse < 8 + 1 + 4)
+ return 0;
+
/* skip mode and mtime_low */
parse += 8;
if (version >= 0x0940)
@@ -81,6 +100,8 @@
l = *parse++;
/* don't care about the file name, and skip checksum */
+ if (end - parse < l + 4)
+ return 0;
parse += l + 4;
*skip = parse - input;
@@ -91,16 +112,15 @@
int (*fill) (void *, unsigned int),
int (*flush) (void *, unsigned int),
u8 *output, int *posp,
- void (*error_fn) (char *x))
+ void (*error) (char *x))
{
- u8 skip = 0, r = 0;
+ u8 r = 0;
+ int skip = 0;
u32 src_len, dst_len;
size_t tmp;
u8 *in_buf, *in_buf_save, *out_buf;
int ret = -1;
- set_error_fn(error_fn);
-
if (output) {
out_buf = output;
} else if (!flush) {
@@ -119,8 +139,8 @@
goto exit_1;
} else if (input) {
in_buf = input;
- } else if (!fill || !posp) {
- error("NULL input pointer and missing position pointer or fill function");
+ } else if (!fill) {
+ error("NULL input pointer and missing fill function");
goto exit_1;
} else {
in_buf = malloc(lzo1x_worst_compress(LZO_BLOCK_SIZE));
@@ -134,22 +154,47 @@
if (posp)
*posp = 0;
- if (fill)
- fill(in_buf, lzo1x_worst_compress(LZO_BLOCK_SIZE));
+ if (fill) {
+ /*
+ * Start from in_buf + HEADER_SIZE_MAX to make it possible
+ * to use memcpy() to copy the unused data to the beginning
+ * of the buffer. This way memmove() isn't needed which
+ * is missing from pre-boot environments of most archs.
+ */
+ in_buf += HEADER_SIZE_MAX;
+ in_len = fill(in_buf, HEADER_SIZE_MAX);
+ }
- if (!parse_header(input, &skip)) {
+ if (!parse_header(in_buf, &skip, in_len)) {
error("invalid header");
goto exit_2;
}
in_buf += skip;
+ in_len -= skip;
+
+ if (fill) {
+ /* Move the unused data to the beginning of the buffer. */
+ memcpy(in_buf_save, in_buf, in_len);
+ in_buf = in_buf_save;
+ }
if (posp)
*posp = skip;
for (;;) {
/* read uncompressed block size */
+ if (fill && in_len < 4) {
+ skip = fill(in_buf + in_len, 4 - in_len);
+ if (skip > 0)
+ in_len += skip;
+ }
+ if (in_len < 4) {
+ error("file corrupted");
+ goto exit_2;
+ }
dst_len = get_unaligned_be32(in_buf);
in_buf += 4;
+ in_len -= 4;
/* exit if last block */
if (dst_len == 0) {
@@ -164,8 +209,18 @@
}
/* read compressed block size, and skip block checksum info */
+ if (fill && in_len < 8) {
+ skip = fill(in_buf + in_len, 8 - in_len);
+ if (skip > 0)
+ in_len += skip;
+ }
+ if (in_len < 8) {
+ error("file corrupted");
+ goto exit_2;
+ }
src_len = get_unaligned_be32(in_buf);
in_buf += 8;
+ in_len -= 8;
if (src_len <= 0 || src_len > dst_len) {
error("file corrupted");
@@ -173,6 +228,15 @@
}
/* decompress */
+ if (fill && in_len < src_len) {
+ skip = fill(in_buf + in_len, src_len - in_len);
+ if (skip > 0)
+ in_len += skip;
+ }
+ if (in_len < src_len) {
+ error("file corrupted");
+ goto exit_2;
+ }
tmp = dst_len;
/* When the input data is not compressed at all,
@@ -190,17 +254,26 @@
}
}
- if (flush)
- flush(out_buf, dst_len);
+ if (flush && flush(out_buf, dst_len) != dst_len)
+ goto exit_2;
if (output)
out_buf += dst_len;
if (posp)
*posp += src_len + 12;
+
+ in_buf += src_len;
+ in_len -= src_len;
if (fill) {
+ /*
+ * If there happens to still be unused data left in
+ * in_buf, move it to the beginning of the buffer.
+ * Use a loop to avoid memmove() dependency.
+ */
+ if (in_len > 0)
+ for (skip = 0; skip < in_len; ++skip)
+ in_buf_save[skip] = in_buf[skip];
in_buf = in_buf_save;
- fill(in_buf, lzo1x_worst_compress(LZO_BLOCK_SIZE));
- } else
- in_buf += src_len;
+ }
}
ret = 0;
diff --git a/lib/decompress_unxz.c b/lib/decompress_unxz.c
new file mode 100644
index 0000000..cecd23d
--- /dev/null
+++ b/lib/decompress_unxz.c
@@ -0,0 +1,397 @@
+/*
+ * Wrapper for decompressing XZ-compressed kernel, initramfs, and initrd
+ *
+ * Author: Lasse Collin <lasse.collin@tukaani.org>
+ *
+ * This file has been put into the public domain.
+ * You can do whatever you want with this file.
+ */
+
+/*
+ * Important notes about in-place decompression
+ *
+ * At least on x86, the kernel is decompressed in place: the compressed data
+ * is placed to the end of the output buffer, and the decompressor overwrites
+ * most of the compressed data. There must be enough safety margin to
+ * guarantee that the write position is always behind the read position.
+ *
+ * The safety margin for XZ with LZMA2 or BCJ+LZMA2 is calculated below.
+ * Note that the margin with XZ is bigger than with Deflate (gzip)!
+ *
+ * The worst case for in-place decompression is that the beginning of
+ * the file is compressed extremely well, and the rest of the file is
+ * uncompressible. Thus, we must look for worst-case expansion when the
+ * compressor is encoding uncompressible data.
+ *
+ * The structure of the .xz file in case of a compresed kernel is as follows.
+ * Sizes (as bytes) of the fields are in parenthesis.
+ *
+ * Stream Header (12)
+ * Block Header:
+ * Block Header (8-12)
+ * Compressed Data (N)
+ * Block Padding (0-3)
+ * CRC32 (4)
+ * Index (8-20)
+ * Stream Footer (12)
+ *
+ * Normally there is exactly one Block, but let's assume that there are
+ * 2-4 Blocks just in case. Because Stream Header and also Block Header
+ * of the first Block don't make the decompressor produce any uncompressed
+ * data, we can ignore them from our calculations. Block Headers of possible
+ * additional Blocks have to be taken into account still. With these
+ * assumptions, it is safe to assume that the total header overhead is
+ * less than 128 bytes.
+ *
+ * Compressed Data contains LZMA2 or BCJ+LZMA2 encoded data. Since BCJ
+ * doesn't change the size of the data, it is enough to calculate the
+ * safety margin for LZMA2.
+ *
+ * LZMA2 stores the data in chunks. Each chunk has a header whose size is
+ * a maximum of 6 bytes, but to get round 2^n numbers, let's assume that
+ * the maximum chunk header size is 8 bytes. After the chunk header, there
+ * may be up to 64 KiB of actual payload in the chunk. Often the payload is
+ * quite a bit smaller though; to be safe, let's assume that an average
+ * chunk has only 32 KiB of payload.
+ *
+ * The maximum uncompressed size of the payload is 2 MiB. The minimum
+ * uncompressed size of the payload is in practice never less than the
+ * payload size itself. The LZMA2 format would allow uncompressed size
+ * to be less than the payload size, but no sane compressor creates such
+ * files. LZMA2 supports storing uncompressible data in uncompressed form,
+ * so there's never a need to create payloads whose uncompressed size is
+ * smaller than the compressed size.
+ *
+ * The assumption, that the uncompressed size of the payload is never
+ * smaller than the payload itself, is valid only when talking about
+ * the payload as a whole. It is possible that the payload has parts where
+ * the decompressor consumes more input than it produces output. Calculating
+ * the worst case for this would be tricky. Instead of trying to do that,
+ * let's simply make sure that the decompressor never overwrites any bytes
+ * of the payload which it is currently reading.
+ *
+ * Now we have enough information to calculate the safety margin. We need
+ * - 128 bytes for the .xz file format headers;
+ * - 8 bytes per every 32 KiB of uncompressed size (one LZMA2 chunk header
+ * per chunk, each chunk having average payload size of 32 KiB); and
+ * - 64 KiB (biggest possible LZMA2 chunk payload size) to make sure that
+ * the decompressor never overwrites anything from the LZMA2 chunk
+ * payload it is currently reading.
+ *
+ * We get the following formula:
+ *
+ * safety_margin = 128 + uncompressed_size * 8 / 32768 + 65536
+ * = 128 + (uncompressed_size >> 12) + 65536
+ *
+ * For comparision, according to arch/x86/boot/compressed/misc.c, the
+ * equivalent formula for Deflate is this:
+ *
+ * safety_margin = 18 + (uncompressed_size >> 12) + 32768
+ *
+ * Thus, when updating Deflate-only in-place kernel decompressor to
+ * support XZ, the fixed overhead has to be increased from 18+32768 bytes
+ * to 128+65536 bytes.
+ */
+
+/*
+ * STATIC is defined to "static" if we are being built for kernel
+ * decompression (pre-boot code). <linux/decompress/mm.h> will define
+ * STATIC to empty if it wasn't already defined. Since we will need to
+ * know later if we are being used for kernel decompression, we define
+ * XZ_PREBOOT here.
+ */
+#ifdef STATIC
+# define XZ_PREBOOT
+#endif
+#ifdef __KERNEL__
+# include <linux/decompress/mm.h>
+#endif
+#define XZ_EXTERN STATIC
+
+#ifndef XZ_PREBOOT
+# include <linux/slab.h>
+# include <linux/xz.h>
+#else
+/*
+ * Use the internal CRC32 code instead of kernel's CRC32 module, which
+ * is not available in early phase of booting.
+ */
+#define XZ_INTERNAL_CRC32 1
+
+/*
+ * For boot time use, we enable only the BCJ filter of the current
+ * architecture or none if no BCJ filter is available for the architecture.
+ */
+#ifdef CONFIG_X86
+# define XZ_DEC_X86
+#endif
+#ifdef CONFIG_PPC
+# define XZ_DEC_POWERPC
+#endif
+#ifdef CONFIG_ARM
+# define XZ_DEC_ARM
+#endif
+#ifdef CONFIG_IA64
+# define XZ_DEC_IA64
+#endif
+#ifdef CONFIG_SPARC
+# define XZ_DEC_SPARC
+#endif
+
+/*
+ * This will get the basic headers so that memeq() and others
+ * can be defined.
+ */
+#include "xz/xz_private.h"
+
+/*
+ * Replace the normal allocation functions with the versions from
+ * <linux/decompress/mm.h>. vfree() needs to support vfree(NULL)
+ * when XZ_DYNALLOC is used, but the pre-boot free() doesn't support it.
+ * Workaround it here because the other decompressors don't need it.
+ */
+#undef kmalloc
+#undef kfree
+#undef vmalloc
+#undef vfree
+#define kmalloc(size, flags) malloc(size)
+#define kfree(ptr) free(ptr)
+#define vmalloc(size) malloc(size)
+#define vfree(ptr) do { if (ptr != NULL) free(ptr); } while (0)
+
+/*
+ * FIXME: Not all basic memory functions are provided in architecture-specific
+ * files (yet). We define our own versions here for now, but this should be
+ * only a temporary solution.
+ *
+ * memeq and memzero are not used much and any remotely sane implementation
+ * is fast enough. memcpy/memmove speed matters in multi-call mode, but
+ * the kernel image is decompressed in single-call mode, in which only
+ * memcpy speed can matter and only if there is a lot of uncompressible data
+ * (LZMA2 stores uncompressible chunks in uncompressed form). Thus, the
+ * functions below should just be kept small; it's probably not worth
+ * optimizing for speed.
+ */
+
+#ifndef memeq
+static bool memeq(const void *a, const void *b, size_t size)
+{
+ const uint8_t *x = a;
+ const uint8_t *y = b;
+ size_t i;
+
+ for (i = 0; i < size; ++i)
+ if (x[i] != y[i])
+ return false;
+
+ return true;
+}
+#endif
+
+#ifndef memzero
+static void memzero(void *buf, size_t size)
+{
+ uint8_t *b = buf;
+ uint8_t *e = b + size;
+
+ while (b != e)
+ *b++ = '\0';
+}
+#endif
+
+#ifndef memmove
+/* Not static to avoid a conflict with the prototype in the Linux headers. */
+void *memmove(void *dest, const void *src, size_t size)
+{
+ uint8_t *d = dest;
+ const uint8_t *s = src;
+ size_t i;
+
+ if (d < s) {
+ for (i = 0; i < size; ++i)
+ d[i] = s[i];
+ } else if (d > s) {
+ i = size;
+ while (i-- > 0)
+ d[i] = s[i];
+ }
+
+ return dest;
+}
+#endif
+
+/*
+ * Since we need memmove anyway, would use it as memcpy too.
+ * Commented out for now to avoid breaking things.
+ */
+/*
+#ifndef memcpy
+# define memcpy memmove
+#endif
+*/
+
+#include "xz/xz_crc32.c"
+#include "xz/xz_dec_stream.c"
+#include "xz/xz_dec_lzma2.c"
+#include "xz/xz_dec_bcj.c"
+
+#endif /* XZ_PREBOOT */
+
+/* Size of the input and output buffers in multi-call mode */
+#define XZ_IOBUF_SIZE 4096
+
+/*
+ * This function implements the API defined in <linux/decompress/generic.h>.
+ *
+ * This wrapper will automatically choose single-call or multi-call mode
+ * of the native XZ decoder API. The single-call mode can be used only when
+ * both input and output buffers are available as a single chunk, i.e. when
+ * fill() and flush() won't be used.
+ */
+STATIC int INIT unxz(unsigned char *in, int in_size,
+ int (*fill)(void *dest, unsigned int size),
+ int (*flush)(void *src, unsigned int size),
+ unsigned char *out, int *in_used,
+ void (*error)(char *x))
+{
+ struct xz_buf b;
+ struct xz_dec *s;
+ enum xz_ret ret;
+ bool must_free_in = false;
+
+#if XZ_INTERNAL_CRC32
+ xz_crc32_init();
+#endif
+
+ if (in_used != NULL)
+ *in_used = 0;
+
+ if (fill == NULL && flush == NULL)
+ s = xz_dec_init(XZ_SINGLE, 0);
+ else
+ s = xz_dec_init(XZ_DYNALLOC, (uint32_t)-1);
+
+ if (s == NULL)
+ goto error_alloc_state;
+
+ if (flush == NULL) {
+ b.out = out;
+ b.out_size = (size_t)-1;
+ } else {
+ b.out_size = XZ_IOBUF_SIZE;
+ b.out = malloc(XZ_IOBUF_SIZE);
+ if (b.out == NULL)
+ goto error_alloc_out;
+ }
+
+ if (in == NULL) {
+ must_free_in = true;
+ in = malloc(XZ_IOBUF_SIZE);
+ if (in == NULL)
+ goto error_alloc_in;
+ }
+
+ b.in = in;
+ b.in_pos = 0;
+ b.in_size = in_size;
+ b.out_pos = 0;
+
+ if (fill == NULL && flush == NULL) {
+ ret = xz_dec_run(s, &b);
+ } else {
+ do {
+ if (b.in_pos == b.in_size && fill != NULL) {
+ if (in_used != NULL)
+ *in_used += b.in_pos;
+
+ b.in_pos = 0;
+
+ in_size = fill(in, XZ_IOBUF_SIZE);
+ if (in_size < 0) {
+ /*
+ * This isn't an optimal error code
+ * but it probably isn't worth making
+ * a new one either.
+ */
+ ret = XZ_BUF_ERROR;
+ break;
+ }
+
+ b.in_size = in_size;
+ }
+
+ ret = xz_dec_run(s, &b);
+
+ if (flush != NULL && (b.out_pos == b.out_size
+ || (ret != XZ_OK && b.out_pos > 0))) {
+ /*
+ * Setting ret here may hide an error
+ * returned by xz_dec_run(), but probably
+ * it's not too bad.
+ */
+ if (flush(b.out, b.out_pos) != (int)b.out_pos)
+ ret = XZ_BUF_ERROR;
+
+ b.out_pos = 0;
+ }
+ } while (ret == XZ_OK);
+
+ if (must_free_in)
+ free(in);
+
+ if (flush != NULL)
+ free(b.out);
+ }
+
+ if (in_used != NULL)
+ *in_used += b.in_pos;
+
+ xz_dec_end(s);
+
+ switch (ret) {
+ case XZ_STREAM_END:
+ return 0;
+
+ case XZ_MEM_ERROR:
+ /* This can occur only in multi-call mode. */
+ error("XZ decompressor ran out of memory");
+ break;
+
+ case XZ_FORMAT_ERROR:
+ error("Input is not in the XZ format (wrong magic bytes)");
+ break;
+
+ case XZ_OPTIONS_ERROR:
+ error("Input was encoded with settings that are not "
+ "supported by this XZ decoder");
+ break;
+
+ case XZ_DATA_ERROR:
+ case XZ_BUF_ERROR:
+ error("XZ-compressed data is corrupt");
+ break;
+
+ default:
+ error("Bug in the XZ decompressor");
+ break;
+ }
+
+ return -1;
+
+error_alloc_in:
+ if (flush != NULL)
+ free(b.out);
+
+error_alloc_out:
+ xz_dec_end(s);
+
+error_alloc_state:
+ error("XZ decompressor ran out of memory");
+ return -1;
+}
+
+/*
+ * This macro is used by architecture-specific files to decompress
+ * the kernel image.
+ */
+#define decompress unxz
diff --git a/lib/flex_array.c b/lib/flex_array.c
index 77a6fea..c0ea40b 100644
--- a/lib/flex_array.c
+++ b/lib/flex_array.c
@@ -23,6 +23,7 @@
#include <linux/flex_array.h>
#include <linux/slab.h>
#include <linux/stddef.h>
+#include <linux/module.h>
struct flex_array_part {
char elements[FLEX_ARRAY_PART_SIZE];
@@ -103,6 +104,7 @@
FLEX_ARRAY_BASE_BYTES_LEFT);
return ret;
}
+EXPORT_SYMBOL(flex_array_alloc);
static int fa_element_to_part_nr(struct flex_array *fa,
unsigned int element_nr)
@@ -126,12 +128,14 @@
for (part_nr = 0; part_nr < FLEX_ARRAY_NR_BASE_PTRS; part_nr++)
kfree(fa->parts[part_nr]);
}
+EXPORT_SYMBOL(flex_array_free_parts);
void flex_array_free(struct flex_array *fa)
{
flex_array_free_parts(fa);
kfree(fa);
}
+EXPORT_SYMBOL(flex_array_free);
static unsigned int index_inside_part(struct flex_array *fa,
unsigned int element_nr)
@@ -196,6 +200,7 @@
memcpy(dst, src, fa->element_size);
return 0;
}
+EXPORT_SYMBOL(flex_array_put);
/**
* flex_array_clear - clear element in array at @element_nr
@@ -223,6 +228,7 @@
memset(dst, FLEX_ARRAY_FREE, fa->element_size);
return 0;
}
+EXPORT_SYMBOL(flex_array_clear);
/**
* flex_array_prealloc - guarantee that array space exists
@@ -259,6 +265,7 @@
}
return 0;
}
+EXPORT_SYMBOL(flex_array_prealloc);
/**
* flex_array_get - pull data back out of the array
@@ -288,6 +295,7 @@
}
return &part->elements[index_inside_part(fa, element_nr)];
}
+EXPORT_SYMBOL(flex_array_get);
/**
* flex_array_get_ptr - pull a ptr back out of the array
@@ -308,6 +316,7 @@
return *tmp;
}
+EXPORT_SYMBOL(flex_array_get_ptr);
static int part_is_free(struct flex_array_part *part)
{
@@ -348,3 +357,4 @@
}
return ret;
}
+EXPORT_SYMBOL(flex_array_shrink);
diff --git a/lib/hexdump.c b/lib/hexdump.c
index b66b2bd..f5fe6ba 100644
--- a/lib/hexdump.c
+++ b/lib/hexdump.c
@@ -154,6 +154,7 @@
}
EXPORT_SYMBOL(hex_dump_to_buffer);
+#ifdef CONFIG_PRINTK
/**
* print_hex_dump - print a text hex dump to syslog for a binary blob of data
* @level: kernel log level (e.g. KERN_DEBUG)
@@ -238,3 +239,4 @@
buf, len, true);
}
EXPORT_SYMBOL(print_hex_dump_bytes);
+#endif
diff --git a/lib/nlattr.c b/lib/nlattr.c
index 00e8a02..5021cbc 100644
--- a/lib/nlattr.c
+++ b/lib/nlattr.c
@@ -167,7 +167,7 @@
* @policy: validation policy
*
* Parses a stream of attributes and stores a pointer to each attribute in
- * the tb array accessable via the attribute type. Attributes with a type
+ * the tb array accessible via the attribute type. Attributes with a type
* exceeding maxtype will be silently ignored for backwards compatibility
* reasons. policy may be set to NULL if no validation is required.
*
diff --git a/lib/swiotlb.c b/lib/swiotlb.c
index 7c06ee5..c47bbe1 100644
--- a/lib/swiotlb.c
+++ b/lib/swiotlb.c
@@ -60,7 +60,7 @@
static char *io_tlb_start, *io_tlb_end;
/*
- * The number of IO TLB blocks (in groups of 64) betweeen io_tlb_start and
+ * The number of IO TLB blocks (in groups of 64) between io_tlb_start and
* io_tlb_end. This is command line adjustable via setup_io_tlb_npages.
*/
static unsigned long io_tlb_nslabs;
diff --git a/lib/vsprintf.c b/lib/vsprintf.c
index c150d3d..d3023df 100644
--- a/lib/vsprintf.c
+++ b/lib/vsprintf.c
@@ -936,6 +936,8 @@
return string(buf, end, uuid, spec);
}
+int kptr_restrict = 1;
+
/*
* Show a '%p' thing. A kernel extension is that the '%p' is followed
* by an extra set of alphanumeric characters that are extended format
@@ -979,6 +981,7 @@
* Implements a "recursive vsnprintf".
* Do not use this feature without some mechanism to verify the
* correctness of the format string and va_list arguments.
+ * - 'K' For a kernel pointer that should be hidden from unprivileged users
*
* Note: The difference between 'S' and 'F' is that on ia64 and ppc64
* function pointers are really function descriptors, which contain a
@@ -1035,6 +1038,25 @@
return buf + vsnprintf(buf, end - buf,
((struct va_format *)ptr)->fmt,
*(((struct va_format *)ptr)->va));
+ case 'K':
+ /*
+ * %pK cannot be used in IRQ context because its test
+ * for CAP_SYSLOG would be meaningless.
+ */
+ if (in_irq() || in_serving_softirq() || in_nmi()) {
+ if (spec.field_width == -1)
+ spec.field_width = 2 * sizeof(void *);
+ return string(buf, end, "pK-error", spec);
+ } else if ((kptr_restrict == 0) ||
+ (kptr_restrict == 1 &&
+ has_capability_noaudit(current, CAP_SYSLOG)))
+ break;
+
+ if (spec.field_width == -1) {
+ spec.field_width = 2 * sizeof(void *);
+ spec.flags |= ZEROPAD;
+ }
+ return number(buf, end, 0, spec);
}
spec.flags |= SMALL;
if (spec.field_width == -1) {
@@ -1451,7 +1473,7 @@
* @args: Arguments for the format string
*
* The return value is the number of characters which have been written into
- * the @buf not including the trailing '\0'. If @size is <= 0 the function
+ * the @buf not including the trailing '\0'. If @size is == 0 the function
* returns 0.
*
* Call this function if you are already dealing with a va_list.
@@ -1465,7 +1487,11 @@
i = vsnprintf(buf, size, fmt, args);
- return (i >= size) ? (size - 1) : i;
+ if (likely(i < size))
+ return i;
+ if (size != 0)
+ return size - 1;
+ return 0;
}
EXPORT_SYMBOL(vscnprintf);
@@ -1513,14 +1539,10 @@
int i;
va_start(args, fmt);
- i = vsnprintf(buf, size, fmt, args);
+ i = vscnprintf(buf, size, fmt, args);
va_end(args);
- if (likely(i < size))
- return i;
- if (size != 0)
- return size - 1;
- return 0;
+ return i;
}
EXPORT_SYMBOL(scnprintf);
diff --git a/lib/xz/Kconfig b/lib/xz/Kconfig
new file mode 100644
index 0000000..e3b6e18
--- /dev/null
+++ b/lib/xz/Kconfig
@@ -0,0 +1,59 @@
+config XZ_DEC
+ tristate "XZ decompression support"
+ select CRC32
+ help
+ LZMA2 compression algorithm and BCJ filters are supported using
+ the .xz file format as the container. For integrity checking,
+ CRC32 is supported. See Documentation/xz.txt for more information.
+
+config XZ_DEC_X86
+ bool "x86 BCJ filter decoder" if EMBEDDED
+ default y
+ depends on XZ_DEC
+ select XZ_DEC_BCJ
+
+config XZ_DEC_POWERPC
+ bool "PowerPC BCJ filter decoder" if EMBEDDED
+ default y
+ depends on XZ_DEC
+ select XZ_DEC_BCJ
+
+config XZ_DEC_IA64
+ bool "IA-64 BCJ filter decoder" if EMBEDDED
+ default y
+ depends on XZ_DEC
+ select XZ_DEC_BCJ
+
+config XZ_DEC_ARM
+ bool "ARM BCJ filter decoder" if EMBEDDED
+ default y
+ depends on XZ_DEC
+ select XZ_DEC_BCJ
+
+config XZ_DEC_ARMTHUMB
+ bool "ARM-Thumb BCJ filter decoder" if EMBEDDED
+ default y
+ depends on XZ_DEC
+ select XZ_DEC_BCJ
+
+config XZ_DEC_SPARC
+ bool "SPARC BCJ filter decoder" if EMBEDDED
+ default y
+ depends on XZ_DEC
+ select XZ_DEC_BCJ
+
+config XZ_DEC_BCJ
+ bool
+ default n
+
+config XZ_DEC_TEST
+ tristate "XZ decompressor tester"
+ default n
+ depends on XZ_DEC
+ help
+ This allows passing .xz files to the in-kernel XZ decoder via
+ a character special file. It calculates CRC32 of the decompressed
+ data and writes diagnostics to the system log.
+
+ Unless you are developing the XZ decoder, you don't need this
+ and should say N.
diff --git a/lib/xz/Makefile b/lib/xz/Makefile
new file mode 100644
index 0000000..a7fa769
--- /dev/null
+++ b/lib/xz/Makefile
@@ -0,0 +1,5 @@
+obj-$(CONFIG_XZ_DEC) += xz_dec.o
+xz_dec-y := xz_dec_syms.o xz_dec_stream.o xz_dec_lzma2.o
+xz_dec-$(CONFIG_XZ_DEC_BCJ) += xz_dec_bcj.o
+
+obj-$(CONFIG_XZ_DEC_TEST) += xz_dec_test.o
diff --git a/lib/xz/xz_crc32.c b/lib/xz/xz_crc32.c
new file mode 100644
index 0000000..34532d1
--- /dev/null
+++ b/lib/xz/xz_crc32.c
@@ -0,0 +1,59 @@
+/*
+ * CRC32 using the polynomial from IEEE-802.3
+ *
+ * Authors: Lasse Collin <lasse.collin@tukaani.org>
+ * Igor Pavlov <http://7-zip.org/>
+ *
+ * This file has been put into the public domain.
+ * You can do whatever you want with this file.
+ */
+
+/*
+ * This is not the fastest implementation, but it is pretty compact.
+ * The fastest versions of xz_crc32() on modern CPUs without hardware
+ * accelerated CRC instruction are 3-5 times as fast as this version,
+ * but they are bigger and use more memory for the lookup table.
+ */
+
+#include "xz_private.h"
+
+/*
+ * STATIC_RW_DATA is used in the pre-boot environment on some architectures.
+ * See <linux/decompress/mm.h> for details.
+ */
+#ifndef STATIC_RW_DATA
+# define STATIC_RW_DATA static
+#endif
+
+STATIC_RW_DATA uint32_t xz_crc32_table[256];
+
+XZ_EXTERN void xz_crc32_init(void)
+{
+ const uint32_t poly = 0xEDB88320;
+
+ uint32_t i;
+ uint32_t j;
+ uint32_t r;
+
+ for (i = 0; i < 256; ++i) {
+ r = i;
+ for (j = 0; j < 8; ++j)
+ r = (r >> 1) ^ (poly & ~((r & 1) - 1));
+
+ xz_crc32_table[i] = r;
+ }
+
+ return;
+}
+
+XZ_EXTERN uint32_t xz_crc32(const uint8_t *buf, size_t size, uint32_t crc)
+{
+ crc = ~crc;
+
+ while (size != 0) {
+ crc = xz_crc32_table[*buf++ ^ (crc & 0xFF)] ^ (crc >> 8);
+ --size;
+ }
+
+ return ~crc;
+}
diff --git a/lib/xz/xz_dec_bcj.c b/lib/xz/xz_dec_bcj.c
new file mode 100644
index 0000000..e51e255
--- /dev/null
+++ b/lib/xz/xz_dec_bcj.c
@@ -0,0 +1,561 @@
+/*
+ * Branch/Call/Jump (BCJ) filter decoders
+ *
+ * Authors: Lasse Collin <lasse.collin@tukaani.org>
+ * Igor Pavlov <http://7-zip.org/>
+ *
+ * This file has been put into the public domain.
+ * You can do whatever you want with this file.
+ */
+
+#include "xz_private.h"
+
+/*
+ * The rest of the file is inside this ifdef. It makes things a little more
+ * convenient when building without support for any BCJ filters.
+ */
+#ifdef XZ_DEC_BCJ
+
+struct xz_dec_bcj {
+ /* Type of the BCJ filter being used */
+ enum {
+ BCJ_X86 = 4, /* x86 or x86-64 */
+ BCJ_POWERPC = 5, /* Big endian only */
+ BCJ_IA64 = 6, /* Big or little endian */
+ BCJ_ARM = 7, /* Little endian only */
+ BCJ_ARMTHUMB = 8, /* Little endian only */
+ BCJ_SPARC = 9 /* Big or little endian */
+ } type;
+
+ /*
+ * Return value of the next filter in the chain. We need to preserve
+ * this information across calls, because we must not call the next
+ * filter anymore once it has returned XZ_STREAM_END.
+ */
+ enum xz_ret ret;
+
+ /* True if we are operating in single-call mode. */
+ bool single_call;
+
+ /*
+ * Absolute position relative to the beginning of the uncompressed
+ * data (in a single .xz Block). We care only about the lowest 32
+ * bits so this doesn't need to be uint64_t even with big files.
+ */
+ uint32_t pos;
+
+ /* x86 filter state */
+ uint32_t x86_prev_mask;
+
+ /* Temporary space to hold the variables from struct xz_buf */
+ uint8_t *out;
+ size_t out_pos;
+ size_t out_size;
+
+ struct {
+ /* Amount of already filtered data in the beginning of buf */
+ size_t filtered;
+
+ /* Total amount of data currently stored in buf */
+ size_t size;
+
+ /*
+ * Buffer to hold a mix of filtered and unfiltered data. This
+ * needs to be big enough to hold Alignment + 2 * Look-ahead:
+ *
+ * Type Alignment Look-ahead
+ * x86 1 4
+ * PowerPC 4 0
+ * IA-64 16 0
+ * ARM 4 0
+ * ARM-Thumb 2 2
+ * SPARC 4 0
+ */
+ uint8_t buf[16];
+ } temp;
+};
+
+#ifdef XZ_DEC_X86
+/*
+ * This is used to test the most significant byte of a memory address
+ * in an x86 instruction.
+ */
+static inline int bcj_x86_test_msbyte(uint8_t b)
+{
+ return b == 0x00 || b == 0xFF;
+}
+
+static size_t bcj_x86(struct xz_dec_bcj *s, uint8_t *buf, size_t size)
+{
+ static const bool mask_to_allowed_status[8]
+ = { true, true, true, false, true, false, false, false };
+
+ static const uint8_t mask_to_bit_num[8] = { 0, 1, 2, 2, 3, 3, 3, 3 };
+
+ size_t i;
+ size_t prev_pos = (size_t)-1;
+ uint32_t prev_mask = s->x86_prev_mask;
+ uint32_t src;
+ uint32_t dest;
+ uint32_t j;
+ uint8_t b;
+
+ if (size <= 4)
+ return 0;
+
+ size -= 4;
+ for (i = 0; i < size; ++i) {
+ if ((buf[i] & 0xFE) != 0xE8)
+ continue;
+
+ prev_pos = i - prev_pos;
+ if (prev_pos > 3) {
+ prev_mask = 0;
+ } else {
+ prev_mask = (prev_mask << (prev_pos - 1)) & 7;
+ if (prev_mask != 0) {
+ b = buf[i + 4 - mask_to_bit_num[prev_mask]];
+ if (!mask_to_allowed_status[prev_mask]
+ || bcj_x86_test_msbyte(b)) {
+ prev_pos = i;
+ prev_mask = (prev_mask << 1) | 1;
+ continue;
+ }
+ }
+ }
+
+ prev_pos = i;
+
+ if (bcj_x86_test_msbyte(buf[i + 4])) {
+ src = get_unaligned_le32(buf + i + 1);
+ while (true) {
+ dest = src - (s->pos + (uint32_t)i + 5);
+ if (prev_mask == 0)
+ break;
+
+ j = mask_to_bit_num[prev_mask] * 8;
+ b = (uint8_t)(dest >> (24 - j));
+ if (!bcj_x86_test_msbyte(b))
+ break;
+
+ src = dest ^ (((uint32_t)1 << (32 - j)) - 1);
+ }
+
+ dest &= 0x01FFFFFF;
+ dest |= (uint32_t)0 - (dest & 0x01000000);
+ put_unaligned_le32(dest, buf + i + 1);
+ i += 4;
+ } else {
+ prev_mask = (prev_mask << 1) | 1;
+ }
+ }
+
+ prev_pos = i - prev_pos;
+ s->x86_prev_mask = prev_pos > 3 ? 0 : prev_mask << (prev_pos - 1);
+ return i;
+}
+#endif
+
+#ifdef XZ_DEC_POWERPC
+static size_t bcj_powerpc(struct xz_dec_bcj *s, uint8_t *buf, size_t size)
+{
+ size_t i;
+ uint32_t instr;
+
+ for (i = 0; i + 4 <= size; i += 4) {
+ instr = get_unaligned_be32(buf + i);
+ if ((instr & 0xFC000003) == 0x48000001) {
+ instr &= 0x03FFFFFC;
+ instr -= s->pos + (uint32_t)i;
+ instr &= 0x03FFFFFC;
+ instr |= 0x48000001;
+ put_unaligned_be32(instr, buf + i);
+ }
+ }
+
+ return i;
+}
+#endif
+
+#ifdef XZ_DEC_IA64
+static size_t bcj_ia64(struct xz_dec_bcj *s, uint8_t *buf, size_t size)
+{
+ static const uint8_t branch_table[32] = {
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 4, 4, 6, 6, 0, 0, 7, 7,
+ 4, 4, 0, 0, 4, 4, 0, 0
+ };
+
+ /*
+ * The local variables take a little bit stack space, but it's less
+ * than what LZMA2 decoder takes, so it doesn't make sense to reduce
+ * stack usage here without doing that for the LZMA2 decoder too.
+ */
+
+ /* Loop counters */
+ size_t i;
+ size_t j;
+
+ /* Instruction slot (0, 1, or 2) in the 128-bit instruction word */
+ uint32_t slot;
+
+ /* Bitwise offset of the instruction indicated by slot */
+ uint32_t bit_pos;
+
+ /* bit_pos split into byte and bit parts */
+ uint32_t byte_pos;
+ uint32_t bit_res;
+
+ /* Address part of an instruction */
+ uint32_t addr;
+
+ /* Mask used to detect which instructions to convert */
+ uint32_t mask;
+
+ /* 41-bit instruction stored somewhere in the lowest 48 bits */
+ uint64_t instr;
+
+ /* Instruction normalized with bit_res for easier manipulation */
+ uint64_t norm;
+
+ for (i = 0; i + 16 <= size; i += 16) {
+ mask = branch_table[buf[i] & 0x1F];
+ for (slot = 0, bit_pos = 5; slot < 3; ++slot, bit_pos += 41) {
+ if (((mask >> slot) & 1) == 0)
+ continue;
+
+ byte_pos = bit_pos >> 3;
+ bit_res = bit_pos & 7;
+ instr = 0;
+ for (j = 0; j < 6; ++j)
+ instr |= (uint64_t)(buf[i + j + byte_pos])
+ << (8 * j);
+
+ norm = instr >> bit_res;
+
+ if (((norm >> 37) & 0x0F) == 0x05
+ && ((norm >> 9) & 0x07) == 0) {
+ addr = (norm >> 13) & 0x0FFFFF;
+ addr |= ((uint32_t)(norm >> 36) & 1) << 20;
+ addr <<= 4;
+ addr -= s->pos + (uint32_t)i;
+ addr >>= 4;
+
+ norm &= ~((uint64_t)0x8FFFFF << 13);
+ norm |= (uint64_t)(addr & 0x0FFFFF) << 13;
+ norm |= (uint64_t)(addr & 0x100000)
+ << (36 - 20);
+
+ instr &= (1 << bit_res) - 1;
+ instr |= norm << bit_res;
+
+ for (j = 0; j < 6; j++)
+ buf[i + j + byte_pos]
+ = (uint8_t)(instr >> (8 * j));
+ }
+ }
+ }
+
+ return i;
+}
+#endif
+
+#ifdef XZ_DEC_ARM
+static size_t bcj_arm(struct xz_dec_bcj *s, uint8_t *buf, size_t size)
+{
+ size_t i;
+ uint32_t addr;
+
+ for (i = 0; i + 4 <= size; i += 4) {
+ if (buf[i + 3] == 0xEB) {
+ addr = (uint32_t)buf[i] | ((uint32_t)buf[i + 1] << 8)
+ | ((uint32_t)buf[i + 2] << 16);
+ addr <<= 2;
+ addr -= s->pos + (uint32_t)i + 8;
+ addr >>= 2;
+ buf[i] = (uint8_t)addr;
+ buf[i + 1] = (uint8_t)(addr >> 8);
+ buf[i + 2] = (uint8_t)(addr >> 16);
+ }
+ }
+
+ return i;
+}
+#endif
+
+#ifdef XZ_DEC_ARMTHUMB
+static size_t bcj_armthumb(struct xz_dec_bcj *s, uint8_t *buf, size_t size)
+{
+ size_t i;
+ uint32_t addr;
+
+ for (i = 0; i + 4 <= size; i += 2) {
+ if ((buf[i + 1] & 0xF8) == 0xF0
+ && (buf[i + 3] & 0xF8) == 0xF8) {
+ addr = (((uint32_t)buf[i + 1] & 0x07) << 19)
+ | ((uint32_t)buf[i] << 11)
+ | (((uint32_t)buf[i + 3] & 0x07) << 8)
+ | (uint32_t)buf[i + 2];
+ addr <<= 1;
+ addr -= s->pos + (uint32_t)i + 4;
+ addr >>= 1;
+ buf[i + 1] = (uint8_t)(0xF0 | ((addr >> 19) & 0x07));
+ buf[i] = (uint8_t)(addr >> 11);
+ buf[i + 3] = (uint8_t)(0xF8 | ((addr >> 8) & 0x07));
+ buf[i + 2] = (uint8_t)addr;
+ i += 2;
+ }
+ }
+
+ return i;
+}
+#endif
+
+#ifdef XZ_DEC_SPARC
+static size_t bcj_sparc(struct xz_dec_bcj *s, uint8_t *buf, size_t size)
+{
+ size_t i;
+ uint32_t instr;
+
+ for (i = 0; i + 4 <= size; i += 4) {
+ instr = get_unaligned_be32(buf + i);
+ if ((instr >> 22) == 0x100 || (instr >> 22) == 0x1FF) {
+ instr <<= 2;
+ instr -= s->pos + (uint32_t)i;
+ instr >>= 2;
+ instr = ((uint32_t)0x40000000 - (instr & 0x400000))
+ | 0x40000000 | (instr & 0x3FFFFF);
+ put_unaligned_be32(instr, buf + i);
+ }
+ }
+
+ return i;
+}
+#endif
+
+/*
+ * Apply the selected BCJ filter. Update *pos and s->pos to match the amount
+ * of data that got filtered.
+ *
+ * NOTE: This is implemented as a switch statement to avoid using function
+ * pointers, which could be problematic in the kernel boot code, which must
+ * avoid pointers to static data (at least on x86).
+ */
+static void bcj_apply(struct xz_dec_bcj *s,
+ uint8_t *buf, size_t *pos, size_t size)
+{
+ size_t filtered;
+
+ buf += *pos;
+ size -= *pos;
+
+ switch (s->type) {
+#ifdef XZ_DEC_X86
+ case BCJ_X86:
+ filtered = bcj_x86(s, buf, size);
+ break;
+#endif
+#ifdef XZ_DEC_POWERPC
+ case BCJ_POWERPC:
+ filtered = bcj_powerpc(s, buf, size);
+ break;
+#endif
+#ifdef XZ_DEC_IA64
+ case BCJ_IA64:
+ filtered = bcj_ia64(s, buf, size);
+ break;
+#endif
+#ifdef XZ_DEC_ARM
+ case BCJ_ARM:
+ filtered = bcj_arm(s, buf, size);
+ break;
+#endif
+#ifdef XZ_DEC_ARMTHUMB
+ case BCJ_ARMTHUMB:
+ filtered = bcj_armthumb(s, buf, size);
+ break;
+#endif
+#ifdef XZ_DEC_SPARC
+ case BCJ_SPARC:
+ filtered = bcj_sparc(s, buf, size);
+ break;
+#endif
+ default:
+ /* Never reached but silence compiler warnings. */
+ filtered = 0;
+ break;
+ }
+
+ *pos += filtered;
+ s->pos += filtered;
+}
+
+/*
+ * Flush pending filtered data from temp to the output buffer.
+ * Move the remaining mixture of possibly filtered and unfiltered
+ * data to the beginning of temp.
+ */
+static void bcj_flush(struct xz_dec_bcj *s, struct xz_buf *b)
+{
+ size_t copy_size;
+
+ copy_size = min_t(size_t, s->temp.filtered, b->out_size - b->out_pos);
+ memcpy(b->out + b->out_pos, s->temp.buf, copy_size);
+ b->out_pos += copy_size;
+
+ s->temp.filtered -= copy_size;
+ s->temp.size -= copy_size;
+ memmove(s->temp.buf, s->temp.buf + copy_size, s->temp.size);
+}
+
+/*
+ * The BCJ filter functions are primitive in sense that they process the
+ * data in chunks of 1-16 bytes. To hide this issue, this function does
+ * some buffering.
+ */
+XZ_EXTERN enum xz_ret xz_dec_bcj_run(struct xz_dec_bcj *s,
+ struct xz_dec_lzma2 *lzma2,
+ struct xz_buf *b)
+{
+ size_t out_start;
+
+ /*
+ * Flush pending already filtered data to the output buffer. Return
+ * immediatelly if we couldn't flush everything, or if the next
+ * filter in the chain had already returned XZ_STREAM_END.
+ */
+ if (s->temp.filtered > 0) {
+ bcj_flush(s, b);
+ if (s->temp.filtered > 0)
+ return XZ_OK;
+
+ if (s->ret == XZ_STREAM_END)
+ return XZ_STREAM_END;
+ }
+
+ /*
+ * If we have more output space than what is currently pending in
+ * temp, copy the unfiltered data from temp to the output buffer
+ * and try to fill the output buffer by decoding more data from the
+ * next filter in the chain. Apply the BCJ filter on the new data
+ * in the output buffer. If everything cannot be filtered, copy it
+ * to temp and rewind the output buffer position accordingly.
+ */
+ if (s->temp.size < b->out_size - b->out_pos) {
+ out_start = b->out_pos;
+ memcpy(b->out + b->out_pos, s->temp.buf, s->temp.size);
+ b->out_pos += s->temp.size;
+
+ s->ret = xz_dec_lzma2_run(lzma2, b);
+ if (s->ret != XZ_STREAM_END
+ && (s->ret != XZ_OK || s->single_call))
+ return s->ret;
+
+ bcj_apply(s, b->out, &out_start, b->out_pos);
+
+ /*
+ * As an exception, if the next filter returned XZ_STREAM_END,
+ * we can do that too, since the last few bytes that remain
+ * unfiltered are meant to remain unfiltered.
+ */
+ if (s->ret == XZ_STREAM_END)
+ return XZ_STREAM_END;
+
+ s->temp.size = b->out_pos - out_start;
+ b->out_pos -= s->temp.size;
+ memcpy(s->temp.buf, b->out + b->out_pos, s->temp.size);
+ }
+
+ /*
+ * If we have unfiltered data in temp, try to fill by decoding more
+ * data from the next filter. Apply the BCJ filter on temp. Then we
+ * hopefully can fill the actual output buffer by copying filtered
+ * data from temp. A mix of filtered and unfiltered data may be left
+ * in temp; it will be taken care on the next call to this function.
+ */
+ if (s->temp.size > 0) {
+ /* Make b->out{,_pos,_size} temporarily point to s->temp. */
+ s->out = b->out;
+ s->out_pos = b->out_pos;
+ s->out_size = b->out_size;
+ b->out = s->temp.buf;
+ b->out_pos = s->temp.size;
+ b->out_size = sizeof(s->temp.buf);
+
+ s->ret = xz_dec_lzma2_run(lzma2, b);
+
+ s->temp.size = b->out_pos;
+ b->out = s->out;
+ b->out_pos = s->out_pos;
+ b->out_size = s->out_size;
+
+ if (s->ret != XZ_OK && s->ret != XZ_STREAM_END)
+ return s->ret;
+
+ bcj_apply(s, s->temp.buf, &s->temp.filtered, s->temp.size);
+
+ /*
+ * If the next filter returned XZ_STREAM_END, we mark that
+ * everything is filtered, since the last unfiltered bytes
+ * of the stream are meant to be left as is.
+ */
+ if (s->ret == XZ_STREAM_END)
+ s->temp.filtered = s->temp.size;
+
+ bcj_flush(s, b);
+ if (s->temp.filtered > 0)
+ return XZ_OK;
+ }
+
+ return s->ret;
+}
+
+XZ_EXTERN struct xz_dec_bcj *xz_dec_bcj_create(bool single_call)
+{
+ struct xz_dec_bcj *s = kmalloc(sizeof(*s), GFP_KERNEL);
+ if (s != NULL)
+ s->single_call = single_call;
+
+ return s;
+}
+
+XZ_EXTERN enum xz_ret xz_dec_bcj_reset(struct xz_dec_bcj *s, uint8_t id)
+{
+ switch (id) {
+#ifdef XZ_DEC_X86
+ case BCJ_X86:
+#endif
+#ifdef XZ_DEC_POWERPC
+ case BCJ_POWERPC:
+#endif
+#ifdef XZ_DEC_IA64
+ case BCJ_IA64:
+#endif
+#ifdef XZ_DEC_ARM
+ case BCJ_ARM:
+#endif
+#ifdef XZ_DEC_ARMTHUMB
+ case BCJ_ARMTHUMB:
+#endif
+#ifdef XZ_DEC_SPARC
+ case BCJ_SPARC:
+#endif
+ break;
+
+ default:
+ /* Unsupported Filter ID */
+ return XZ_OPTIONS_ERROR;
+ }
+
+ s->type = id;
+ s->ret = XZ_OK;
+ s->pos = 0;
+ s->x86_prev_mask = 0;
+ s->temp.filtered = 0;
+ s->temp.size = 0;
+
+ return XZ_OK;
+}
+
+#endif
diff --git a/lib/xz/xz_dec_lzma2.c b/lib/xz/xz_dec_lzma2.c
new file mode 100644
index 0000000..ea5fa4f
--- /dev/null
+++ b/lib/xz/xz_dec_lzma2.c
@@ -0,0 +1,1171 @@
+/*
+ * LZMA2 decoder
+ *
+ * Authors: Lasse Collin <lasse.collin@tukaani.org>
+ * Igor Pavlov <http://7-zip.org/>
+ *
+ * This file has been put into the public domain.
+ * You can do whatever you want with this file.
+ */
+
+#include "xz_private.h"
+#include "xz_lzma2.h"
+
+/*
+ * Range decoder initialization eats the first five bytes of each LZMA chunk.
+ */
+#define RC_INIT_BYTES 5
+
+/*
+ * Minimum number of usable input buffer to safely decode one LZMA symbol.
+ * The worst case is that we decode 22 bits using probabilities and 26
+ * direct bits. This may decode at maximum of 20 bytes of input. However,
+ * lzma_main() does an extra normalization before returning, thus we
+ * need to put 21 here.
+ */
+#define LZMA_IN_REQUIRED 21
+
+/*
+ * Dictionary (history buffer)
+ *
+ * These are always true:
+ * start <= pos <= full <= end
+ * pos <= limit <= end
+ *
+ * In multi-call mode, also these are true:
+ * end == size
+ * size <= size_max
+ * allocated <= size
+ *
+ * Most of these variables are size_t to support single-call mode,
+ * in which the dictionary variables address the actual output
+ * buffer directly.
+ */
+struct dictionary {
+ /* Beginning of the history buffer */
+ uint8_t *buf;
+
+ /* Old position in buf (before decoding more data) */
+ size_t start;
+
+ /* Position in buf */
+ size_t pos;
+
+ /*
+ * How full dictionary is. This is used to detect corrupt input that
+ * would read beyond the beginning of the uncompressed stream.
+ */
+ size_t full;
+
+ /* Write limit; we don't write to buf[limit] or later bytes. */
+ size_t limit;
+
+ /*
+ * End of the dictionary buffer. In multi-call mode, this is
+ * the same as the dictionary size. In single-call mode, this
+ * indicates the size of the output buffer.
+ */
+ size_t end;
+
+ /*
+ * Size of the dictionary as specified in Block Header. This is used
+ * together with "full" to detect corrupt input that would make us
+ * read beyond the beginning of the uncompressed stream.
+ */
+ uint32_t size;
+
+ /*
+ * Maximum allowed dictionary size in multi-call mode.
+ * This is ignored in single-call mode.
+ */
+ uint32_t size_max;
+
+ /*
+ * Amount of memory currently allocated for the dictionary.
+ * This is used only with XZ_DYNALLOC. (With XZ_PREALLOC,
+ * size_max is always the same as the allocated size.)
+ */
+ uint32_t allocated;
+
+ /* Operation mode */
+ enum xz_mode mode;
+};
+
+/* Range decoder */
+struct rc_dec {
+ uint32_t range;
+ uint32_t code;
+
+ /*
+ * Number of initializing bytes remaining to be read
+ * by rc_read_init().
+ */
+ uint32_t init_bytes_left;
+
+ /*
+ * Buffer from which we read our input. It can be either
+ * temp.buf or the caller-provided input buffer.
+ */
+ const uint8_t *in;
+ size_t in_pos;
+ size_t in_limit;
+};
+
+/* Probabilities for a length decoder. */
+struct lzma_len_dec {
+ /* Probability of match length being at least 10 */
+ uint16_t choice;
+
+ /* Probability of match length being at least 18 */
+ uint16_t choice2;
+
+ /* Probabilities for match lengths 2-9 */
+ uint16_t low[POS_STATES_MAX][LEN_LOW_SYMBOLS];
+
+ /* Probabilities for match lengths 10-17 */
+ uint16_t mid[POS_STATES_MAX][LEN_MID_SYMBOLS];
+
+ /* Probabilities for match lengths 18-273 */
+ uint16_t high[LEN_HIGH_SYMBOLS];
+};
+
+struct lzma_dec {
+ /* Distances of latest four matches */
+ uint32_t rep0;
+ uint32_t rep1;
+ uint32_t rep2;
+ uint32_t rep3;
+
+ /* Types of the most recently seen LZMA symbols */
+ enum lzma_state state;
+
+ /*
+ * Length of a match. This is updated so that dict_repeat can
+ * be called again to finish repeating the whole match.
+ */
+ uint32_t len;
+
+ /*
+ * LZMA properties or related bit masks (number of literal
+ * context bits, a mask dervied from the number of literal
+ * position bits, and a mask dervied from the number
+ * position bits)
+ */
+ uint32_t lc;
+ uint32_t literal_pos_mask; /* (1 << lp) - 1 */
+ uint32_t pos_mask; /* (1 << pb) - 1 */
+
+ /* If 1, it's a match. Otherwise it's a single 8-bit literal. */
+ uint16_t is_match[STATES][POS_STATES_MAX];
+
+ /* If 1, it's a repeated match. The distance is one of rep0 .. rep3. */
+ uint16_t is_rep[STATES];
+
+ /*
+ * If 0, distance of a repeated match is rep0.
+ * Otherwise check is_rep1.
+ */
+ uint16_t is_rep0[STATES];
+
+ /*
+ * If 0, distance of a repeated match is rep1.
+ * Otherwise check is_rep2.
+ */
+ uint16_t is_rep1[STATES];
+
+ /* If 0, distance of a repeated match is rep2. Otherwise it is rep3. */
+ uint16_t is_rep2[STATES];
+
+ /*
+ * If 1, the repeated match has length of one byte. Otherwise
+ * the length is decoded from rep_len_decoder.
+ */
+ uint16_t is_rep0_long[STATES][POS_STATES_MAX];
+
+ /*
+ * Probability tree for the highest two bits of the match
+ * distance. There is a separate probability tree for match
+ * lengths of 2 (i.e. MATCH_LEN_MIN), 3, 4, and [5, 273].
+ */
+ uint16_t dist_slot[DIST_STATES][DIST_SLOTS];
+
+ /*
+ * Probility trees for additional bits for match distance
+ * when the distance is in the range [4, 127].
+ */
+ uint16_t dist_special[FULL_DISTANCES - DIST_MODEL_END];
+
+ /*
+ * Probability tree for the lowest four bits of a match
+ * distance that is equal to or greater than 128.
+ */
+ uint16_t dist_align[ALIGN_SIZE];
+
+ /* Length of a normal match */
+ struct lzma_len_dec match_len_dec;
+
+ /* Length of a repeated match */
+ struct lzma_len_dec rep_len_dec;
+
+ /* Probabilities of literals */
+ uint16_t literal[LITERAL_CODERS_MAX][LITERAL_CODER_SIZE];
+};
+
+struct lzma2_dec {
+ /* Position in xz_dec_lzma2_run(). */
+ enum lzma2_seq {
+ SEQ_CONTROL,
+ SEQ_UNCOMPRESSED_1,
+ SEQ_UNCOMPRESSED_2,
+ SEQ_COMPRESSED_0,
+ SEQ_COMPRESSED_1,
+ SEQ_PROPERTIES,
+ SEQ_LZMA_PREPARE,
+ SEQ_LZMA_RUN,
+ SEQ_COPY
+ } sequence;
+
+ /* Next position after decoding the compressed size of the chunk. */
+ enum lzma2_seq next_sequence;
+
+ /* Uncompressed size of LZMA chunk (2 MiB at maximum) */
+ uint32_t uncompressed;
+
+ /*
+ * Compressed size of LZMA chunk or compressed/uncompressed
+ * size of uncompressed chunk (64 KiB at maximum)
+ */
+ uint32_t compressed;
+
+ /*
+ * True if dictionary reset is needed. This is false before
+ * the first chunk (LZMA or uncompressed).
+ */
+ bool need_dict_reset;
+
+ /*
+ * True if new LZMA properties are needed. This is false
+ * before the first LZMA chunk.
+ */
+ bool need_props;
+};
+
+struct xz_dec_lzma2 {
+ /*
+ * The order below is important on x86 to reduce code size and
+ * it shouldn't hurt on other platforms. Everything up to and
+ * including lzma.pos_mask are in the first 128 bytes on x86-32,
+ * which allows using smaller instructions to access those
+ * variables. On x86-64, fewer variables fit into the first 128
+ * bytes, but this is still the best order without sacrificing
+ * the readability by splitting the structures.
+ */
+ struct rc_dec rc;
+ struct dictionary dict;
+ struct lzma2_dec lzma2;
+ struct lzma_dec lzma;
+
+ /*
+ * Temporary buffer which holds small number of input bytes between
+ * decoder calls. See lzma2_lzma() for details.
+ */
+ struct {
+ uint32_t size;
+ uint8_t buf[3 * LZMA_IN_REQUIRED];
+ } temp;
+};
+
+/**************
+ * Dictionary *
+ **************/
+
+/*
+ * Reset the dictionary state. When in single-call mode, set up the beginning
+ * of the dictionary to point to the actual output buffer.
+ */
+static void dict_reset(struct dictionary *dict, struct xz_buf *b)
+{
+ if (DEC_IS_SINGLE(dict->mode)) {
+ dict->buf = b->out + b->out_pos;
+ dict->end = b->out_size - b->out_pos;
+ }
+
+ dict->start = 0;
+ dict->pos = 0;
+ dict->limit = 0;
+ dict->full = 0;
+}
+
+/* Set dictionary write limit */
+static void dict_limit(struct dictionary *dict, size_t out_max)
+{
+ if (dict->end - dict->pos <= out_max)
+ dict->limit = dict->end;
+ else
+ dict->limit = dict->pos + out_max;
+}
+
+/* Return true if at least one byte can be written into the dictionary. */
+static inline bool dict_has_space(const struct dictionary *dict)
+{
+ return dict->pos < dict->limit;
+}
+
+/*
+ * Get a byte from the dictionary at the given distance. The distance is
+ * assumed to valid, or as a special case, zero when the dictionary is
+ * still empty. This special case is needed for single-call decoding to
+ * avoid writing a '\0' to the end of the destination buffer.
+ */
+static inline uint32_t dict_get(const struct dictionary *dict, uint32_t dist)
+{
+ size_t offset = dict->pos - dist - 1;
+
+ if (dist >= dict->pos)
+ offset += dict->end;
+
+ return dict->full > 0 ? dict->buf[offset] : 0;
+}
+
+/*
+ * Put one byte into the dictionary. It is assumed that there is space for it.
+ */
+static inline void dict_put(struct dictionary *dict, uint8_t byte)
+{
+ dict->buf[dict->pos++] = byte;
+
+ if (dict->full < dict->pos)
+ dict->full = dict->pos;
+}
+
+/*
+ * Repeat given number of bytes from the given distance. If the distance is
+ * invalid, false is returned. On success, true is returned and *len is
+ * updated to indicate how many bytes were left to be repeated.
+ */
+static bool dict_repeat(struct dictionary *dict, uint32_t *len, uint32_t dist)
+{
+ size_t back;
+ uint32_t left;
+
+ if (dist >= dict->full || dist >= dict->size)
+ return false;
+
+ left = min_t(size_t, dict->limit - dict->pos, *len);
+ *len -= left;
+
+ back = dict->pos - dist - 1;
+ if (dist >= dict->pos)
+ back += dict->end;
+
+ do {
+ dict->buf[dict->pos++] = dict->buf[back++];
+ if (back == dict->end)
+ back = 0;
+ } while (--left > 0);
+
+ if (dict->full < dict->pos)
+ dict->full = dict->pos;
+
+ return true;
+}
+
+/* Copy uncompressed data as is from input to dictionary and output buffers. */
+static void dict_uncompressed(struct dictionary *dict, struct xz_buf *b,
+ uint32_t *left)
+{
+ size_t copy_size;
+
+ while (*left > 0 && b->in_pos < b->in_size
+ && b->out_pos < b->out_size) {
+ copy_size = min(b->in_size - b->in_pos,
+ b->out_size - b->out_pos);
+ if (copy_size > dict->end - dict->pos)
+ copy_size = dict->end - dict->pos;
+ if (copy_size > *left)
+ copy_size = *left;
+
+ *left -= copy_size;
+
+ memcpy(dict->buf + dict->pos, b->in + b->in_pos, copy_size);
+ dict->pos += copy_size;
+
+ if (dict->full < dict->pos)
+ dict->full = dict->pos;
+
+ if (DEC_IS_MULTI(dict->mode)) {
+ if (dict->pos == dict->end)
+ dict->pos = 0;
+
+ memcpy(b->out + b->out_pos, b->in + b->in_pos,
+ copy_size);
+ }
+
+ dict->start = dict->pos;
+
+ b->out_pos += copy_size;
+ b->in_pos += copy_size;
+ }
+}
+
+/*
+ * Flush pending data from dictionary to b->out. It is assumed that there is
+ * enough space in b->out. This is guaranteed because caller uses dict_limit()
+ * before decoding data into the dictionary.
+ */
+static uint32_t dict_flush(struct dictionary *dict, struct xz_buf *b)
+{
+ size_t copy_size = dict->pos - dict->start;
+
+ if (DEC_IS_MULTI(dict->mode)) {
+ if (dict->pos == dict->end)
+ dict->pos = 0;
+
+ memcpy(b->out + b->out_pos, dict->buf + dict->start,
+ copy_size);
+ }
+
+ dict->start = dict->pos;
+ b->out_pos += copy_size;
+ return copy_size;
+}
+
+/*****************
+ * Range decoder *
+ *****************/
+
+/* Reset the range decoder. */
+static void rc_reset(struct rc_dec *rc)
+{
+ rc->range = (uint32_t)-1;
+ rc->code = 0;
+ rc->init_bytes_left = RC_INIT_BYTES;
+}
+
+/*
+ * Read the first five initial bytes into rc->code if they haven't been
+ * read already. (Yes, the first byte gets completely ignored.)
+ */
+static bool rc_read_init(struct rc_dec *rc, struct xz_buf *b)
+{
+ while (rc->init_bytes_left > 0) {
+ if (b->in_pos == b->in_size)
+ return false;
+
+ rc->code = (rc->code << 8) + b->in[b->in_pos++];
+ --rc->init_bytes_left;
+ }
+
+ return true;
+}
+
+/* Return true if there may not be enough input for the next decoding loop. */
+static inline bool rc_limit_exceeded(const struct rc_dec *rc)
+{
+ return rc->in_pos > rc->in_limit;
+}
+
+/*
+ * Return true if it is possible (from point of view of range decoder) that
+ * we have reached the end of the LZMA chunk.
+ */
+static inline bool rc_is_finished(const struct rc_dec *rc)
+{
+ return rc->code == 0;
+}
+
+/* Read the next input byte if needed. */
+static __always_inline void rc_normalize(struct rc_dec *rc)
+{
+ if (rc->range < RC_TOP_VALUE) {
+ rc->range <<= RC_SHIFT_BITS;
+ rc->code = (rc->code << RC_SHIFT_BITS) + rc->in[rc->in_pos++];
+ }
+}
+
+/*
+ * Decode one bit. In some versions, this function has been splitted in three
+ * functions so that the compiler is supposed to be able to more easily avoid
+ * an extra branch. In this particular version of the LZMA decoder, this
+ * doesn't seem to be a good idea (tested with GCC 3.3.6, 3.4.6, and 4.3.3
+ * on x86). Using a non-splitted version results in nicer looking code too.
+ *
+ * NOTE: This must return an int. Do not make it return a bool or the speed
+ * of the code generated by GCC 3.x decreases 10-15 %. (GCC 4.3 doesn't care,
+ * and it generates 10-20 % faster code than GCC 3.x from this file anyway.)
+ */
+static __always_inline int rc_bit(struct rc_dec *rc, uint16_t *prob)
+{
+ uint32_t bound;
+ int bit;
+
+ rc_normalize(rc);
+ bound = (rc->range >> RC_BIT_MODEL_TOTAL_BITS) * *prob;
+ if (rc->code < bound) {
+ rc->range = bound;
+ *prob += (RC_BIT_MODEL_TOTAL - *prob) >> RC_MOVE_BITS;
+ bit = 0;
+ } else {
+ rc->range -= bound;
+ rc->code -= bound;
+ *prob -= *prob >> RC_MOVE_BITS;
+ bit = 1;
+ }
+
+ return bit;
+}
+
+/* Decode a bittree starting from the most significant bit. */
+static __always_inline uint32_t rc_bittree(struct rc_dec *rc,
+ uint16_t *probs, uint32_t limit)
+{
+ uint32_t symbol = 1;
+
+ do {
+ if (rc_bit(rc, &probs[symbol]))
+ symbol = (symbol << 1) + 1;
+ else
+ symbol <<= 1;
+ } while (symbol < limit);
+
+ return symbol;
+}
+
+/* Decode a bittree starting from the least significant bit. */
+static __always_inline void rc_bittree_reverse(struct rc_dec *rc,
+ uint16_t *probs,
+ uint32_t *dest, uint32_t limit)
+{
+ uint32_t symbol = 1;
+ uint32_t i = 0;
+
+ do {
+ if (rc_bit(rc, &probs[symbol])) {
+ symbol = (symbol << 1) + 1;
+ *dest += 1 << i;
+ } else {
+ symbol <<= 1;
+ }
+ } while (++i < limit);
+}
+
+/* Decode direct bits (fixed fifty-fifty probability) */
+static inline void rc_direct(struct rc_dec *rc, uint32_t *dest, uint32_t limit)
+{
+ uint32_t mask;
+
+ do {
+ rc_normalize(rc);
+ rc->range >>= 1;
+ rc->code -= rc->range;
+ mask = (uint32_t)0 - (rc->code >> 31);
+ rc->code += rc->range & mask;
+ *dest = (*dest << 1) + (mask + 1);
+ } while (--limit > 0);
+}
+
+/********
+ * LZMA *
+ ********/
+
+/* Get pointer to literal coder probability array. */
+static uint16_t *lzma_literal_probs(struct xz_dec_lzma2 *s)
+{
+ uint32_t prev_byte = dict_get(&s->dict, 0);
+ uint32_t low = prev_byte >> (8 - s->lzma.lc);
+ uint32_t high = (s->dict.pos & s->lzma.literal_pos_mask) << s->lzma.lc;
+ return s->lzma.literal[low + high];
+}
+
+/* Decode a literal (one 8-bit byte) */
+static void lzma_literal(struct xz_dec_lzma2 *s)
+{
+ uint16_t *probs;
+ uint32_t symbol;
+ uint32_t match_byte;
+ uint32_t match_bit;
+ uint32_t offset;
+ uint32_t i;
+
+ probs = lzma_literal_probs(s);
+
+ if (lzma_state_is_literal(s->lzma.state)) {
+ symbol = rc_bittree(&s->rc, probs, 0x100);
+ } else {
+ symbol = 1;
+ match_byte = dict_get(&s->dict, s->lzma.rep0) << 1;
+ offset = 0x100;
+
+ do {
+ match_bit = match_byte & offset;
+ match_byte <<= 1;
+ i = offset + match_bit + symbol;
+
+ if (rc_bit(&s->rc, &probs[i])) {
+ symbol = (symbol << 1) + 1;
+ offset &= match_bit;
+ } else {
+ symbol <<= 1;
+ offset &= ~match_bit;
+ }
+ } while (symbol < 0x100);
+ }
+
+ dict_put(&s->dict, (uint8_t)symbol);
+ lzma_state_literal(&s->lzma.state);
+}
+
+/* Decode the length of the match into s->lzma.len. */
+static void lzma_len(struct xz_dec_lzma2 *s, struct lzma_len_dec *l,
+ uint32_t pos_state)
+{
+ uint16_t *probs;
+ uint32_t limit;
+
+ if (!rc_bit(&s->rc, &l->choice)) {
+ probs = l->low[pos_state];
+ limit = LEN_LOW_SYMBOLS;
+ s->lzma.len = MATCH_LEN_MIN;
+ } else {
+ if (!rc_bit(&s->rc, &l->choice2)) {
+ probs = l->mid[pos_state];
+ limit = LEN_MID_SYMBOLS;
+ s->lzma.len = MATCH_LEN_MIN + LEN_LOW_SYMBOLS;
+ } else {
+ probs = l->high;
+ limit = LEN_HIGH_SYMBOLS;
+ s->lzma.len = MATCH_LEN_MIN + LEN_LOW_SYMBOLS
+ + LEN_MID_SYMBOLS;
+ }
+ }
+
+ s->lzma.len += rc_bittree(&s->rc, probs, limit) - limit;
+}
+
+/* Decode a match. The distance will be stored in s->lzma.rep0. */
+static void lzma_match(struct xz_dec_lzma2 *s, uint32_t pos_state)
+{
+ uint16_t *probs;
+ uint32_t dist_slot;
+ uint32_t limit;
+
+ lzma_state_match(&s->lzma.state);
+
+ s->lzma.rep3 = s->lzma.rep2;
+ s->lzma.rep2 = s->lzma.rep1;
+ s->lzma.rep1 = s->lzma.rep0;
+
+ lzma_len(s, &s->lzma.match_len_dec, pos_state);
+
+ probs = s->lzma.dist_slot[lzma_get_dist_state(s->lzma.len)];
+ dist_slot = rc_bittree(&s->rc, probs, DIST_SLOTS) - DIST_SLOTS;
+
+ if (dist_slot < DIST_MODEL_START) {
+ s->lzma.rep0 = dist_slot;
+ } else {
+ limit = (dist_slot >> 1) - 1;
+ s->lzma.rep0 = 2 + (dist_slot & 1);
+
+ if (dist_slot < DIST_MODEL_END) {
+ s->lzma.rep0 <<= limit;
+ probs = s->lzma.dist_special + s->lzma.rep0
+ - dist_slot - 1;
+ rc_bittree_reverse(&s->rc, probs,
+ &s->lzma.rep0, limit);
+ } else {
+ rc_direct(&s->rc, &s->lzma.rep0, limit - ALIGN_BITS);
+ s->lzma.rep0 <<= ALIGN_BITS;
+ rc_bittree_reverse(&s->rc, s->lzma.dist_align,
+ &s->lzma.rep0, ALIGN_BITS);
+ }
+ }
+}
+
+/*
+ * Decode a repeated match. The distance is one of the four most recently
+ * seen matches. The distance will be stored in s->lzma.rep0.
+ */
+static void lzma_rep_match(struct xz_dec_lzma2 *s, uint32_t pos_state)
+{
+ uint32_t tmp;
+
+ if (!rc_bit(&s->rc, &s->lzma.is_rep0[s->lzma.state])) {
+ if (!rc_bit(&s->rc, &s->lzma.is_rep0_long[
+ s->lzma.state][pos_state])) {
+ lzma_state_short_rep(&s->lzma.state);
+ s->lzma.len = 1;
+ return;
+ }
+ } else {
+ if (!rc_bit(&s->rc, &s->lzma.is_rep1[s->lzma.state])) {
+ tmp = s->lzma.rep1;
+ } else {
+ if (!rc_bit(&s->rc, &s->lzma.is_rep2[s->lzma.state])) {
+ tmp = s->lzma.rep2;
+ } else {
+ tmp = s->lzma.rep3;
+ s->lzma.rep3 = s->lzma.rep2;
+ }
+
+ s->lzma.rep2 = s->lzma.rep1;
+ }
+
+ s->lzma.rep1 = s->lzma.rep0;
+ s->lzma.rep0 = tmp;
+ }
+
+ lzma_state_long_rep(&s->lzma.state);
+ lzma_len(s, &s->lzma.rep_len_dec, pos_state);
+}
+
+/* LZMA decoder core */
+static bool lzma_main(struct xz_dec_lzma2 *s)
+{
+ uint32_t pos_state;
+
+ /*
+ * If the dictionary was reached during the previous call, try to
+ * finish the possibly pending repeat in the dictionary.
+ */
+ if (dict_has_space(&s->dict) && s->lzma.len > 0)
+ dict_repeat(&s->dict, &s->lzma.len, s->lzma.rep0);
+
+ /*
+ * Decode more LZMA symbols. One iteration may consume up to
+ * LZMA_IN_REQUIRED - 1 bytes.
+ */
+ while (dict_has_space(&s->dict) && !rc_limit_exceeded(&s->rc)) {
+ pos_state = s->dict.pos & s->lzma.pos_mask;
+
+ if (!rc_bit(&s->rc, &s->lzma.is_match[
+ s->lzma.state][pos_state])) {
+ lzma_literal(s);
+ } else {
+ if (rc_bit(&s->rc, &s->lzma.is_rep[s->lzma.state]))
+ lzma_rep_match(s, pos_state);
+ else
+ lzma_match(s, pos_state);
+
+ if (!dict_repeat(&s->dict, &s->lzma.len, s->lzma.rep0))
+ return false;
+ }
+ }
+
+ /*
+ * Having the range decoder always normalized when we are outside
+ * this function makes it easier to correctly handle end of the chunk.
+ */
+ rc_normalize(&s->rc);
+
+ return true;
+}
+
+/*
+ * Reset the LZMA decoder and range decoder state. Dictionary is nore reset
+ * here, because LZMA state may be reset without resetting the dictionary.
+ */
+static void lzma_reset(struct xz_dec_lzma2 *s)
+{
+ uint16_t *probs;
+ size_t i;
+
+ s->lzma.state = STATE_LIT_LIT;
+ s->lzma.rep0 = 0;
+ s->lzma.rep1 = 0;
+ s->lzma.rep2 = 0;
+ s->lzma.rep3 = 0;
+
+ /*
+ * All probabilities are initialized to the same value. This hack
+ * makes the code smaller by avoiding a separate loop for each
+ * probability array.
+ *
+ * This could be optimized so that only that part of literal
+ * probabilities that are actually required. In the common case
+ * we would write 12 KiB less.
+ */
+ probs = s->lzma.is_match[0];
+ for (i = 0; i < PROBS_TOTAL; ++i)
+ probs[i] = RC_BIT_MODEL_TOTAL / 2;
+
+ rc_reset(&s->rc);
+}
+
+/*
+ * Decode and validate LZMA properties (lc/lp/pb) and calculate the bit masks
+ * from the decoded lp and pb values. On success, the LZMA decoder state is
+ * reset and true is returned.
+ */
+static bool lzma_props(struct xz_dec_lzma2 *s, uint8_t props)
+{
+ if (props > (4 * 5 + 4) * 9 + 8)
+ return false;
+
+ s->lzma.pos_mask = 0;
+ while (props >= 9 * 5) {
+ props -= 9 * 5;
+ ++s->lzma.pos_mask;
+ }
+
+ s->lzma.pos_mask = (1 << s->lzma.pos_mask) - 1;
+
+ s->lzma.literal_pos_mask = 0;
+ while (props >= 9) {
+ props -= 9;
+ ++s->lzma.literal_pos_mask;
+ }
+
+ s->lzma.lc = props;
+
+ if (s->lzma.lc + s->lzma.literal_pos_mask > 4)
+ return false;
+
+ s->lzma.literal_pos_mask = (1 << s->lzma.literal_pos_mask) - 1;
+
+ lzma_reset(s);
+
+ return true;
+}
+
+/*********
+ * LZMA2 *
+ *********/
+
+/*
+ * The LZMA decoder assumes that if the input limit (s->rc.in_limit) hasn't
+ * been exceeded, it is safe to read up to LZMA_IN_REQUIRED bytes. This
+ * wrapper function takes care of making the LZMA decoder's assumption safe.
+ *
+ * As long as there is plenty of input left to be decoded in the current LZMA
+ * chunk, we decode directly from the caller-supplied input buffer until
+ * there's LZMA_IN_REQUIRED bytes left. Those remaining bytes are copied into
+ * s->temp.buf, which (hopefully) gets filled on the next call to this
+ * function. We decode a few bytes from the temporary buffer so that we can
+ * continue decoding from the caller-supplied input buffer again.
+ */
+static bool lzma2_lzma(struct xz_dec_lzma2 *s, struct xz_buf *b)
+{
+ size_t in_avail;
+ uint32_t tmp;
+
+ in_avail = b->in_size - b->in_pos;
+ if (s->temp.size > 0 || s->lzma2.compressed == 0) {
+ tmp = 2 * LZMA_IN_REQUIRED - s->temp.size;
+ if (tmp > s->lzma2.compressed - s->temp.size)
+ tmp = s->lzma2.compressed - s->temp.size;
+ if (tmp > in_avail)
+ tmp = in_avail;
+
+ memcpy(s->temp.buf + s->temp.size, b->in + b->in_pos, tmp);
+
+ if (s->temp.size + tmp == s->lzma2.compressed) {
+ memzero(s->temp.buf + s->temp.size + tmp,
+ sizeof(s->temp.buf)
+ - s->temp.size - tmp);
+ s->rc.in_limit = s->temp.size + tmp;
+ } else if (s->temp.size + tmp < LZMA_IN_REQUIRED) {
+ s->temp.size += tmp;
+ b->in_pos += tmp;
+ return true;
+ } else {
+ s->rc.in_limit = s->temp.size + tmp - LZMA_IN_REQUIRED;
+ }
+
+ s->rc.in = s->temp.buf;
+ s->rc.in_pos = 0;
+
+ if (!lzma_main(s) || s->rc.in_pos > s->temp.size + tmp)
+ return false;
+
+ s->lzma2.compressed -= s->rc.in_pos;
+
+ if (s->rc.in_pos < s->temp.size) {
+ s->temp.size -= s->rc.in_pos;
+ memmove(s->temp.buf, s->temp.buf + s->rc.in_pos,
+ s->temp.size);
+ return true;
+ }
+
+ b->in_pos += s->rc.in_pos - s->temp.size;
+ s->temp.size = 0;
+ }
+
+ in_avail = b->in_size - b->in_pos;
+ if (in_avail >= LZMA_IN_REQUIRED) {
+ s->rc.in = b->in;
+ s->rc.in_pos = b->in_pos;
+
+ if (in_avail >= s->lzma2.compressed + LZMA_IN_REQUIRED)
+ s->rc.in_limit = b->in_pos + s->lzma2.compressed;
+ else
+ s->rc.in_limit = b->in_size - LZMA_IN_REQUIRED;
+
+ if (!lzma_main(s))
+ return false;
+
+ in_avail = s->rc.in_pos - b->in_pos;
+ if (in_avail > s->lzma2.compressed)
+ return false;
+
+ s->lzma2.compressed -= in_avail;
+ b->in_pos = s->rc.in_pos;
+ }
+
+ in_avail = b->in_size - b->in_pos;
+ if (in_avail < LZMA_IN_REQUIRED) {
+ if (in_avail > s->lzma2.compressed)
+ in_avail = s->lzma2.compressed;
+
+ memcpy(s->temp.buf, b->in + b->in_pos, in_avail);
+ s->temp.size = in_avail;
+ b->in_pos += in_avail;
+ }
+
+ return true;
+}
+
+/*
+ * Take care of the LZMA2 control layer, and forward the job of actual LZMA
+ * decoding or copying of uncompressed chunks to other functions.
+ */
+XZ_EXTERN enum xz_ret xz_dec_lzma2_run(struct xz_dec_lzma2 *s,
+ struct xz_buf *b)
+{
+ uint32_t tmp;
+
+ while (b->in_pos < b->in_size || s->lzma2.sequence == SEQ_LZMA_RUN) {
+ switch (s->lzma2.sequence) {
+ case SEQ_CONTROL:
+ /*
+ * LZMA2 control byte
+ *
+ * Exact values:
+ * 0x00 End marker
+ * 0x01 Dictionary reset followed by
+ * an uncompressed chunk
+ * 0x02 Uncompressed chunk (no dictionary reset)
+ *
+ * Highest three bits (s->control & 0xE0):
+ * 0xE0 Dictionary reset, new properties and state
+ * reset, followed by LZMA compressed chunk
+ * 0xC0 New properties and state reset, followed
+ * by LZMA compressed chunk (no dictionary
+ * reset)
+ * 0xA0 State reset using old properties,
+ * followed by LZMA compressed chunk (no
+ * dictionary reset)
+ * 0x80 LZMA chunk (no dictionary or state reset)
+ *
+ * For LZMA compressed chunks, the lowest five bits
+ * (s->control & 1F) are the highest bits of the
+ * uncompressed size (bits 16-20).
+ *
+ * A new LZMA2 stream must begin with a dictionary
+ * reset. The first LZMA chunk must set new
+ * properties and reset the LZMA state.
+ *
+ * Values that don't match anything described above
+ * are invalid and we return XZ_DATA_ERROR.
+ */
+ tmp = b->in[b->in_pos++];
+
+ if (tmp >= 0xE0 || tmp == 0x01) {
+ s->lzma2.need_props = true;
+ s->lzma2.need_dict_reset = false;
+ dict_reset(&s->dict, b);
+ } else if (s->lzma2.need_dict_reset) {
+ return XZ_DATA_ERROR;
+ }
+
+ if (tmp >= 0x80) {
+ s->lzma2.uncompressed = (tmp & 0x1F) << 16;
+ s->lzma2.sequence = SEQ_UNCOMPRESSED_1;
+
+ if (tmp >= 0xC0) {
+ /*
+ * When there are new properties,
+ * state reset is done at
+ * SEQ_PROPERTIES.
+ */
+ s->lzma2.need_props = false;
+ s->lzma2.next_sequence
+ = SEQ_PROPERTIES;
+
+ } else if (s->lzma2.need_props) {
+ return XZ_DATA_ERROR;
+
+ } else {
+ s->lzma2.next_sequence
+ = SEQ_LZMA_PREPARE;
+ if (tmp >= 0xA0)
+ lzma_reset(s);
+ }
+ } else {
+ if (tmp == 0x00)
+ return XZ_STREAM_END;
+
+ if (tmp > 0x02)
+ return XZ_DATA_ERROR;
+
+ s->lzma2.sequence = SEQ_COMPRESSED_0;
+ s->lzma2.next_sequence = SEQ_COPY;
+ }
+
+ break;
+
+ case SEQ_UNCOMPRESSED_1:
+ s->lzma2.uncompressed
+ += (uint32_t)b->in[b->in_pos++] << 8;
+ s->lzma2.sequence = SEQ_UNCOMPRESSED_2;
+ break;
+
+ case SEQ_UNCOMPRESSED_2:
+ s->lzma2.uncompressed
+ += (uint32_t)b->in[b->in_pos++] + 1;
+ s->lzma2.sequence = SEQ_COMPRESSED_0;
+ break;
+
+ case SEQ_COMPRESSED_0:
+ s->lzma2.compressed
+ = (uint32_t)b->in[b->in_pos++] << 8;
+ s->lzma2.sequence = SEQ_COMPRESSED_1;
+ break;
+
+ case SEQ_COMPRESSED_1:
+ s->lzma2.compressed
+ += (uint32_t)b->in[b->in_pos++] + 1;
+ s->lzma2.sequence = s->lzma2.next_sequence;
+ break;
+
+ case SEQ_PROPERTIES:
+ if (!lzma_props(s, b->in[b->in_pos++]))
+ return XZ_DATA_ERROR;
+
+ s->lzma2.sequence = SEQ_LZMA_PREPARE;
+
+ case SEQ_LZMA_PREPARE:
+ if (s->lzma2.compressed < RC_INIT_BYTES)
+ return XZ_DATA_ERROR;
+
+ if (!rc_read_init(&s->rc, b))
+ return XZ_OK;
+
+ s->lzma2.compressed -= RC_INIT_BYTES;
+ s->lzma2.sequence = SEQ_LZMA_RUN;
+
+ case SEQ_LZMA_RUN:
+ /*
+ * Set dictionary limit to indicate how much we want
+ * to be encoded at maximum. Decode new data into the
+ * dictionary. Flush the new data from dictionary to
+ * b->out. Check if we finished decoding this chunk.
+ * In case the dictionary got full but we didn't fill
+ * the output buffer yet, we may run this loop
+ * multiple times without changing s->lzma2.sequence.
+ */
+ dict_limit(&s->dict, min_t(size_t,
+ b->out_size - b->out_pos,
+ s->lzma2.uncompressed));
+ if (!lzma2_lzma(s, b))
+ return XZ_DATA_ERROR;
+
+ s->lzma2.uncompressed -= dict_flush(&s->dict, b);
+
+ if (s->lzma2.uncompressed == 0) {
+ if (s->lzma2.compressed > 0 || s->lzma.len > 0
+ || !rc_is_finished(&s->rc))
+ return XZ_DATA_ERROR;
+
+ rc_reset(&s->rc);
+ s->lzma2.sequence = SEQ_CONTROL;
+
+ } else if (b->out_pos == b->out_size
+ || (b->in_pos == b->in_size
+ && s->temp.size
+ < s->lzma2.compressed)) {
+ return XZ_OK;
+ }
+
+ break;
+
+ case SEQ_COPY:
+ dict_uncompressed(&s->dict, b, &s->lzma2.compressed);
+ if (s->lzma2.compressed > 0)
+ return XZ_OK;
+
+ s->lzma2.sequence = SEQ_CONTROL;
+ break;
+ }
+ }
+
+ return XZ_OK;
+}
+
+XZ_EXTERN struct xz_dec_lzma2 *xz_dec_lzma2_create(enum xz_mode mode,
+ uint32_t dict_max)
+{
+ struct xz_dec_lzma2 *s = kmalloc(sizeof(*s), GFP_KERNEL);
+ if (s == NULL)
+ return NULL;
+
+ s->dict.mode = mode;
+ s->dict.size_max = dict_max;
+
+ if (DEC_IS_PREALLOC(mode)) {
+ s->dict.buf = vmalloc(dict_max);
+ if (s->dict.buf == NULL) {
+ kfree(s);
+ return NULL;
+ }
+ } else if (DEC_IS_DYNALLOC(mode)) {
+ s->dict.buf = NULL;
+ s->dict.allocated = 0;
+ }
+
+ return s;
+}
+
+XZ_EXTERN enum xz_ret xz_dec_lzma2_reset(struct xz_dec_lzma2 *s, uint8_t props)
+{
+ /* This limits dictionary size to 3 GiB to keep parsing simpler. */
+ if (props > 39)
+ return XZ_OPTIONS_ERROR;
+
+ s->dict.size = 2 + (props & 1);
+ s->dict.size <<= (props >> 1) + 11;
+
+ if (DEC_IS_MULTI(s->dict.mode)) {
+ if (s->dict.size > s->dict.size_max)
+ return XZ_MEMLIMIT_ERROR;
+
+ s->dict.end = s->dict.size;
+
+ if (DEC_IS_DYNALLOC(s->dict.mode)) {
+ if (s->dict.allocated < s->dict.size) {
+ vfree(s->dict.buf);
+ s->dict.buf = vmalloc(s->dict.size);
+ if (s->dict.buf == NULL) {
+ s->dict.allocated = 0;
+ return XZ_MEM_ERROR;
+ }
+ }
+ }
+ }
+
+ s->lzma.len = 0;
+
+ s->lzma2.sequence = SEQ_CONTROL;
+ s->lzma2.need_dict_reset = true;
+
+ s->temp.size = 0;
+
+ return XZ_OK;
+}
+
+XZ_EXTERN void xz_dec_lzma2_end(struct xz_dec_lzma2 *s)
+{
+ if (DEC_IS_MULTI(s->dict.mode))
+ vfree(s->dict.buf);
+
+ kfree(s);
+}
diff --git a/lib/xz/xz_dec_stream.c b/lib/xz/xz_dec_stream.c
new file mode 100644
index 0000000..ac809b1
--- /dev/null
+++ b/lib/xz/xz_dec_stream.c
@@ -0,0 +1,821 @@
+/*
+ * .xz Stream decoder
+ *
+ * Author: Lasse Collin <lasse.collin@tukaani.org>
+ *
+ * This file has been put into the public domain.
+ * You can do whatever you want with this file.
+ */
+
+#include "xz_private.h"
+#include "xz_stream.h"
+
+/* Hash used to validate the Index field */
+struct xz_dec_hash {
+ vli_type unpadded;
+ vli_type uncompressed;
+ uint32_t crc32;
+};
+
+struct xz_dec {
+ /* Position in dec_main() */
+ enum {
+ SEQ_STREAM_HEADER,
+ SEQ_BLOCK_START,
+ SEQ_BLOCK_HEADER,
+ SEQ_BLOCK_UNCOMPRESS,
+ SEQ_BLOCK_PADDING,
+ SEQ_BLOCK_CHECK,
+ SEQ_INDEX,
+ SEQ_INDEX_PADDING,
+ SEQ_INDEX_CRC32,
+ SEQ_STREAM_FOOTER
+ } sequence;
+
+ /* Position in variable-length integers and Check fields */
+ uint32_t pos;
+
+ /* Variable-length integer decoded by dec_vli() */
+ vli_type vli;
+
+ /* Saved in_pos and out_pos */
+ size_t in_start;
+ size_t out_start;
+
+ /* CRC32 value in Block or Index */
+ uint32_t crc32;
+
+ /* Type of the integrity check calculated from uncompressed data */
+ enum xz_check check_type;
+
+ /* Operation mode */
+ enum xz_mode mode;
+
+ /*
+ * True if the next call to xz_dec_run() is allowed to return
+ * XZ_BUF_ERROR.
+ */
+ bool allow_buf_error;
+
+ /* Information stored in Block Header */
+ struct {
+ /*
+ * Value stored in the Compressed Size field, or
+ * VLI_UNKNOWN if Compressed Size is not present.
+ */
+ vli_type compressed;
+
+ /*
+ * Value stored in the Uncompressed Size field, or
+ * VLI_UNKNOWN if Uncompressed Size is not present.
+ */
+ vli_type uncompressed;
+
+ /* Size of the Block Header field */
+ uint32_t size;
+ } block_header;
+
+ /* Information collected when decoding Blocks */
+ struct {
+ /* Observed compressed size of the current Block */
+ vli_type compressed;
+
+ /* Observed uncompressed size of the current Block */
+ vli_type uncompressed;
+
+ /* Number of Blocks decoded so far */
+ vli_type count;
+
+ /*
+ * Hash calculated from the Block sizes. This is used to
+ * validate the Index field.
+ */
+ struct xz_dec_hash hash;
+ } block;
+
+ /* Variables needed when verifying the Index field */
+ struct {
+ /* Position in dec_index() */
+ enum {
+ SEQ_INDEX_COUNT,
+ SEQ_INDEX_UNPADDED,
+ SEQ_INDEX_UNCOMPRESSED
+ } sequence;
+
+ /* Size of the Index in bytes */
+ vli_type size;
+
+ /* Number of Records (matches block.count in valid files) */
+ vli_type count;
+
+ /*
+ * Hash calculated from the Records (matches block.hash in
+ * valid files).
+ */
+ struct xz_dec_hash hash;
+ } index;
+
+ /*
+ * Temporary buffer needed to hold Stream Header, Block Header,
+ * and Stream Footer. The Block Header is the biggest (1 KiB)
+ * so we reserve space according to that. buf[] has to be aligned
+ * to a multiple of four bytes; the size_t variables before it
+ * should guarantee this.
+ */
+ struct {
+ size_t pos;
+ size_t size;
+ uint8_t buf[1024];
+ } temp;
+
+ struct xz_dec_lzma2 *lzma2;
+
+#ifdef XZ_DEC_BCJ
+ struct xz_dec_bcj *bcj;
+ bool bcj_active;
+#endif
+};
+
+#ifdef XZ_DEC_ANY_CHECK
+/* Sizes of the Check field with different Check IDs */
+static const uint8_t check_sizes[16] = {
+ 0,
+ 4, 4, 4,
+ 8, 8, 8,
+ 16, 16, 16,
+ 32, 32, 32,
+ 64, 64, 64
+};
+#endif
+
+/*
+ * Fill s->temp by copying data starting from b->in[b->in_pos]. Caller
+ * must have set s->temp.pos to indicate how much data we are supposed
+ * to copy into s->temp.buf. Return true once s->temp.pos has reached
+ * s->temp.size.
+ */
+static bool fill_temp(struct xz_dec *s, struct xz_buf *b)
+{
+ size_t copy_size = min_t(size_t,
+ b->in_size - b->in_pos, s->temp.size - s->temp.pos);
+
+ memcpy(s->temp.buf + s->temp.pos, b->in + b->in_pos, copy_size);
+ b->in_pos += copy_size;
+ s->temp.pos += copy_size;
+
+ if (s->temp.pos == s->temp.size) {
+ s->temp.pos = 0;
+ return true;
+ }
+
+ return false;
+}
+
+/* Decode a variable-length integer (little-endian base-128 encoding) */
+static enum xz_ret dec_vli(struct xz_dec *s, const uint8_t *in,
+ size_t *in_pos, size_t in_size)
+{
+ uint8_t byte;
+
+ if (s->pos == 0)
+ s->vli = 0;
+
+ while (*in_pos < in_size) {
+ byte = in[*in_pos];
+ ++*in_pos;
+
+ s->vli |= (vli_type)(byte & 0x7F) << s->pos;
+
+ if ((byte & 0x80) == 0) {
+ /* Don't allow non-minimal encodings. */
+ if (byte == 0 && s->pos != 0)
+ return XZ_DATA_ERROR;
+
+ s->pos = 0;
+ return XZ_STREAM_END;
+ }
+
+ s->pos += 7;
+ if (s->pos == 7 * VLI_BYTES_MAX)
+ return XZ_DATA_ERROR;
+ }
+
+ return XZ_OK;
+}
+
+/*
+ * Decode the Compressed Data field from a Block. Update and validate
+ * the observed compressed and uncompressed sizes of the Block so that
+ * they don't exceed the values possibly stored in the Block Header
+ * (validation assumes that no integer overflow occurs, since vli_type
+ * is normally uint64_t). Update the CRC32 if presence of the CRC32
+ * field was indicated in Stream Header.
+ *
+ * Once the decoding is finished, validate that the observed sizes match
+ * the sizes possibly stored in the Block Header. Update the hash and
+ * Block count, which are later used to validate the Index field.
+ */
+static enum xz_ret dec_block(struct xz_dec *s, struct xz_buf *b)
+{
+ enum xz_ret ret;
+
+ s->in_start = b->in_pos;
+ s->out_start = b->out_pos;
+
+#ifdef XZ_DEC_BCJ
+ if (s->bcj_active)
+ ret = xz_dec_bcj_run(s->bcj, s->lzma2, b);
+ else
+#endif
+ ret = xz_dec_lzma2_run(s->lzma2, b);
+
+ s->block.compressed += b->in_pos - s->in_start;
+ s->block.uncompressed += b->out_pos - s->out_start;
+
+ /*
+ * There is no need to separately check for VLI_UNKNOWN, since
+ * the observed sizes are always smaller than VLI_UNKNOWN.
+ */
+ if (s->block.compressed > s->block_header.compressed
+ || s->block.uncompressed
+ > s->block_header.uncompressed)
+ return XZ_DATA_ERROR;
+
+ if (s->check_type == XZ_CHECK_CRC32)
+ s->crc32 = xz_crc32(b->out + s->out_start,
+ b->out_pos - s->out_start, s->crc32);
+
+ if (ret == XZ_STREAM_END) {
+ if (s->block_header.compressed != VLI_UNKNOWN
+ && s->block_header.compressed
+ != s->block.compressed)
+ return XZ_DATA_ERROR;
+
+ if (s->block_header.uncompressed != VLI_UNKNOWN
+ && s->block_header.uncompressed
+ != s->block.uncompressed)
+ return XZ_DATA_ERROR;
+
+ s->block.hash.unpadded += s->block_header.size
+ + s->block.compressed;
+
+#ifdef XZ_DEC_ANY_CHECK
+ s->block.hash.unpadded += check_sizes[s->check_type];
+#else
+ if (s->check_type == XZ_CHECK_CRC32)
+ s->block.hash.unpadded += 4;
+#endif
+
+ s->block.hash.uncompressed += s->block.uncompressed;
+ s->block.hash.crc32 = xz_crc32(
+ (const uint8_t *)&s->block.hash,
+ sizeof(s->block.hash), s->block.hash.crc32);
+
+ ++s->block.count;
+ }
+
+ return ret;
+}
+
+/* Update the Index size and the CRC32 value. */
+static void index_update(struct xz_dec *s, const struct xz_buf *b)
+{
+ size_t in_used = b->in_pos - s->in_start;
+ s->index.size += in_used;
+ s->crc32 = xz_crc32(b->in + s->in_start, in_used, s->crc32);
+}
+
+/*
+ * Decode the Number of Records, Unpadded Size, and Uncompressed Size
+ * fields from the Index field. That is, Index Padding and CRC32 are not
+ * decoded by this function.
+ *
+ * This can return XZ_OK (more input needed), XZ_STREAM_END (everything
+ * successfully decoded), or XZ_DATA_ERROR (input is corrupt).
+ */
+static enum xz_ret dec_index(struct xz_dec *s, struct xz_buf *b)
+{
+ enum xz_ret ret;
+
+ do {
+ ret = dec_vli(s, b->in, &b->in_pos, b->in_size);
+ if (ret != XZ_STREAM_END) {
+ index_update(s, b);
+ return ret;
+ }
+
+ switch (s->index.sequence) {
+ case SEQ_INDEX_COUNT:
+ s->index.count = s->vli;
+
+ /*
+ * Validate that the Number of Records field
+ * indicates the same number of Records as
+ * there were Blocks in the Stream.
+ */
+ if (s->index.count != s->block.count)
+ return XZ_DATA_ERROR;
+
+ s->index.sequence = SEQ_INDEX_UNPADDED;
+ break;
+
+ case SEQ_INDEX_UNPADDED:
+ s->index.hash.unpadded += s->vli;
+ s->index.sequence = SEQ_INDEX_UNCOMPRESSED;
+ break;
+
+ case SEQ_INDEX_UNCOMPRESSED:
+ s->index.hash.uncompressed += s->vli;
+ s->index.hash.crc32 = xz_crc32(
+ (const uint8_t *)&s->index.hash,
+ sizeof(s->index.hash),
+ s->index.hash.crc32);
+ --s->index.count;
+ s->index.sequence = SEQ_INDEX_UNPADDED;
+ break;
+ }
+ } while (s->index.count > 0);
+
+ return XZ_STREAM_END;
+}
+
+/*
+ * Validate that the next four input bytes match the value of s->crc32.
+ * s->pos must be zero when starting to validate the first byte.
+ */
+static enum xz_ret crc32_validate(struct xz_dec *s, struct xz_buf *b)
+{
+ do {
+ if (b->in_pos == b->in_size)
+ return XZ_OK;
+
+ if (((s->crc32 >> s->pos) & 0xFF) != b->in[b->in_pos++])
+ return XZ_DATA_ERROR;
+
+ s->pos += 8;
+
+ } while (s->pos < 32);
+
+ s->crc32 = 0;
+ s->pos = 0;
+
+ return XZ_STREAM_END;
+}
+
+#ifdef XZ_DEC_ANY_CHECK
+/*
+ * Skip over the Check field when the Check ID is not supported.
+ * Returns true once the whole Check field has been skipped over.
+ */
+static bool check_skip(struct xz_dec *s, struct xz_buf *b)
+{
+ while (s->pos < check_sizes[s->check_type]) {
+ if (b->in_pos == b->in_size)
+ return false;
+
+ ++b->in_pos;
+ ++s->pos;
+ }
+
+ s->pos = 0;
+
+ return true;
+}
+#endif
+
+/* Decode the Stream Header field (the first 12 bytes of the .xz Stream). */
+static enum xz_ret dec_stream_header(struct xz_dec *s)
+{
+ if (!memeq(s->temp.buf, HEADER_MAGIC, HEADER_MAGIC_SIZE))
+ return XZ_FORMAT_ERROR;
+
+ if (xz_crc32(s->temp.buf + HEADER_MAGIC_SIZE, 2, 0)
+ != get_le32(s->temp.buf + HEADER_MAGIC_SIZE + 2))
+ return XZ_DATA_ERROR;
+
+ if (s->temp.buf[HEADER_MAGIC_SIZE] != 0)
+ return XZ_OPTIONS_ERROR;
+
+ /*
+ * Of integrity checks, we support only none (Check ID = 0) and
+ * CRC32 (Check ID = 1). However, if XZ_DEC_ANY_CHECK is defined,
+ * we will accept other check types too, but then the check won't
+ * be verified and a warning (XZ_UNSUPPORTED_CHECK) will be given.
+ */
+ s->check_type = s->temp.buf[HEADER_MAGIC_SIZE + 1];
+
+#ifdef XZ_DEC_ANY_CHECK
+ if (s->check_type > XZ_CHECK_MAX)
+ return XZ_OPTIONS_ERROR;
+
+ if (s->check_type > XZ_CHECK_CRC32)
+ return XZ_UNSUPPORTED_CHECK;
+#else
+ if (s->check_type > XZ_CHECK_CRC32)
+ return XZ_OPTIONS_ERROR;
+#endif
+
+ return XZ_OK;
+}
+
+/* Decode the Stream Footer field (the last 12 bytes of the .xz Stream) */
+static enum xz_ret dec_stream_footer(struct xz_dec *s)
+{
+ if (!memeq(s->temp.buf + 10, FOOTER_MAGIC, FOOTER_MAGIC_SIZE))
+ return XZ_DATA_ERROR;
+
+ if (xz_crc32(s->temp.buf + 4, 6, 0) != get_le32(s->temp.buf))
+ return XZ_DATA_ERROR;
+
+ /*
+ * Validate Backward Size. Note that we never added the size of the
+ * Index CRC32 field to s->index.size, thus we use s->index.size / 4
+ * instead of s->index.size / 4 - 1.
+ */
+ if ((s->index.size >> 2) != get_le32(s->temp.buf + 4))
+ return XZ_DATA_ERROR;
+
+ if (s->temp.buf[8] != 0 || s->temp.buf[9] != s->check_type)
+ return XZ_DATA_ERROR;
+
+ /*
+ * Use XZ_STREAM_END instead of XZ_OK to be more convenient
+ * for the caller.
+ */
+ return XZ_STREAM_END;
+}
+
+/* Decode the Block Header and initialize the filter chain. */
+static enum xz_ret dec_block_header(struct xz_dec *s)
+{
+ enum xz_ret ret;
+
+ /*
+ * Validate the CRC32. We know that the temp buffer is at least
+ * eight bytes so this is safe.
+ */
+ s->temp.size -= 4;
+ if (xz_crc32(s->temp.buf, s->temp.size, 0)
+ != get_le32(s->temp.buf + s->temp.size))
+ return XZ_DATA_ERROR;
+
+ s->temp.pos = 2;
+
+ /*
+ * Catch unsupported Block Flags. We support only one or two filters
+ * in the chain, so we catch that with the same test.
+ */
+#ifdef XZ_DEC_BCJ
+ if (s->temp.buf[1] & 0x3E)
+#else
+ if (s->temp.buf[1] & 0x3F)
+#endif
+ return XZ_OPTIONS_ERROR;
+
+ /* Compressed Size */
+ if (s->temp.buf[1] & 0x40) {
+ if (dec_vli(s, s->temp.buf, &s->temp.pos, s->temp.size)
+ != XZ_STREAM_END)
+ return XZ_DATA_ERROR;
+
+ s->block_header.compressed = s->vli;
+ } else {
+ s->block_header.compressed = VLI_UNKNOWN;
+ }
+
+ /* Uncompressed Size */
+ if (s->temp.buf[1] & 0x80) {
+ if (dec_vli(s, s->temp.buf, &s->temp.pos, s->temp.size)
+ != XZ_STREAM_END)
+ return XZ_DATA_ERROR;
+
+ s->block_header.uncompressed = s->vli;
+ } else {
+ s->block_header.uncompressed = VLI_UNKNOWN;
+ }
+
+#ifdef XZ_DEC_BCJ
+ /* If there are two filters, the first one must be a BCJ filter. */
+ s->bcj_active = s->temp.buf[1] & 0x01;
+ if (s->bcj_active) {
+ if (s->temp.size - s->temp.pos < 2)
+ return XZ_OPTIONS_ERROR;
+
+ ret = xz_dec_bcj_reset(s->bcj, s->temp.buf[s->temp.pos++]);
+ if (ret != XZ_OK)
+ return ret;
+
+ /*
+ * We don't support custom start offset,
+ * so Size of Properties must be zero.
+ */
+ if (s->temp.buf[s->temp.pos++] != 0x00)
+ return XZ_OPTIONS_ERROR;
+ }
+#endif
+
+ /* Valid Filter Flags always take at least two bytes. */
+ if (s->temp.size - s->temp.pos < 2)
+ return XZ_DATA_ERROR;
+
+ /* Filter ID = LZMA2 */
+ if (s->temp.buf[s->temp.pos++] != 0x21)
+ return XZ_OPTIONS_ERROR;
+
+ /* Size of Properties = 1-byte Filter Properties */
+ if (s->temp.buf[s->temp.pos++] != 0x01)
+ return XZ_OPTIONS_ERROR;
+
+ /* Filter Properties contains LZMA2 dictionary size. */
+ if (s->temp.size - s->temp.pos < 1)
+ return XZ_DATA_ERROR;
+
+ ret = xz_dec_lzma2_reset(s->lzma2, s->temp.buf[s->temp.pos++]);
+ if (ret != XZ_OK)
+ return ret;
+
+ /* The rest must be Header Padding. */
+ while (s->temp.pos < s->temp.size)
+ if (s->temp.buf[s->temp.pos++] != 0x00)
+ return XZ_OPTIONS_ERROR;
+
+ s->temp.pos = 0;
+ s->block.compressed = 0;
+ s->block.uncompressed = 0;
+
+ return XZ_OK;
+}
+
+static enum xz_ret dec_main(struct xz_dec *s, struct xz_buf *b)
+{
+ enum xz_ret ret;
+
+ /*
+ * Store the start position for the case when we are in the middle
+ * of the Index field.
+ */
+ s->in_start = b->in_pos;
+
+ while (true) {
+ switch (s->sequence) {
+ case SEQ_STREAM_HEADER:
+ /*
+ * Stream Header is copied to s->temp, and then
+ * decoded from there. This way if the caller
+ * gives us only little input at a time, we can
+ * still keep the Stream Header decoding code
+ * simple. Similar approach is used in many places
+ * in this file.
+ */
+ if (!fill_temp(s, b))
+ return XZ_OK;
+
+ /*
+ * If dec_stream_header() returns
+ * XZ_UNSUPPORTED_CHECK, it is still possible
+ * to continue decoding if working in multi-call
+ * mode. Thus, update s->sequence before calling
+ * dec_stream_header().
+ */
+ s->sequence = SEQ_BLOCK_START;
+
+ ret = dec_stream_header(s);
+ if (ret != XZ_OK)
+ return ret;
+
+ case SEQ_BLOCK_START:
+ /* We need one byte of input to continue. */
+ if (b->in_pos == b->in_size)
+ return XZ_OK;
+
+ /* See if this is the beginning of the Index field. */
+ if (b->in[b->in_pos] == 0) {
+ s->in_start = b->in_pos++;
+ s->sequence = SEQ_INDEX;
+ break;
+ }
+
+ /*
+ * Calculate the size of the Block Header and
+ * prepare to decode it.
+ */
+ s->block_header.size
+ = ((uint32_t)b->in[b->in_pos] + 1) * 4;
+
+ s->temp.size = s->block_header.size;
+ s->temp.pos = 0;
+ s->sequence = SEQ_BLOCK_HEADER;
+
+ case SEQ_BLOCK_HEADER:
+ if (!fill_temp(s, b))
+ return XZ_OK;
+
+ ret = dec_block_header(s);
+ if (ret != XZ_OK)
+ return ret;
+
+ s->sequence = SEQ_BLOCK_UNCOMPRESS;
+
+ case SEQ_BLOCK_UNCOMPRESS:
+ ret = dec_block(s, b);
+ if (ret != XZ_STREAM_END)
+ return ret;
+
+ s->sequence = SEQ_BLOCK_PADDING;
+
+ case SEQ_BLOCK_PADDING:
+ /*
+ * Size of Compressed Data + Block Padding
+ * must be a multiple of four. We don't need
+ * s->block.compressed for anything else
+ * anymore, so we use it here to test the size
+ * of the Block Padding field.
+ */
+ while (s->block.compressed & 3) {
+ if (b->in_pos == b->in_size)
+ return XZ_OK;
+
+ if (b->in[b->in_pos++] != 0)
+ return XZ_DATA_ERROR;
+
+ ++s->block.compressed;
+ }
+
+ s->sequence = SEQ_BLOCK_CHECK;
+
+ case SEQ_BLOCK_CHECK:
+ if (s->check_type == XZ_CHECK_CRC32) {
+ ret = crc32_validate(s, b);
+ if (ret != XZ_STREAM_END)
+ return ret;
+ }
+#ifdef XZ_DEC_ANY_CHECK
+ else if (!check_skip(s, b)) {
+ return XZ_OK;
+ }
+#endif
+
+ s->sequence = SEQ_BLOCK_START;
+ break;
+
+ case SEQ_INDEX:
+ ret = dec_index(s, b);
+ if (ret != XZ_STREAM_END)
+ return ret;
+
+ s->sequence = SEQ_INDEX_PADDING;
+
+ case SEQ_INDEX_PADDING:
+ while ((s->index.size + (b->in_pos - s->in_start))
+ & 3) {
+ if (b->in_pos == b->in_size) {
+ index_update(s, b);
+ return XZ_OK;
+ }
+
+ if (b->in[b->in_pos++] != 0)
+ return XZ_DATA_ERROR;
+ }
+
+ /* Finish the CRC32 value and Index size. */
+ index_update(s, b);
+
+ /* Compare the hashes to validate the Index field. */
+ if (!memeq(&s->block.hash, &s->index.hash,
+ sizeof(s->block.hash)))
+ return XZ_DATA_ERROR;
+
+ s->sequence = SEQ_INDEX_CRC32;
+
+ case SEQ_INDEX_CRC32:
+ ret = crc32_validate(s, b);
+ if (ret != XZ_STREAM_END)
+ return ret;
+
+ s->temp.size = STREAM_HEADER_SIZE;
+ s->sequence = SEQ_STREAM_FOOTER;
+
+ case SEQ_STREAM_FOOTER:
+ if (!fill_temp(s, b))
+ return XZ_OK;
+
+ return dec_stream_footer(s);
+ }
+ }
+
+ /* Never reached */
+}
+
+/*
+ * xz_dec_run() is a wrapper for dec_main() to handle some special cases in
+ * multi-call and single-call decoding.
+ *
+ * In multi-call mode, we must return XZ_BUF_ERROR when it seems clear that we
+ * are not going to make any progress anymore. This is to prevent the caller
+ * from calling us infinitely when the input file is truncated or otherwise
+ * corrupt. Since zlib-style API allows that the caller fills the input buffer
+ * only when the decoder doesn't produce any new output, we have to be careful
+ * to avoid returning XZ_BUF_ERROR too easily: XZ_BUF_ERROR is returned only
+ * after the second consecutive call to xz_dec_run() that makes no progress.
+ *
+ * In single-call mode, if we couldn't decode everything and no error
+ * occurred, either the input is truncated or the output buffer is too small.
+ * Since we know that the last input byte never produces any output, we know
+ * that if all the input was consumed and decoding wasn't finished, the file
+ * must be corrupt. Otherwise the output buffer has to be too small or the
+ * file is corrupt in a way that decoding it produces too big output.
+ *
+ * If single-call decoding fails, we reset b->in_pos and b->out_pos back to
+ * their original values. This is because with some filter chains there won't
+ * be any valid uncompressed data in the output buffer unless the decoding
+ * actually succeeds (that's the price to pay of using the output buffer as
+ * the workspace).
+ */
+XZ_EXTERN enum xz_ret xz_dec_run(struct xz_dec *s, struct xz_buf *b)
+{
+ size_t in_start;
+ size_t out_start;
+ enum xz_ret ret;
+
+ if (DEC_IS_SINGLE(s->mode))
+ xz_dec_reset(s);
+
+ in_start = b->in_pos;
+ out_start = b->out_pos;
+ ret = dec_main(s, b);
+
+ if (DEC_IS_SINGLE(s->mode)) {
+ if (ret == XZ_OK)
+ ret = b->in_pos == b->in_size
+ ? XZ_DATA_ERROR : XZ_BUF_ERROR;
+
+ if (ret != XZ_STREAM_END) {
+ b->in_pos = in_start;
+ b->out_pos = out_start;
+ }
+
+ } else if (ret == XZ_OK && in_start == b->in_pos
+ && out_start == b->out_pos) {
+ if (s->allow_buf_error)
+ ret = XZ_BUF_ERROR;
+
+ s->allow_buf_error = true;
+ } else {
+ s->allow_buf_error = false;
+ }
+
+ return ret;
+}
+
+XZ_EXTERN struct xz_dec *xz_dec_init(enum xz_mode mode, uint32_t dict_max)
+{
+ struct xz_dec *s = kmalloc(sizeof(*s), GFP_KERNEL);
+ if (s == NULL)
+ return NULL;
+
+ s->mode = mode;
+
+#ifdef XZ_DEC_BCJ
+ s->bcj = xz_dec_bcj_create(DEC_IS_SINGLE(mode));
+ if (s->bcj == NULL)
+ goto error_bcj;
+#endif
+
+ s->lzma2 = xz_dec_lzma2_create(mode, dict_max);
+ if (s->lzma2 == NULL)
+ goto error_lzma2;
+
+ xz_dec_reset(s);
+ return s;
+
+error_lzma2:
+#ifdef XZ_DEC_BCJ
+ xz_dec_bcj_end(s->bcj);
+error_bcj:
+#endif
+ kfree(s);
+ return NULL;
+}
+
+XZ_EXTERN void xz_dec_reset(struct xz_dec *s)
+{
+ s->sequence = SEQ_STREAM_HEADER;
+ s->allow_buf_error = false;
+ s->pos = 0;
+ s->crc32 = 0;
+ memzero(&s->block, sizeof(s->block));
+ memzero(&s->index, sizeof(s->index));
+ s->temp.pos = 0;
+ s->temp.size = STREAM_HEADER_SIZE;
+}
+
+XZ_EXTERN void xz_dec_end(struct xz_dec *s)
+{
+ if (s != NULL) {
+ xz_dec_lzma2_end(s->lzma2);
+#ifdef XZ_DEC_BCJ
+ xz_dec_bcj_end(s->bcj);
+#endif
+ kfree(s);
+ }
+}
diff --git a/lib/xz/xz_dec_syms.c b/lib/xz/xz_dec_syms.c
new file mode 100644
index 0000000..32eb3c0
--- /dev/null
+++ b/lib/xz/xz_dec_syms.c
@@ -0,0 +1,26 @@
+/*
+ * XZ decoder module information
+ *
+ * Author: Lasse Collin <lasse.collin@tukaani.org>
+ *
+ * This file has been put into the public domain.
+ * You can do whatever you want with this file.
+ */
+
+#include <linux/module.h>
+#include <linux/xz.h>
+
+EXPORT_SYMBOL(xz_dec_init);
+EXPORT_SYMBOL(xz_dec_reset);
+EXPORT_SYMBOL(xz_dec_run);
+EXPORT_SYMBOL(xz_dec_end);
+
+MODULE_DESCRIPTION("XZ decompressor");
+MODULE_VERSION("1.0");
+MODULE_AUTHOR("Lasse Collin <lasse.collin@tukaani.org> and Igor Pavlov");
+
+/*
+ * This code is in the public domain, but in Linux it's simplest to just
+ * say it's GPL and consider the authors as the copyright holders.
+ */
+MODULE_LICENSE("GPL");
diff --git a/lib/xz/xz_dec_test.c b/lib/xz/xz_dec_test.c
new file mode 100644
index 0000000..da28a19
--- /dev/null
+++ b/lib/xz/xz_dec_test.c
@@ -0,0 +1,220 @@
+/*
+ * XZ decoder tester
+ *
+ * Author: Lasse Collin <lasse.collin@tukaani.org>
+ *
+ * This file has been put into the public domain.
+ * You can do whatever you want with this file.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/fs.h>
+#include <linux/uaccess.h>
+#include <linux/crc32.h>
+#include <linux/xz.h>
+
+/* Maximum supported dictionary size */
+#define DICT_MAX (1 << 20)
+
+/* Device name to pass to register_chrdev(). */
+#define DEVICE_NAME "xz_dec_test"
+
+/* Dynamically allocated device major number */
+static int device_major;
+
+/*
+ * We reuse the same decoder state, and thus can decode only one
+ * file at a time.
+ */
+static bool device_is_open;
+
+/* XZ decoder state */
+static struct xz_dec *state;
+
+/*
+ * Return value of xz_dec_run(). We need to avoid calling xz_dec_run() after
+ * it has returned XZ_STREAM_END, so we make this static.
+ */
+static enum xz_ret ret;
+
+/*
+ * Input and output buffers. The input buffer is used as a temporary safe
+ * place for the data coming from the userspace.
+ */
+static uint8_t buffer_in[1024];
+static uint8_t buffer_out[1024];
+
+/*
+ * Structure to pass the input and output buffers to the XZ decoder.
+ * A few of the fields are never modified so we initialize them here.
+ */
+static struct xz_buf buffers = {
+ .in = buffer_in,
+ .out = buffer_out,
+ .out_size = sizeof(buffer_out)
+};
+
+/*
+ * CRC32 of uncompressed data. This is used to give the user a simple way
+ * to check that the decoder produces correct output.
+ */
+static uint32_t crc;
+
+static int xz_dec_test_open(struct inode *i, struct file *f)
+{
+ if (device_is_open)
+ return -EBUSY;
+
+ device_is_open = true;
+
+ xz_dec_reset(state);
+ ret = XZ_OK;
+ crc = 0xFFFFFFFF;
+
+ buffers.in_pos = 0;
+ buffers.in_size = 0;
+ buffers.out_pos = 0;
+
+ printk(KERN_INFO DEVICE_NAME ": opened\n");
+ return 0;
+}
+
+static int xz_dec_test_release(struct inode *i, struct file *f)
+{
+ device_is_open = false;
+
+ if (ret == XZ_OK)
+ printk(KERN_INFO DEVICE_NAME ": input was truncated\n");
+
+ printk(KERN_INFO DEVICE_NAME ": closed\n");
+ return 0;
+}
+
+/*
+ * Decode the data given to us from the userspace. CRC32 of the uncompressed
+ * data is calculated and is printed at the end of successful decoding. The
+ * uncompressed data isn't stored anywhere for further use.
+ *
+ * The .xz file must have exactly one Stream and no Stream Padding. The data
+ * after the first Stream is considered to be garbage.
+ */
+static ssize_t xz_dec_test_write(struct file *file, const char __user *buf,
+ size_t size, loff_t *pos)
+{
+ size_t remaining;
+
+ if (ret != XZ_OK) {
+ if (size > 0)
+ printk(KERN_INFO DEVICE_NAME ": %zu bytes of "
+ "garbage at the end of the file\n",
+ size);
+
+ return -ENOSPC;
+ }
+
+ printk(KERN_INFO DEVICE_NAME ": decoding %zu bytes of input\n",
+ size);
+
+ remaining = size;
+ while ((remaining > 0 || buffers.out_pos == buffers.out_size)
+ && ret == XZ_OK) {
+ if (buffers.in_pos == buffers.in_size) {
+ buffers.in_pos = 0;
+ buffers.in_size = min(remaining, sizeof(buffer_in));
+ if (copy_from_user(buffer_in, buf, buffers.in_size))
+ return -EFAULT;
+
+ buf += buffers.in_size;
+ remaining -= buffers.in_size;
+ }
+
+ buffers.out_pos = 0;
+ ret = xz_dec_run(state, &buffers);
+ crc = crc32(crc, buffer_out, buffers.out_pos);
+ }
+
+ switch (ret) {
+ case XZ_OK:
+ printk(KERN_INFO DEVICE_NAME ": XZ_OK\n");
+ return size;
+
+ case XZ_STREAM_END:
+ printk(KERN_INFO DEVICE_NAME ": XZ_STREAM_END, "
+ "CRC32 = 0x%08X\n", ~crc);
+ return size - remaining - (buffers.in_size - buffers.in_pos);
+
+ case XZ_MEMLIMIT_ERROR:
+ printk(KERN_INFO DEVICE_NAME ": XZ_MEMLIMIT_ERROR\n");
+ break;
+
+ case XZ_FORMAT_ERROR:
+ printk(KERN_INFO DEVICE_NAME ": XZ_FORMAT_ERROR\n");
+ break;
+
+ case XZ_OPTIONS_ERROR:
+ printk(KERN_INFO DEVICE_NAME ": XZ_OPTIONS_ERROR\n");
+ break;
+
+ case XZ_DATA_ERROR:
+ printk(KERN_INFO DEVICE_NAME ": XZ_DATA_ERROR\n");
+ break;
+
+ case XZ_BUF_ERROR:
+ printk(KERN_INFO DEVICE_NAME ": XZ_BUF_ERROR\n");
+ break;
+
+ default:
+ printk(KERN_INFO DEVICE_NAME ": Bug detected!\n");
+ break;
+ }
+
+ return -EIO;
+}
+
+/* Allocate the XZ decoder state and register the character device. */
+static int __init xz_dec_test_init(void)
+{
+ static const struct file_operations fileops = {
+ .owner = THIS_MODULE,
+ .open = &xz_dec_test_open,
+ .release = &xz_dec_test_release,
+ .write = &xz_dec_test_write
+ };
+
+ state = xz_dec_init(XZ_PREALLOC, DICT_MAX);
+ if (state == NULL)
+ return -ENOMEM;
+
+ device_major = register_chrdev(0, DEVICE_NAME, &fileops);
+ if (device_major < 0) {
+ xz_dec_end(state);
+ return device_major;
+ }
+
+ printk(KERN_INFO DEVICE_NAME ": module loaded\n");
+ printk(KERN_INFO DEVICE_NAME ": Create a device node with "
+ "'mknod " DEVICE_NAME " c %d 0' and write .xz files "
+ "to it.\n", device_major);
+ return 0;
+}
+
+static void __exit xz_dec_test_exit(void)
+{
+ unregister_chrdev(device_major, DEVICE_NAME);
+ xz_dec_end(state);
+ printk(KERN_INFO DEVICE_NAME ": module unloaded\n");
+}
+
+module_init(xz_dec_test_init);
+module_exit(xz_dec_test_exit);
+
+MODULE_DESCRIPTION("XZ decompressor tester");
+MODULE_VERSION("1.0");
+MODULE_AUTHOR("Lasse Collin <lasse.collin@tukaani.org>");
+
+/*
+ * This code is in the public domain, but in Linux it's simplest to just
+ * say it's GPL and consider the authors as the copyright holders.
+ */
+MODULE_LICENSE("GPL");
diff --git a/lib/xz/xz_lzma2.h b/lib/xz/xz_lzma2.h
new file mode 100644
index 0000000..071d67b
--- /dev/null
+++ b/lib/xz/xz_lzma2.h
@@ -0,0 +1,204 @@
+/*
+ * LZMA2 definitions
+ *
+ * Authors: Lasse Collin <lasse.collin@tukaani.org>
+ * Igor Pavlov <http://7-zip.org/>
+ *
+ * This file has been put into the public domain.
+ * You can do whatever you want with this file.
+ */
+
+#ifndef XZ_LZMA2_H
+#define XZ_LZMA2_H
+
+/* Range coder constants */
+#define RC_SHIFT_BITS 8
+#define RC_TOP_BITS 24
+#define RC_TOP_VALUE (1 << RC_TOP_BITS)
+#define RC_BIT_MODEL_TOTAL_BITS 11
+#define RC_BIT_MODEL_TOTAL (1 << RC_BIT_MODEL_TOTAL_BITS)
+#define RC_MOVE_BITS 5
+
+/*
+ * Maximum number of position states. A position state is the lowest pb
+ * number of bits of the current uncompressed offset. In some places there
+ * are different sets of probabilities for different position states.
+ */
+#define POS_STATES_MAX (1 << 4)
+
+/*
+ * This enum is used to track which LZMA symbols have occurred most recently
+ * and in which order. This information is used to predict the next symbol.
+ *
+ * Symbols:
+ * - Literal: One 8-bit byte
+ * - Match: Repeat a chunk of data at some distance
+ * - Long repeat: Multi-byte match at a recently seen distance
+ * - Short repeat: One-byte repeat at a recently seen distance
+ *
+ * The symbol names are in from STATE_oldest_older_previous. REP means
+ * either short or long repeated match, and NONLIT means any non-literal.
+ */
+enum lzma_state {
+ STATE_LIT_LIT,
+ STATE_MATCH_LIT_LIT,
+ STATE_REP_LIT_LIT,
+ STATE_SHORTREP_LIT_LIT,
+ STATE_MATCH_LIT,
+ STATE_REP_LIT,
+ STATE_SHORTREP_LIT,
+ STATE_LIT_MATCH,
+ STATE_LIT_LONGREP,
+ STATE_LIT_SHORTREP,
+ STATE_NONLIT_MATCH,
+ STATE_NONLIT_REP
+};
+
+/* Total number of states */
+#define STATES 12
+
+/* The lowest 7 states indicate that the previous state was a literal. */
+#define LIT_STATES 7
+
+/* Indicate that the latest symbol was a literal. */
+static inline void lzma_state_literal(enum lzma_state *state)
+{
+ if (*state <= STATE_SHORTREP_LIT_LIT)
+ *state = STATE_LIT_LIT;
+ else if (*state <= STATE_LIT_SHORTREP)
+ *state -= 3;
+ else
+ *state -= 6;
+}
+
+/* Indicate that the latest symbol was a match. */
+static inline void lzma_state_match(enum lzma_state *state)
+{
+ *state = *state < LIT_STATES ? STATE_LIT_MATCH : STATE_NONLIT_MATCH;
+}
+
+/* Indicate that the latest state was a long repeated match. */
+static inline void lzma_state_long_rep(enum lzma_state *state)
+{
+ *state = *state < LIT_STATES ? STATE_LIT_LONGREP : STATE_NONLIT_REP;
+}
+
+/* Indicate that the latest symbol was a short match. */
+static inline void lzma_state_short_rep(enum lzma_state *state)
+{
+ *state = *state < LIT_STATES ? STATE_LIT_SHORTREP : STATE_NONLIT_REP;
+}
+
+/* Test if the previous symbol was a literal. */
+static inline bool lzma_state_is_literal(enum lzma_state state)
+{
+ return state < LIT_STATES;
+}
+
+/* Each literal coder is divided in three sections:
+ * - 0x001-0x0FF: Without match byte
+ * - 0x101-0x1FF: With match byte; match bit is 0
+ * - 0x201-0x2FF: With match byte; match bit is 1
+ *
+ * Match byte is used when the previous LZMA symbol was something else than
+ * a literal (that is, it was some kind of match).
+ */
+#define LITERAL_CODER_SIZE 0x300
+
+/* Maximum number of literal coders */
+#define LITERAL_CODERS_MAX (1 << 4)
+
+/* Minimum length of a match is two bytes. */
+#define MATCH_LEN_MIN 2
+
+/* Match length is encoded with 4, 5, or 10 bits.
+ *
+ * Length Bits
+ * 2-9 4 = Choice=0 + 3 bits
+ * 10-17 5 = Choice=1 + Choice2=0 + 3 bits
+ * 18-273 10 = Choice=1 + Choice2=1 + 8 bits
+ */
+#define LEN_LOW_BITS 3
+#define LEN_LOW_SYMBOLS (1 << LEN_LOW_BITS)
+#define LEN_MID_BITS 3
+#define LEN_MID_SYMBOLS (1 << LEN_MID_BITS)
+#define LEN_HIGH_BITS 8
+#define LEN_HIGH_SYMBOLS (1 << LEN_HIGH_BITS)
+#define LEN_SYMBOLS (LEN_LOW_SYMBOLS + LEN_MID_SYMBOLS + LEN_HIGH_SYMBOLS)
+
+/*
+ * Maximum length of a match is 273 which is a result of the encoding
+ * described above.
+ */
+#define MATCH_LEN_MAX (MATCH_LEN_MIN + LEN_SYMBOLS - 1)
+
+/*
+ * Different sets of probabilities are used for match distances that have
+ * very short match length: Lengths of 2, 3, and 4 bytes have a separate
+ * set of probabilities for each length. The matches with longer length
+ * use a shared set of probabilities.
+ */
+#define DIST_STATES 4
+
+/*
+ * Get the index of the appropriate probability array for decoding
+ * the distance slot.
+ */
+static inline uint32_t lzma_get_dist_state(uint32_t len)
+{
+ return len < DIST_STATES + MATCH_LEN_MIN
+ ? len - MATCH_LEN_MIN : DIST_STATES - 1;
+}
+
+/*
+ * The highest two bits of a 32-bit match distance are encoded using six bits.
+ * This six-bit value is called a distance slot. This way encoding a 32-bit
+ * value takes 6-36 bits, larger values taking more bits.
+ */
+#define DIST_SLOT_BITS 6
+#define DIST_SLOTS (1 << DIST_SLOT_BITS)
+
+/* Match distances up to 127 are fully encoded using probabilities. Since
+ * the highest two bits (distance slot) are always encoded using six bits,
+ * the distances 0-3 don't need any additional bits to encode, since the
+ * distance slot itself is the same as the actual distance. DIST_MODEL_START
+ * indicates the first distance slot where at least one additional bit is
+ * needed.
+ */
+#define DIST_MODEL_START 4
+
+/*
+ * Match distances greater than 127 are encoded in three pieces:
+ * - distance slot: the highest two bits
+ * - direct bits: 2-26 bits below the highest two bits
+ * - alignment bits: four lowest bits
+ *
+ * Direct bits don't use any probabilities.
+ *
+ * The distance slot value of 14 is for distances 128-191.
+ */
+#define DIST_MODEL_END 14
+
+/* Distance slots that indicate a distance <= 127. */
+#define FULL_DISTANCES_BITS (DIST_MODEL_END / 2)
+#define FULL_DISTANCES (1 << FULL_DISTANCES_BITS)
+
+/*
+ * For match distances greater than 127, only the highest two bits and the
+ * lowest four bits (alignment) is encoded using probabilities.
+ */
+#define ALIGN_BITS 4
+#define ALIGN_SIZE (1 << ALIGN_BITS)
+#define ALIGN_MASK (ALIGN_SIZE - 1)
+
+/* Total number of all probability variables */
+#define PROBS_TOTAL (1846 + LITERAL_CODERS_MAX * LITERAL_CODER_SIZE)
+
+/*
+ * LZMA remembers the four most recent match distances. Reusing these
+ * distances tends to take less space than re-encoding the actual
+ * distance value.
+ */
+#define REPS 4
+
+#endif
diff --git a/lib/xz/xz_private.h b/lib/xz/xz_private.h
new file mode 100644
index 0000000..a65633e
--- /dev/null
+++ b/lib/xz/xz_private.h
@@ -0,0 +1,156 @@
+/*
+ * Private includes and definitions
+ *
+ * Author: Lasse Collin <lasse.collin@tukaani.org>
+ *
+ * This file has been put into the public domain.
+ * You can do whatever you want with this file.
+ */
+
+#ifndef XZ_PRIVATE_H
+#define XZ_PRIVATE_H
+
+#ifdef __KERNEL__
+# include <linux/xz.h>
+# include <asm/byteorder.h>
+# include <asm/unaligned.h>
+ /* XZ_PREBOOT may be defined only via decompress_unxz.c. */
+# ifndef XZ_PREBOOT
+# include <linux/slab.h>
+# include <linux/vmalloc.h>
+# include <linux/string.h>
+# ifdef CONFIG_XZ_DEC_X86
+# define XZ_DEC_X86
+# endif
+# ifdef CONFIG_XZ_DEC_POWERPC
+# define XZ_DEC_POWERPC
+# endif
+# ifdef CONFIG_XZ_DEC_IA64
+# define XZ_DEC_IA64
+# endif
+# ifdef CONFIG_XZ_DEC_ARM
+# define XZ_DEC_ARM
+# endif
+# ifdef CONFIG_XZ_DEC_ARMTHUMB
+# define XZ_DEC_ARMTHUMB
+# endif
+# ifdef CONFIG_XZ_DEC_SPARC
+# define XZ_DEC_SPARC
+# endif
+# define memeq(a, b, size) (memcmp(a, b, size) == 0)
+# define memzero(buf, size) memset(buf, 0, size)
+# endif
+# define get_le32(p) le32_to_cpup((const uint32_t *)(p))
+#else
+ /*
+ * For userspace builds, use a separate header to define the required
+ * macros and functions. This makes it easier to adapt the code into
+ * different environments and avoids clutter in the Linux kernel tree.
+ */
+# include "xz_config.h"
+#endif
+
+/* If no specific decoding mode is requested, enable support for all modes. */
+#if !defined(XZ_DEC_SINGLE) && !defined(XZ_DEC_PREALLOC) \
+ && !defined(XZ_DEC_DYNALLOC)
+# define XZ_DEC_SINGLE
+# define XZ_DEC_PREALLOC
+# define XZ_DEC_DYNALLOC
+#endif
+
+/*
+ * The DEC_IS_foo(mode) macros are used in "if" statements. If only some
+ * of the supported modes are enabled, these macros will evaluate to true or
+ * false at compile time and thus allow the compiler to omit unneeded code.
+ */
+#ifdef XZ_DEC_SINGLE
+# define DEC_IS_SINGLE(mode) ((mode) == XZ_SINGLE)
+#else
+# define DEC_IS_SINGLE(mode) (false)
+#endif
+
+#ifdef XZ_DEC_PREALLOC
+# define DEC_IS_PREALLOC(mode) ((mode) == XZ_PREALLOC)
+#else
+# define DEC_IS_PREALLOC(mode) (false)
+#endif
+
+#ifdef XZ_DEC_DYNALLOC
+# define DEC_IS_DYNALLOC(mode) ((mode) == XZ_DYNALLOC)
+#else
+# define DEC_IS_DYNALLOC(mode) (false)
+#endif
+
+#if !defined(XZ_DEC_SINGLE)
+# define DEC_IS_MULTI(mode) (true)
+#elif defined(XZ_DEC_PREALLOC) || defined(XZ_DEC_DYNALLOC)
+# define DEC_IS_MULTI(mode) ((mode) != XZ_SINGLE)
+#else
+# define DEC_IS_MULTI(mode) (false)
+#endif
+
+/*
+ * If any of the BCJ filter decoders are wanted, define XZ_DEC_BCJ.
+ * XZ_DEC_BCJ is used to enable generic support for BCJ decoders.
+ */
+#ifndef XZ_DEC_BCJ
+# if defined(XZ_DEC_X86) || defined(XZ_DEC_POWERPC) \
+ || defined(XZ_DEC_IA64) || defined(XZ_DEC_ARM) \
+ || defined(XZ_DEC_ARM) || defined(XZ_DEC_ARMTHUMB) \
+ || defined(XZ_DEC_SPARC)
+# define XZ_DEC_BCJ
+# endif
+#endif
+
+/*
+ * Allocate memory for LZMA2 decoder. xz_dec_lzma2_reset() must be used
+ * before calling xz_dec_lzma2_run().
+ */
+XZ_EXTERN struct xz_dec_lzma2 *xz_dec_lzma2_create(enum xz_mode mode,
+ uint32_t dict_max);
+
+/*
+ * Decode the LZMA2 properties (one byte) and reset the decoder. Return
+ * XZ_OK on success, XZ_MEMLIMIT_ERROR if the preallocated dictionary is not
+ * big enough, and XZ_OPTIONS_ERROR if props indicates something that this
+ * decoder doesn't support.
+ */
+XZ_EXTERN enum xz_ret xz_dec_lzma2_reset(struct xz_dec_lzma2 *s,
+ uint8_t props);
+
+/* Decode raw LZMA2 stream from b->in to b->out. */
+XZ_EXTERN enum xz_ret xz_dec_lzma2_run(struct xz_dec_lzma2 *s,
+ struct xz_buf *b);
+
+/* Free the memory allocated for the LZMA2 decoder. */
+XZ_EXTERN void xz_dec_lzma2_end(struct xz_dec_lzma2 *s);
+
+#ifdef XZ_DEC_BCJ
+/*
+ * Allocate memory for BCJ decoders. xz_dec_bcj_reset() must be used before
+ * calling xz_dec_bcj_run().
+ */
+XZ_EXTERN struct xz_dec_bcj *xz_dec_bcj_create(bool single_call);
+
+/*
+ * Decode the Filter ID of a BCJ filter. This implementation doesn't
+ * support custom start offsets, so no decoding of Filter Properties
+ * is needed. Returns XZ_OK if the given Filter ID is supported.
+ * Otherwise XZ_OPTIONS_ERROR is returned.
+ */
+XZ_EXTERN enum xz_ret xz_dec_bcj_reset(struct xz_dec_bcj *s, uint8_t id);
+
+/*
+ * Decode raw BCJ + LZMA2 stream. This must be used only if there actually is
+ * a BCJ filter in the chain. If the chain has only LZMA2, xz_dec_lzma2_run()
+ * must be called directly.
+ */
+XZ_EXTERN enum xz_ret xz_dec_bcj_run(struct xz_dec_bcj *s,
+ struct xz_dec_lzma2 *lzma2,
+ struct xz_buf *b);
+
+/* Free the memory allocated for the BCJ filters. */
+#define xz_dec_bcj_end(s) kfree(s)
+#endif
+
+#endif
diff --git a/lib/xz/xz_stream.h b/lib/xz/xz_stream.h
new file mode 100644
index 0000000..66cb5a7
--- /dev/null
+++ b/lib/xz/xz_stream.h
@@ -0,0 +1,62 @@
+/*
+ * Definitions for handling the .xz file format
+ *
+ * Author: Lasse Collin <lasse.collin@tukaani.org>
+ *
+ * This file has been put into the public domain.
+ * You can do whatever you want with this file.
+ */
+
+#ifndef XZ_STREAM_H
+#define XZ_STREAM_H
+
+#if defined(__KERNEL__) && !XZ_INTERNAL_CRC32
+# include <linux/crc32.h>
+# undef crc32
+# define xz_crc32(buf, size, crc) \
+ (~crc32_le(~(uint32_t)(crc), buf, size))
+#endif
+
+/*
+ * See the .xz file format specification at
+ * http://tukaani.org/xz/xz-file-format.txt
+ * to understand the container format.
+ */
+
+#define STREAM_HEADER_SIZE 12
+
+#define HEADER_MAGIC "\3757zXZ"
+#define HEADER_MAGIC_SIZE 6
+
+#define FOOTER_MAGIC "YZ"
+#define FOOTER_MAGIC_SIZE 2
+
+/*
+ * Variable-length integer can hold a 63-bit unsigned integer or a special
+ * value indicating that the value is unknown.
+ *
+ * Experimental: vli_type can be defined to uint32_t to save a few bytes
+ * in code size (no effect on speed). Doing so limits the uncompressed and
+ * compressed size of the file to less than 256 MiB and may also weaken
+ * error detection slightly.
+ */
+typedef uint64_t vli_type;
+
+#define VLI_MAX ((vli_type)-1 / 2)
+#define VLI_UNKNOWN ((vli_type)-1)
+
+/* Maximum encoded size of a VLI */
+#define VLI_BYTES_MAX (sizeof(vli_type) * 8 / 7)
+
+/* Integrity Check types */
+enum xz_check {
+ XZ_CHECK_NONE = 0,
+ XZ_CHECK_CRC32 = 1,
+ XZ_CHECK_CRC64 = 4,
+ XZ_CHECK_SHA256 = 10
+};
+
+/* Maximum possible Check ID */
+#define XZ_CHECK_MAX 15
+
+#endif
diff --git a/mm/page-writeback.c b/mm/page-writeback.c
index b4edfe7..b5d8a1f 100644
--- a/mm/page-writeback.c
+++ b/mm/page-writeback.c
@@ -404,7 +404,7 @@
* - vm.dirty_background_ratio or vm.dirty_background_bytes
* - vm.dirty_ratio or vm.dirty_bytes
* The dirty limits will be lifted by 1/4 for PF_LESS_THROTTLE (ie. nfsd) and
- * runtime tasks.
+ * real-time tasks.
*/
void global_dirty_limits(unsigned long *pbackground, unsigned long *pdirty)
{
diff --git a/mm/page_alloc.c b/mm/page_alloc.c
index ff7e158..826ba69 100644
--- a/mm/page_alloc.c
+++ b/mm/page_alloc.c
@@ -4014,7 +4014,7 @@
zone->pageblock_flags = alloc_bootmem_node(pgdat, usemapsize);
}
#else
-static void inline setup_usemap(struct pglist_data *pgdat,
+static inline void setup_usemap(struct pglist_data *pgdat,
struct zone *zone, unsigned long zonesize) {}
#endif /* CONFIG_SPARSEMEM */
diff --git a/mm/percpu.c b/mm/percpu.c
index 3dd4984..3f93001 100644
--- a/mm/percpu.c
+++ b/mm/percpu.c
@@ -258,7 +258,7 @@
/*
* (Un)populated page region iterators. Iterate over (un)populated
- * page regions betwen @start and @end in @chunk. @rs and @re should
+ * page regions between @start and @end in @chunk. @rs and @re should
* be integer variables and will be set to start and end page index of
* the current region.
*/
diff --git a/mm/rmap.c b/mm/rmap.c
index 1a8bf76b..c95d2ba 100644
--- a/mm/rmap.c
+++ b/mm/rmap.c
@@ -94,7 +94,7 @@
* anonymous pages mapped into it with that anon_vma.
*
* The common case will be that we already have one, but if
- * if not we either need to find an adjacent mapping that we
+ * not we either need to find an adjacent mapping that we
* can re-use the anon_vma from (very common when the only
* reason for splitting a vma has been mprotect()), or we
* allocate a new one.
diff --git a/mm/sparse-vmemmap.c b/mm/sparse-vmemmap.c
index 29d6cbf..64b9840 100644
--- a/mm/sparse-vmemmap.c
+++ b/mm/sparse-vmemmap.c
@@ -9,7 +9,7 @@
*
* However, virtual mappings need a page table and TLBs. Many Linux
* architectures already map their physical space using 1-1 mappings
- * via TLBs. For those arches the virtual memmory map is essentially
+ * via TLBs. For those arches the virtual memory map is essentially
* for free if we use the same page size as the 1-1 mappings. In that
* case the overhead consists of a few additional pages that are
* allocated to create a view of memory for vmemmap.
diff --git a/net/Kconfig b/net/Kconfig
index ad0aafe..7284062 100644
--- a/net/Kconfig
+++ b/net/Kconfig
@@ -253,7 +253,9 @@
what was just said, you don't need it: say N.
Documentation on how to use TCP connection probing can be found
- at http://linux-net.osdl.org/index.php/TcpProbe
+ at:
+
+ http://www.linuxfoundation.org/collaborate/workgroups/networking/tcpprobe
To compile this code as a module, choose M here: the
module will be called tcp_probe.
diff --git a/net/ceph/ceph_hash.c b/net/ceph/ceph_hash.c
index 815ef88..0a1b53b 100644
--- a/net/ceph/ceph_hash.c
+++ b/net/ceph/ceph_hash.c
@@ -1,5 +1,6 @@
#include <linux/ceph/types.h>
+#include <linux/module.h>
/*
* Robert Jenkin's hash function.
@@ -104,6 +105,7 @@
return -1;
}
}
+EXPORT_SYMBOL(ceph_str_hash);
const char *ceph_str_hash_name(int type)
{
@@ -116,3 +118,4 @@
return "unknown";
}
}
+EXPORT_SYMBOL(ceph_str_hash_name);
diff --git a/net/ceph/messenger.c b/net/ceph/messenger.c
index b6ff4a1..dff633d 100644
--- a/net/ceph/messenger.c
+++ b/net/ceph/messenger.c
@@ -96,7 +96,7 @@
int ceph_msgr_init(void)
{
- ceph_msgr_wq = create_workqueue("ceph-msgr");
+ ceph_msgr_wq = alloc_workqueue("ceph-msgr", WQ_NON_REENTRANT, 0);
if (!ceph_msgr_wq) {
pr_err("msgr_init failed to create workqueue\n");
return -ENOMEM;
@@ -1920,20 +1920,6 @@
/*
* Atomically queue work on a connection. Bump @con reference to
* avoid races with connection teardown.
- *
- * There is some trickery going on with QUEUED and BUSY because we
- * only want a _single_ thread operating on each connection at any
- * point in time, but we want to use all available CPUs.
- *
- * The worker thread only proceeds if it can atomically set BUSY. It
- * clears QUEUED and does it's thing. When it thinks it's done, it
- * clears BUSY, then rechecks QUEUED.. if it's set again, it loops
- * (tries again to set BUSY).
- *
- * To queue work, we first set QUEUED, _then_ if BUSY isn't set, we
- * try to queue work. If that fails (work is already queued, or BUSY)
- * we give up (work also already being done or is queued) but leave QUEUED
- * set so that the worker thread will loop if necessary.
*/
static void queue_con(struct ceph_connection *con)
{
@@ -1948,11 +1934,7 @@
return;
}
- set_bit(QUEUED, &con->state);
- if (test_bit(BUSY, &con->state)) {
- dout("queue_con %p - already BUSY\n", con);
- con->ops->put(con);
- } else if (!queue_work(ceph_msgr_wq, &con->work.work)) {
+ if (!queue_delayed_work(ceph_msgr_wq, &con->work, 0)) {
dout("queue_con %p - already queued\n", con);
con->ops->put(con);
} else {
@@ -1967,15 +1949,6 @@
{
struct ceph_connection *con = container_of(work, struct ceph_connection,
work.work);
- int backoff = 0;
-
-more:
- if (test_and_set_bit(BUSY, &con->state) != 0) {
- dout("con_work %p BUSY already set\n", con);
- goto out;
- }
- dout("con_work %p start, clearing QUEUED\n", con);
- clear_bit(QUEUED, &con->state);
mutex_lock(&con->mutex);
@@ -1994,28 +1967,13 @@
try_read(con) < 0 ||
try_write(con) < 0) {
mutex_unlock(&con->mutex);
- backoff = 1;
ceph_fault(con); /* error/fault path */
goto done_unlocked;
}
done:
mutex_unlock(&con->mutex);
-
done_unlocked:
- clear_bit(BUSY, &con->state);
- dout("con->state=%lu\n", con->state);
- if (test_bit(QUEUED, &con->state)) {
- if (!backoff || test_bit(OPENING, &con->state)) {
- dout("con_work %p QUEUED reset, looping\n", con);
- goto more;
- }
- dout("con_work %p QUEUED reset, but just faulted\n", con);
- clear_bit(QUEUED, &con->state);
- }
- dout("con_work %p done\n", con);
-
-out:
con->ops->put(con);
}
diff --git a/net/ceph/osdmap.c b/net/ceph/osdmap.c
index d73f3f6..71603ac 100644
--- a/net/ceph/osdmap.c
+++ b/net/ceph/osdmap.c
@@ -605,8 +605,10 @@
goto bad;
}
err = __decode_pool(p, end, pi);
- if (err < 0)
+ if (err < 0) {
+ kfree(pi);
goto bad;
+ }
__insert_pg_pool(&map->pg_pools, pi);
}
diff --git a/net/core/dev.c b/net/core/dev.c
index a3ef808..06d0e7b 100644
--- a/net/core/dev.c
+++ b/net/core/dev.c
@@ -6218,7 +6218,7 @@
static void __net_exit default_device_exit_batch(struct list_head *net_list)
{
/* At exit all network devices most be removed from a network
- * namespace. Do this in the reverse order of registeration.
+ * namespace. Do this in the reverse order of registration.
* Do this across as many network namespaces as possible to
* improve batching efficiency.
*/
diff --git a/net/core/sock.c b/net/core/sock.c
index a658aeb..7dfed79 100644
--- a/net/core/sock.c
+++ b/net/core/sock.c
@@ -157,7 +157,7 @@
"sk_lock-27" , "sk_lock-28" , "sk_lock-AF_CAN" ,
"sk_lock-AF_TIPC" , "sk_lock-AF_BLUETOOTH", "sk_lock-IUCV" ,
"sk_lock-AF_RXRPC" , "sk_lock-AF_ISDN" , "sk_lock-AF_PHONET" ,
- "sk_lock-AF_IEEE802154", "sk_lock-AF_CAIF" ,
+ "sk_lock-AF_IEEE802154", "sk_lock-AF_CAIF" , "sk_lock-AF_ALG" ,
"sk_lock-AF_MAX"
};
static const char *const af_family_slock_key_strings[AF_MAX+1] = {
@@ -173,7 +173,7 @@
"slock-27" , "slock-28" , "slock-AF_CAN" ,
"slock-AF_TIPC" , "slock-AF_BLUETOOTH", "slock-AF_IUCV" ,
"slock-AF_RXRPC" , "slock-AF_ISDN" , "slock-AF_PHONET" ,
- "slock-AF_IEEE802154", "slock-AF_CAIF" ,
+ "slock-AF_IEEE802154", "slock-AF_CAIF" , "slock-AF_ALG" ,
"slock-AF_MAX"
};
static const char *const af_family_clock_key_strings[AF_MAX+1] = {
@@ -189,7 +189,7 @@
"clock-27" , "clock-28" , "clock-AF_CAN" ,
"clock-AF_TIPC" , "clock-AF_BLUETOOTH", "clock-AF_IUCV" ,
"clock-AF_RXRPC" , "clock-AF_ISDN" , "clock-AF_PHONET" ,
- "clock-AF_IEEE802154", "clock-AF_CAIF" ,
+ "clock-AF_IEEE802154", "clock-AF_CAIF" , "clock-AF_ALG" ,
"clock-AF_MAX"
};
diff --git a/net/dccp/Kconfig b/net/dccp/Kconfig
index ad6dffd..b75968a 100644
--- a/net/dccp/Kconfig
+++ b/net/dccp/Kconfig
@@ -49,7 +49,9 @@
what was just said, you don't need it: say N.
Documentation on how to use DCCP connection probing can be found
- at http://linux-net.osdl.org/index.php/DccpProbe
+ at:
+
+ http://www.linuxfoundation.org/collaborate/workgroups/networking/dccpprobe
To compile this code as a module, choose M here: the
module will be called dccp_probe.
diff --git a/net/decnet/dn_dev.c b/net/decnet/dn_dev.c
index 0ba1563..0dcaa90 100644
--- a/net/decnet/dn_dev.c
+++ b/net/decnet/dn_dev.c
@@ -1130,7 +1130,7 @@
/*
* This processes a device up event. We only start up
* the loopback device & ethernet devices with correct
- * MAC addreses automatically. Others must be started
+ * MAC addresses automatically. Others must be started
* specifically.
*
* FIXME: How should we configure the loopback address ? If we could dispense
diff --git a/net/ipv4/Kconfig b/net/ipv4/Kconfig
index 9e95d7f..a5a1050 100644
--- a/net/ipv4/Kconfig
+++ b/net/ipv4/Kconfig
@@ -432,7 +432,9 @@
---help---
Support for INET (TCP, DCCP, etc) socket monitoring interface used by
native Linux tools such as ss. ss is included in iproute2, currently
- downloadable at <http://linux-net.osdl.org/index.php/Iproute2>.
+ downloadable at:
+
+ http://www.linuxfoundation.org/collaborate/workgroups/networking/iproute2
If unsure, say Y.
diff --git a/net/ipv4/tcp_output.c b/net/ipv4/tcp_output.c
index dc7c096..406f320 100644
--- a/net/ipv4/tcp_output.c
+++ b/net/ipv4/tcp_output.c
@@ -1350,7 +1350,7 @@
return 0;
}
-/* Intialize TSO state of a skb.
+/* Initialize TSO state of a skb.
* This must be invoked the first time we consider transmitting
* SKB onto the wire.
*/
diff --git a/net/ipv6/af_inet6.c b/net/ipv6/af_inet6.c
index 059a3de..978e80e 100644
--- a/net/ipv6/af_inet6.c
+++ b/net/ipv6/af_inet6.c
@@ -300,7 +300,7 @@
goto out;
}
- /* Reproduce AF_INET checks to make the bindings consitant */
+ /* Reproduce AF_INET checks to make the bindings consistent */
v4addr = addr->sin6_addr.s6_addr32[3];
chk_addr_ret = inet_addr_type(net, v4addr);
if (!sysctl_ip_nonlocal_bind &&
diff --git a/net/sched/Kconfig b/net/sched/Kconfig
index a36270a..f04d4a4 100644
--- a/net/sched/Kconfig
+++ b/net/sched/Kconfig
@@ -24,7 +24,7 @@
To administer these schedulers, you'll need the user-level utilities
from the package iproute2+tc at <ftp://ftp.tux.org/pub/net/ip-routing/>.
That package also contains some documentation; for more, check out
- <http://linux-net.osdl.org/index.php/Iproute2>.
+ <http://www.linuxfoundation.org/collaborate/workgroups/networking/iproute2>.
This Quality of Service (QoS) support will enable you to use
Differentiated Services (diffserv) and Resource Reservation Protocol
diff --git a/scripts/Makefile.lib b/scripts/Makefile.lib
index 396da16..1c702ca 100644
--- a/scripts/Makefile.lib
+++ b/scripts/Makefile.lib
@@ -262,6 +262,34 @@
lzop -9 && $(call size_append, $(filter-out FORCE,$^))) > $@ || \
(rm -f $@ ; false)
+# XZ
+# ---------------------------------------------------------------------------
+# Use xzkern to compress the kernel image and xzmisc to compress other things.
+#
+# xzkern uses a big LZMA2 dictionary since it doesn't increase memory usage
+# of the kernel decompressor. A BCJ filter is used if it is available for
+# the target architecture. xzkern also appends uncompressed size of the data
+# using size_append. The .xz format has the size information available at
+# the end of the file too, but it's in more complex format and it's good to
+# avoid changing the part of the boot code that reads the uncompressed size.
+# Note that the bytes added by size_append will make the xz tool think that
+# the file is corrupt. This is expected.
+#
+# xzmisc doesn't use size_append, so it can be used to create normal .xz
+# files. xzmisc uses smaller LZMA2 dictionary than xzkern, because a very
+# big dictionary would increase the memory usage too much in the multi-call
+# decompression mode. A BCJ filter isn't used either.
+quiet_cmd_xzkern = XZKERN $@
+cmd_xzkern = (cat $(filter-out FORCE,$^) | \
+ sh $(srctree)/scripts/xz_wrap.sh && \
+ $(call size_append, $(filter-out FORCE,$^))) > $@ || \
+ (rm -f $@ ; false)
+
+quiet_cmd_xzmisc = XZMISC $@
+cmd_xzmisc = (cat $(filter-out FORCE,$^) | \
+ xz --check=crc32 --lzma2=dict=1MiB) > $@ || \
+ (rm -f $@ ; false)
+
# misc stuff
# ---------------------------------------------------------------------------
quote:="
diff --git a/scripts/checkpatch.pl b/scripts/checkpatch.pl
index e3c7fc0..4c0383d 100755
--- a/scripts/checkpatch.pl
+++ b/scripts/checkpatch.pl
@@ -859,7 +859,7 @@
$av_preprocessor = 0;
}
- } elsif ($cur =~ /^(\(\s*$Type\s*)\)/) {
+ } elsif ($cur =~ /^(\(\s*$Type\s*)\)/ && $av_pending eq '_') {
print "CAST($1)\n" if ($dbg_values > 1);
push(@av_paren_type, $type);
$type = 'C';
@@ -2743,6 +2743,11 @@
WARN("plain inline is preferred over $1\n" . $herecurr);
}
+# Check for __attribute__ packed, prefer __packed
+ if ($line =~ /\b__attribute__\s*\(\s*\(.*\bpacked\b/) {
+ WARN("__packed is preferred over __attribute__((packed))\n" . $herecurr);
+ }
+
# check for sizeof(&)
if ($line =~ /\bsizeof\s*\(\s*\&/) {
WARN("sizeof(& should be avoided\n" . $herecurr);
@@ -2785,10 +2790,15 @@
}
# check for pointless casting of kmalloc return
- if ($line =~ /\*\s*\)\s*k[czm]alloc\b/) {
+ if ($line =~ /\*\s*\)\s*[kv][czm]alloc(_node){0,1}\b/) {
WARN("unnecessary cast may hide bugs, see http://c-faq.com/malloc/mallocnocast.html\n" . $herecurr);
}
+# check for multiple semicolons
+ if ($line =~ /;\s*;\s*$/) {
+ WARN("Statements terminations use 1 semicolon\n" . $herecurr);
+ }
+
# check for gcc specific __FUNCTION__
if ($line =~ /__FUNCTION__/) {
WARN("__func__ should be used instead of gcc specific __FUNCTION__\n" . $herecurr);
@@ -2892,6 +2902,11 @@
ERROR("lockdep_no_validate class is reserved for device->mutex.\n" . $herecurr);
}
}
+
+ if ($line =~ /debugfs_create_file.*S_IWUGO/ ||
+ $line =~ /DEVICE_ATTR.*S_IWUGO/ ) {
+ WARN("Exporting world writable files is usually an error. Consider more restrictive permissions.\n" . $herecurr);
+ }
}
# If we have no input at all, then there is nothing to report on
diff --git a/scripts/gen_initramfs_list.sh b/scripts/gen_initramfs_list.sh
index 5958fff..55caecd 100644
--- a/scripts/gen_initramfs_list.sh
+++ b/scripts/gen_initramfs_list.sh
@@ -243,6 +243,8 @@
echo "$output_file" | grep -q "\.gz$" && compr="gzip -9 -f"
echo "$output_file" | grep -q "\.bz2$" && compr="bzip2 -9 -f"
echo "$output_file" | grep -q "\.lzma$" && compr="lzma -9 -f"
+ echo "$output_file" | grep -q "\.xz$" && \
+ compr="xz --check=crc32 --lzma2=dict=1MiB"
echo "$output_file" | grep -q "\.lzo$" && compr="lzop -9 -f"
echo "$output_file" | grep -q "\.cpio$" && compr="cat"
shift
diff --git a/scripts/get_maintainer.pl b/scripts/get_maintainer.pl
index d21ec3a..139e0ff 100755
--- a/scripts/get_maintainer.pl
+++ b/scripts/get_maintainer.pl
@@ -13,7 +13,7 @@
use strict;
my $P = $0;
-my $V = '0.26-beta6';
+my $V = '0.26';
use Getopt::Long qw(:config no_auto_abbrev);
@@ -40,7 +40,7 @@
my $output_multiline = 1;
my $output_separator = ", ";
my $output_roles = 0;
-my $output_rolestats = 0;
+my $output_rolestats = 1;
my $scm = 0;
my $web = 0;
my $subsystem = 0;
@@ -494,6 +494,40 @@
exit($exit);
+sub range_is_maintained {
+ my ($start, $end) = @_;
+
+ for (my $i = $start; $i < $end; $i++) {
+ my $line = $typevalue[$i];
+ if ($line =~ m/^(\C):\s*(.*)/) {
+ my $type = $1;
+ my $value = $2;
+ if ($type eq 'S') {
+ if ($value =~ /(maintain|support)/i) {
+ return 1;
+ }
+ }
+ }
+ }
+ return 0;
+}
+
+sub range_has_maintainer {
+ my ($start, $end) = @_;
+
+ for (my $i = $start; $i < $end; $i++) {
+ my $line = $typevalue[$i];
+ if ($line =~ m/^(\C):\s*(.*)/) {
+ my $type = $1;
+ my $value = $2;
+ if ($type eq 'M') {
+ return 1;
+ }
+ }
+ }
+ return 0;
+}
+
sub get_maintainers {
%email_hash_name = ();
%email_hash_address = ();
@@ -556,7 +590,9 @@
my $file_pd = ($file =~ tr@/@@);
$value_pd++ if (substr($value,-1,1) ne "/");
$value_pd = -1 if ($value =~ /^\.\*/);
- if ($value_pd >= $file_pd) {
+ if ($value_pd >= $file_pd &&
+ range_is_maintained($start, $end) &&
+ range_has_maintainer($start, $end)) {
$exact_pattern_match_hash{$file} = 1;
}
if ($pattern_depth == 0 ||
@@ -720,7 +756,8 @@
--help => show this help information
Default options:
- [--email --git --m --n --l --multiline --pattern-depth=0 --remove-duplicates]
+ [--email --nogit --git-fallback --m --n --l --multiline -pattern-depth=0
+ --remove-duplicates --rolestats]
Notes:
Using "-f directory" may give unexpected results:
diff --git a/scripts/mod/modpost.c b/scripts/mod/modpost.c
index 97d2259..e8fba95 100644
--- a/scripts/mod/modpost.c
+++ b/scripts/mod/modpost.c
@@ -1615,7 +1615,7 @@
* A module includes a number of sections that are discarded
* either when loaded or when used as built-in.
* For loaded modules all functions marked __init and all data
- * marked __initdata will be discarded when the module has been intialized.
+ * marked __initdata will be discarded when the module has been initialized.
* Likewise for modules used built-in the sections marked __exit
* are discarded because __exit marked function are supposed to be called
* only when a module is unloaded which never happens for built-in modules.
diff --git a/scripts/xz_wrap.sh b/scripts/xz_wrap.sh
new file mode 100644
index 0000000..17a5798
--- /dev/null
+++ b/scripts/xz_wrap.sh
@@ -0,0 +1,23 @@
+#!/bin/sh
+#
+# This is a wrapper for xz to compress the kernel image using appropriate
+# compression options depending on the architecture.
+#
+# Author: Lasse Collin <lasse.collin@tukaani.org>
+#
+# This file has been put into the public domain.
+# You can do whatever you want with this file.
+#
+
+BCJ=
+LZMA2OPTS=
+
+case $ARCH in
+ x86|x86_64) BCJ=--x86 ;;
+ powerpc) BCJ=--powerpc ;;
+ ia64) BCJ=--ia64; LZMA2OPTS=pb=4 ;;
+ arm) BCJ=--arm ;;
+ sparc) BCJ=--sparc ;;
+esac
+
+exec xz --check=crc32 $BCJ --lzma2=$LZMA2OPTS,dict=32MiB
diff --git a/security/apparmor/include/match.h b/security/apparmor/include/match.h
index 19ba16e..a4a8639 100644
--- a/security/apparmor/include/match.h
+++ b/security/apparmor/include/match.h
@@ -28,7 +28,7 @@
* The format used for transition tables is based on the GNU flex table
* file format (--tables-file option; see Table File Format in the flex
* info pages and the flex sources for documentation). The magic number
- * used in the header is 0x1B5E783D insted of 0xF13C57B1 though, because
+ * used in the header is 0x1B5E783D instead of 0xF13C57B1 though, because
* the YY_ID_CHK (check) and YY_ID_DEF (default) tables are used
* slightly differently (see the apparmor-parser package).
*/
diff --git a/sound/core/init.c b/sound/core/init.c
index 57b792e2..3e65da2 100644
--- a/sound/core/init.c
+++ b/sound/core/init.c
@@ -642,7 +642,7 @@
* external accesses. Thus, you should call this function at the end
* of the initialization of the card.
*
- * Returns zero otherwise a negative error code if the registrain failed.
+ * Returns zero otherwise a negative error code if the registration failed.
*/
int snd_card_register(struct snd_card *card)
{
diff --git a/sound/core/pcm_native.c b/sound/core/pcm_native.c
index e82c1f9..c0ebb51 100644
--- a/sound/core/pcm_native.c
+++ b/sound/core/pcm_native.c
@@ -984,7 +984,7 @@
if (push)
snd_pcm_update_hw_ptr(substream);
/* The jiffies check in snd_pcm_update_hw_ptr*() is done by
- * a delta betwen the current jiffies, this gives a large enough
+ * a delta between the current jiffies, this gives a large enough
* delta, effectively to skip the check once.
*/
substream->runtime->hw_ptr_jiffies = jiffies - HZ * 1000;
diff --git a/sound/isa/opl3sa2.c b/sound/isa/opl3sa2.c
index 265abcc..9b915e2 100644
--- a/sound/isa/opl3sa2.c
+++ b/sound/isa/opl3sa2.c
@@ -264,7 +264,7 @@
snd_printd("OPL3-SA [0x%lx] detect (1) = 0x%x (0x%x)\n", port, tmp, tmp1);
return -ENODEV;
}
- /* try if the MIC register is accesible */
+ /* try if the MIC register is accessible */
tmp = snd_opl3sa2_read(chip, OPL3SA2_MIC);
snd_opl3sa2_write(chip, OPL3SA2_MIC, 0x8a);
if (((tmp1 = snd_opl3sa2_read(chip, OPL3SA2_MIC)) & 0x9f) != 0x8a) {
diff --git a/sound/pci/ca0106/ca0106.h b/sound/pci/ca0106/ca0106.h
index f19c110..fc53b9b 100644
--- a/sound/pci/ca0106/ca0106.h
+++ b/sound/pci/ca0106/ca0106.h
@@ -188,7 +188,7 @@
#define PLAYBACK_LIST_PTR 0x02 /* Pointer to the current period being played */
/* PTR[5:0], Default: 0x0 */
#define PLAYBACK_UNKNOWN3 0x03 /* Not used ?? */
-#define PLAYBACK_DMA_ADDR 0x04 /* Playback DMA addresss */
+#define PLAYBACK_DMA_ADDR 0x04 /* Playback DMA address */
/* DMA[31:0], Default: 0x0 */
#define PLAYBACK_PERIOD_SIZE 0x05 /* Playback period size. win2000 uses 0x04000000 */
/* SIZE[31:16], Default: 0x0 */
diff --git a/sound/pci/ca0106/ca0106_main.c b/sound/pci/ca0106/ca0106_main.c
index d2d12c0..01b4938 100644
--- a/sound/pci/ca0106/ca0106_main.c
+++ b/sound/pci/ca0106/ca0106_main.c
@@ -1082,7 +1082,7 @@
struct snd_pcm_runtime *runtime = substream->runtime;
struct snd_ca0106_pcm *epcm = runtime->private_data;
snd_pcm_uframes_t ptr, ptr1, ptr2 = 0;
- int channel = channel=epcm->channel_id;
+ int channel = epcm->channel_id;
if (!epcm->running)
return 0;
diff --git a/sound/pci/emu10k1/emu10k1x.c b/sound/pci/emu10k1/emu10k1x.c
index df47f73..0c701e4 100644
--- a/sound/pci/emu10k1/emu10k1x.c
+++ b/sound/pci/emu10k1/emu10k1x.c
@@ -114,7 +114,7 @@
*/
#define PLAYBACK_LIST_SIZE 0x01 /* Size of list in bytes << 16. E.g. 8 periods -> 0x00380000 */
#define PLAYBACK_LIST_PTR 0x02 /* Pointer to the current period being played */
-#define PLAYBACK_DMA_ADDR 0x04 /* Playback DMA addresss */
+#define PLAYBACK_DMA_ADDR 0x04 /* Playback DMA address */
#define PLAYBACK_PERIOD_SIZE 0x05 /* Playback period size */
#define PLAYBACK_POINTER 0x06 /* Playback period pointer. Sample currently in DAC */
#define PLAYBACK_UNKNOWN1 0x07
diff --git a/sound/pci/emu10k1/p16v.h b/sound/pci/emu10k1/p16v.h
index 15321494..00f4817 100644
--- a/sound/pci/emu10k1/p16v.h
+++ b/sound/pci/emu10k1/p16v.h
@@ -96,7 +96,7 @@
#define PLAYBACK_LIST_SIZE 0x01 /* Size of list in bytes << 16. E.g. 8 periods -> 0x00380000 */
#define PLAYBACK_LIST_PTR 0x02 /* Pointer to the current period being played */
#define PLAYBACK_UNKNOWN3 0x03 /* Not used */
-#define PLAYBACK_DMA_ADDR 0x04 /* Playback DMA addresss */
+#define PLAYBACK_DMA_ADDR 0x04 /* Playback DMA address */
#define PLAYBACK_PERIOD_SIZE 0x05 /* Playback period size. win2000 uses 0x04000000 */
#define PLAYBACK_POINTER 0x06 /* Playback period pointer. Used with PLAYBACK_LIST_PTR to determine buffer position currently in DAC */
#define PLAYBACK_FIFO_END_ADDRESS 0x07 /* Playback FIFO end address */
diff --git a/sound/pci/es1968.c b/sound/pci/es1968.c
index 23a58f0..7c17f45 100644
--- a/sound/pci/es1968.c
+++ b/sound/pci/es1968.c
@@ -220,7 +220,7 @@
#define RINGB_EN_2CODEC 0x0020
#define RINGB_SING_BIT_DUAL 0x0040
-/* ****Port Adresses**** */
+/* ****Port Addresses**** */
/* Write & Read */
#define ESM_INDEX 0x02
diff --git a/sound/pci/rme9652/hdspm.c b/sound/pci/rme9652/hdspm.c
index 0c98ef9..f5eadfc 100644
--- a/sound/pci/rme9652/hdspm.c
+++ b/sound/pci/rme9652/hdspm.c
@@ -487,7 +487,7 @@
struct snd_kcontrol *playback_mixer_ctls[HDSPM_MAX_CHANNELS];
/* but input to much, so not used */
struct snd_kcontrol *input_mixer_ctls[HDSPM_MAX_CHANNELS];
- /* full mixer accessable over mixer ioctl or hwdep-device */
+ /* full mixer accessible over mixer ioctl or hwdep-device */
struct hdspm_mixer *mixer;
};
@@ -550,7 +550,7 @@
return bit2freq_tab[n];
}
-/* Write/read to/from HDSPM with Adresses in Bytes
+/* Write/read to/from HDSPM with Addresses in Bytes
not words but only 32Bit writes are allowed */
static inline void hdspm_write(struct hdspm * hdspm, unsigned int reg,
@@ -2908,7 +2908,7 @@
/* Channel playback mixer as default control
Note: the whole matrix would be 128*HDSPM_MIXER_CHANNELS Faders,
- thats too * big for any alsamixer they are accesible via special
+ thats too * big for any alsamixer they are accessible via special
IOCTL on hwdep and the mixer 2dimensional mixer control
*/
diff --git a/sound/ppc/snd_ps3.c b/sound/ppc/snd_ps3.c
index 581a670..edce8a2 100644
--- a/sound/ppc/snd_ps3.c
+++ b/sound/ppc/snd_ps3.c
@@ -51,7 +51,7 @@
static int snd_ps3_start_delay = CONFIG_SND_PS3_DEFAULT_START_DELAY;
module_param_named(start_delay, snd_ps3_start_delay, uint, 0644);
-MODULE_PARM_DESC(start_delay, "time to insert silent data in milisec");
+MODULE_PARM_DESC(start_delay, "time to insert silent data in ms");
static int index = SNDRV_DEFAULT_IDX1;
static char *id = SNDRV_DEFAULT_STR1;
diff --git a/sound/soc/codecs/max98088.c b/sound/soc/codecs/max98088.c
index 6447dbb..72f0dbc 100644
--- a/sound/soc/codecs/max98088.c
+++ b/sound/soc/codecs/max98088.c
@@ -1957,7 +1957,7 @@
return ret;
}
- /* initalize private data */
+ /* initialize private data */
max98088->sysclk = (unsigned)-1;
max98088->eq_textcnt = 0;
diff --git a/sound/soc/s3c24xx/smdk_spdif.c b/sound/soc/s3c24xx/smdk_spdif.c
index c8bd904..761e02b 100644
--- a/sound/soc/s3c24xx/smdk_spdif.c
+++ b/sound/soc/s3c24xx/smdk_spdif.c
@@ -61,7 +61,7 @@
goto out3;
}
- /* Set audio clock heirachy for S/PDIF */
+ /* Set audio clock hierarchy for S/PDIF */
clk_set_parent(mout_epll, fout_epll);
clk_set_parent(sclk_audio0, mout_epll);
clk_set_parent(sclk_spdif, sclk_audio0);
@@ -79,7 +79,7 @@
/* We should haved to set clock directly on this part because of clock
* scheme of Samsudng SoCs did not support to set rates from abstrct
- * clock of it's heirachy.
+ * clock of it's hierarchy.
*/
static int set_audio_clock_rate(unsigned long epll_rate,
unsigned long audio_rate)
@@ -197,7 +197,7 @@
if (ret)
goto err1;
- /* Set audio clock heirachy manually */
+ /* Set audio clock hierarchy manually */
ret = set_audio_clock_heirachy(smdk_snd_spdif_device);
if (ret)
goto err1;
diff --git a/tools/power/x86/turbostat/Makefile b/tools/power/x86/turbostat/Makefile
new file mode 100644
index 0000000..fd8e1f1
--- /dev/null
+++ b/tools/power/x86/turbostat/Makefile
@@ -0,0 +1,8 @@
+turbostat : turbostat.c
+
+clean :
+ rm -f turbostat
+
+install :
+ install turbostat /usr/bin/turbostat
+ install turbostat.8 /usr/share/man/man8
diff --git a/tools/power/x86/turbostat/turbostat.8 b/tools/power/x86/turbostat/turbostat.8
new file mode 100644
index 0000000..ff75125
--- /dev/null
+++ b/tools/power/x86/turbostat/turbostat.8
@@ -0,0 +1,172 @@
+.TH TURBOSTAT 8
+.SH NAME
+turbostat \- Report processor frequency and idle statistics
+.SH SYNOPSIS
+.ft B
+.B turbostat
+.RB [ "\-v" ]
+.RB [ "\-M MSR#" ]
+.RB command
+.br
+.B turbostat
+.RB [ "\-v" ]
+.RB [ "\-M MSR#" ]
+.RB [ "\-i interval_sec" ]
+.SH DESCRIPTION
+\fBturbostat \fP reports processor topology, frequency
+and idle power state statistics on modern X86 processors.
+Either \fBcommand\fP is forked and statistics are printed
+upon its completion, or statistics are printed periodically.
+
+\fBturbostat \fP
+requires that the processor
+supports an "invariant" TSC, plus the APERF and MPERF MSRs.
+\fBturbostat \fP will report idle cpu power state residency
+on processors that additionally support C-state residency counters.
+
+.SS Options
+The \fB-v\fP option increases verbosity.
+.PP
+The \fB-M MSR#\fP option dumps the specified MSR,
+in addition to the usual frequency and idle statistics.
+.PP
+The \fB-i interval_sec\fP option prints statistics every \fiinterval_sec\fP seconds.
+The default is 5 seconds.
+.PP
+The \fBcommand\fP parameter forks \fBcommand\fP and upon its exit,
+displays the statistics gathered since it was forked.
+.PP
+.SH FIELD DESCRIPTIONS
+.nf
+\fBpkg\fP processor package number.
+\fBcore\fP processor core number.
+\fBCPU\fP Linux CPU (logical processor) number.
+\fB%c0\fP percent of the interval that the CPU retired instructions.
+\fBGHz\fP average clock rate while the CPU was in c0 state.
+\fBTSC\fP average GHz that the TSC ran during the entire interval.
+\fB%c1, %c3, %c6\fP show the percentage residency in hardware core idle states.
+\fB%pc3, %pc6\fP percentage residency in hardware package idle states.
+.fi
+.PP
+.SH EXAMPLE
+Without any parameters, turbostat prints out counters ever 5 seconds.
+(override interval with "-i sec" option, or specify a command
+for turbostat to fork).
+
+The first row of statistics reflect the average for the entire system.
+Subsequent rows show per-CPU statistics.
+
+.nf
+[root@x980]# ./turbostat
+core CPU %c0 GHz TSC %c1 %c3 %c6 %pc3 %pc6
+ 0.04 1.62 3.38 0.11 0.00 99.85 0.00 95.07
+ 0 0 0.04 1.62 3.38 0.06 0.00 99.90 0.00 95.07
+ 0 6 0.02 1.62 3.38 0.08 0.00 99.90 0.00 95.07
+ 1 2 0.10 1.62 3.38 0.29 0.00 99.61 0.00 95.07
+ 1 8 0.11 1.62 3.38 0.28 0.00 99.61 0.00 95.07
+ 2 4 0.01 1.62 3.38 0.01 0.00 99.98 0.00 95.07
+ 2 10 0.01 1.61 3.38 0.02 0.00 99.98 0.00 95.07
+ 8 1 0.07 1.62 3.38 0.15 0.00 99.78 0.00 95.07
+ 8 7 0.03 1.62 3.38 0.19 0.00 99.78 0.00 95.07
+ 9 3 0.01 1.62 3.38 0.02 0.00 99.98 0.00 95.07
+ 9 9 0.01 1.62 3.38 0.02 0.00 99.98 0.00 95.07
+ 10 5 0.01 1.62 3.38 0.13 0.00 99.86 0.00 95.07
+ 10 11 0.08 1.62 3.38 0.05 0.00 99.86 0.00 95.07
+.fi
+.SH VERBOSE EXAMPLE
+The "-v" option adds verbosity to the output:
+
+.nf
+GenuineIntel 11 CPUID levels; family:model:stepping 0x6:2c:2 (6:44:2)
+12 * 133 = 1600 MHz max efficiency
+25 * 133 = 3333 MHz TSC frequency
+26 * 133 = 3467 MHz max turbo 4 active cores
+26 * 133 = 3467 MHz max turbo 3 active cores
+27 * 133 = 3600 MHz max turbo 2 active cores
+27 * 133 = 3600 MHz max turbo 1 active cores
+
+.fi
+The \fBmax efficiency\fP frequency, a.k.a. Low Frequency Mode, is the frequency
+available at the minimum package voltage. The \fBTSC frequency\fP is the nominal
+maximum frequency of the processor if turbo-mode were not available. This frequency
+should be sustainable on all CPUs indefinitely, given nominal power and cooling.
+The remaining rows show what maximum turbo frequency is possible
+depending on the number of idle cores. Note that this information is
+not available on all processors.
+.SH FORK EXAMPLE
+If turbostat is invoked with a command, it will fork that command
+and output the statistics gathered when the command exits.
+eg. Here a cycle soaker is run on 1 CPU (see %c0) for a few seconds
+until ^C while the other CPUs are mostly idle:
+
+.nf
+[root@x980 lenb]# ./turbostat cat /dev/zero > /dev/null
+
+^Ccore CPU %c0 GHz TSC %c1 %c3 %c6 %pc3 %pc6
+ 8.49 3.63 3.38 16.23 0.66 74.63 0.00 0.00
+ 0 0 1.22 3.62 3.38 32.18 0.00 66.60 0.00 0.00
+ 0 6 0.40 3.61 3.38 33.00 0.00 66.60 0.00 0.00
+ 1 2 0.11 3.14 3.38 0.19 3.95 95.75 0.00 0.00
+ 1 8 0.05 2.88 3.38 0.25 3.95 95.75 0.00 0.00
+ 2 4 0.00 3.13 3.38 0.02 0.00 99.98 0.00 0.00
+ 2 10 0.00 3.09 3.38 0.02 0.00 99.98 0.00 0.00
+ 8 1 0.04 3.50 3.38 14.43 0.00 85.54 0.00 0.00
+ 8 7 0.03 2.98 3.38 14.43 0.00 85.54 0.00 0.00
+ 9 3 0.00 3.16 3.38 100.00 0.00 0.00 0.00 0.00
+ 9 9 99.93 3.63 3.38 0.06 0.00 0.00 0.00 0.00
+ 10 5 0.01 2.82 3.38 0.08 0.00 99.91 0.00 0.00
+ 10 11 0.02 3.36 3.38 0.06 0.00 99.91 0.00 0.00
+6.950866 sec
+
+.fi
+Above the cycle soaker drives cpu9 up 3.6 Ghz turbo limit
+while the other processors are generally in various states of idle.
+
+Note that cpu3 is an HT sibling sharing core9
+with cpu9, and thus it is unable to get to an idle state
+deeper than c1 while cpu9 is busy.
+
+Note that turbostat reports average GHz of 3.61, while
+the arithmetic average of the GHz column above is 3.24.
+This is a weighted average, where the weight is %c0. ie. it is the total number of
+un-halted cycles elapsed per time divided by the number of CPUs.
+.SH NOTES
+
+.B "turbostat "
+must be run as root.
+
+.B "turbostat "
+reads hardware counters, but doesn't write them.
+So it will not interfere with the OS or other programs, including
+multiple invocations of itself.
+
+\fBturbostat \fP
+may work poorly on Linux-2.6.20 through 2.6.29,
+as \fBacpi-cpufreq \fPperiodically cleared the APERF and MPERF
+in those kernels.
+
+The APERF, MPERF MSRs are defined to count non-halted cycles.
+Although it is not guaranteed by the architecture, turbostat assumes
+that they count at TSC rate, which is true on all processors tested to date.
+
+.SH REFERENCES
+"Intel® Turbo Boost Technology
+in Intel® Core™ Microarchitecture (Nehalem) Based Processors"
+http://download.intel.com/design/processor/applnots/320354.pdf
+
+"Intel® 64 and IA-32 Architectures Software Developer's Manual
+Volume 3B: System Programming Guide"
+http://www.intel.com/products/processor/manuals/
+
+.SH FILES
+.ta
+.nf
+/dev/cpu/*/msr
+.fi
+
+.SH "SEE ALSO"
+msr(4), vmstat(8)
+.PP
+.SH AUTHORS
+.nf
+Written by Len Brown <len.brown@intel.com>
diff --git a/tools/power/x86/turbostat/turbostat.c b/tools/power/x86/turbostat/turbostat.c
new file mode 100644
index 0000000..4c6983d
--- /dev/null
+++ b/tools/power/x86/turbostat/turbostat.c
@@ -0,0 +1,1048 @@
+/*
+ * turbostat -- show CPU frequency and C-state residency
+ * on modern Intel turbo-capable processors.
+ *
+ * Copyright (c) 2010, Intel Corporation.
+ * Len Brown <len.brown@intel.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope 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.,
+ * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include <stdio.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <sys/stat.h>
+#include <sys/resource.h>
+#include <fcntl.h>
+#include <signal.h>
+#include <sys/time.h>
+#include <stdlib.h>
+#include <dirent.h>
+#include <string.h>
+#include <ctype.h>
+
+#define MSR_TSC 0x10
+#define MSR_NEHALEM_PLATFORM_INFO 0xCE
+#define MSR_NEHALEM_TURBO_RATIO_LIMIT 0x1AD
+#define MSR_APERF 0xE8
+#define MSR_MPERF 0xE7
+#define MSR_PKG_C2_RESIDENCY 0x60D /* SNB only */
+#define MSR_PKG_C3_RESIDENCY 0x3F8
+#define MSR_PKG_C6_RESIDENCY 0x3F9
+#define MSR_PKG_C7_RESIDENCY 0x3FA /* SNB only */
+#define MSR_CORE_C3_RESIDENCY 0x3FC
+#define MSR_CORE_C6_RESIDENCY 0x3FD
+#define MSR_CORE_C7_RESIDENCY 0x3FE /* SNB only */
+
+char *proc_stat = "/proc/stat";
+unsigned int interval_sec = 5; /* set with -i interval_sec */
+unsigned int verbose; /* set with -v */
+unsigned int skip_c0;
+unsigned int skip_c1;
+unsigned int do_nhm_cstates;
+unsigned int do_snb_cstates;
+unsigned int has_aperf;
+unsigned int units = 1000000000; /* Ghz etc */
+unsigned int genuine_intel;
+unsigned int has_invariant_tsc;
+unsigned int do_nehalem_platform_info;
+unsigned int do_nehalem_turbo_ratio_limit;
+unsigned int extra_msr_offset;
+double bclk;
+unsigned int show_pkg;
+unsigned int show_core;
+unsigned int show_cpu;
+
+int aperf_mperf_unstable;
+int backwards_count;
+char *progname;
+int need_reinitialize;
+
+int num_cpus;
+
+typedef struct per_cpu_counters {
+ unsigned long long tsc; /* per thread */
+ unsigned long long aperf; /* per thread */
+ unsigned long long mperf; /* per thread */
+ unsigned long long c1; /* per thread (calculated) */
+ unsigned long long c3; /* per core */
+ unsigned long long c6; /* per core */
+ unsigned long long c7; /* per core */
+ unsigned long long pc2; /* per package */
+ unsigned long long pc3; /* per package */
+ unsigned long long pc6; /* per package */
+ unsigned long long pc7; /* per package */
+ unsigned long long extra_msr; /* per thread */
+ int pkg;
+ int core;
+ int cpu;
+ struct per_cpu_counters *next;
+} PCC;
+
+PCC *pcc_even;
+PCC *pcc_odd;
+PCC *pcc_delta;
+PCC *pcc_average;
+struct timeval tv_even;
+struct timeval tv_odd;
+struct timeval tv_delta;
+
+unsigned long long get_msr(int cpu, off_t offset)
+{
+ ssize_t retval;
+ unsigned long long msr;
+ char pathname[32];
+ int fd;
+
+ sprintf(pathname, "/dev/cpu/%d/msr", cpu);
+ fd = open(pathname, O_RDONLY);
+ if (fd < 0) {
+ perror(pathname);
+ need_reinitialize = 1;
+ return 0;
+ }
+
+ retval = pread(fd, &msr, sizeof msr, offset);
+ if (retval != sizeof msr) {
+ fprintf(stderr, "cpu%d pread(..., 0x%zx) = %jd\n",
+ cpu, offset, retval);
+ exit(-2);
+ }
+
+ close(fd);
+ return msr;
+}
+
+void print_header()
+{
+ if (show_pkg)
+ fprintf(stderr, "pkg ");
+ if (show_core)
+ fprintf(stderr, "core");
+ if (show_cpu)
+ fprintf(stderr, " CPU");
+ if (do_nhm_cstates)
+ fprintf(stderr, " %%c0 ");
+ if (has_aperf)
+ fprintf(stderr, " GHz");
+ fprintf(stderr, " TSC");
+ if (do_nhm_cstates)
+ fprintf(stderr, " %%c1 ");
+ if (do_nhm_cstates)
+ fprintf(stderr, " %%c3 ");
+ if (do_nhm_cstates)
+ fprintf(stderr, " %%c6 ");
+ if (do_snb_cstates)
+ fprintf(stderr, " %%c7 ");
+ if (do_snb_cstates)
+ fprintf(stderr, " %%pc2 ");
+ if (do_nhm_cstates)
+ fprintf(stderr, " %%pc3 ");
+ if (do_nhm_cstates)
+ fprintf(stderr, " %%pc6 ");
+ if (do_snb_cstates)
+ fprintf(stderr, " %%pc7 ");
+ if (extra_msr_offset)
+ fprintf(stderr, " MSR 0x%x ", extra_msr_offset);
+
+ putc('\n', stderr);
+}
+
+void dump_pcc(PCC *pcc)
+{
+ fprintf(stderr, "package: %d ", pcc->pkg);
+ fprintf(stderr, "core:: %d ", pcc->core);
+ fprintf(stderr, "CPU: %d ", pcc->cpu);
+ fprintf(stderr, "TSC: %016llX\n", pcc->tsc);
+ fprintf(stderr, "c3: %016llX\n", pcc->c3);
+ fprintf(stderr, "c6: %016llX\n", pcc->c6);
+ fprintf(stderr, "c7: %016llX\n", pcc->c7);
+ fprintf(stderr, "aperf: %016llX\n", pcc->aperf);
+ fprintf(stderr, "pc2: %016llX\n", pcc->pc2);
+ fprintf(stderr, "pc3: %016llX\n", pcc->pc3);
+ fprintf(stderr, "pc6: %016llX\n", pcc->pc6);
+ fprintf(stderr, "pc7: %016llX\n", pcc->pc7);
+ fprintf(stderr, "msr0x%x: %016llX\n", extra_msr_offset, pcc->extra_msr);
+}
+
+void dump_list(PCC *pcc)
+{
+ printf("dump_list 0x%p\n", pcc);
+
+ for (; pcc; pcc = pcc->next)
+ dump_pcc(pcc);
+}
+
+void print_pcc(PCC *p)
+{
+ double interval_float;
+
+ interval_float = tv_delta.tv_sec + tv_delta.tv_usec/1000000.0;
+
+ /* topology columns, print blanks on 1st (average) line */
+ if (p == pcc_average) {
+ if (show_pkg)
+ fprintf(stderr, " ");
+ if (show_core)
+ fprintf(stderr, " ");
+ if (show_cpu)
+ fprintf(stderr, " ");
+ } else {
+ if (show_pkg)
+ fprintf(stderr, "%4d", p->pkg);
+ if (show_core)
+ fprintf(stderr, "%4d", p->core);
+ if (show_cpu)
+ fprintf(stderr, "%4d", p->cpu);
+ }
+
+ /* %c0 */
+ if (do_nhm_cstates) {
+ if (!skip_c0)
+ fprintf(stderr, "%7.2f", 100.0 * p->mperf/p->tsc);
+ else
+ fprintf(stderr, " ****");
+ }
+
+ /* GHz */
+ if (has_aperf) {
+ if (!aperf_mperf_unstable) {
+ fprintf(stderr, "%5.2f",
+ 1.0 * p->tsc / units * p->aperf /
+ p->mperf / interval_float);
+ } else {
+ if (p->aperf > p->tsc || p->mperf > p->tsc) {
+ fprintf(stderr, " ****");
+ } else {
+ fprintf(stderr, "%4.1f*",
+ 1.0 * p->tsc /
+ units * p->aperf /
+ p->mperf / interval_float);
+ }
+ }
+ }
+
+ /* TSC */
+ fprintf(stderr, "%5.2f", 1.0 * p->tsc/units/interval_float);
+
+ if (do_nhm_cstates) {
+ if (!skip_c1)
+ fprintf(stderr, "%7.2f", 100.0 * p->c1/p->tsc);
+ else
+ fprintf(stderr, " ****");
+ }
+ if (do_nhm_cstates)
+ fprintf(stderr, "%7.2f", 100.0 * p->c3/p->tsc);
+ if (do_nhm_cstates)
+ fprintf(stderr, "%7.2f", 100.0 * p->c6/p->tsc);
+ if (do_snb_cstates)
+ fprintf(stderr, "%7.2f", 100.0 * p->c7/p->tsc);
+ if (do_snb_cstates)
+ fprintf(stderr, "%7.2f", 100.0 * p->pc2/p->tsc);
+ if (do_nhm_cstates)
+ fprintf(stderr, "%7.2f", 100.0 * p->pc3/p->tsc);
+ if (do_nhm_cstates)
+ fprintf(stderr, "%7.2f", 100.0 * p->pc6/p->tsc);
+ if (do_snb_cstates)
+ fprintf(stderr, "%7.2f", 100.0 * p->pc7/p->tsc);
+ if (extra_msr_offset)
+ fprintf(stderr, " 0x%016llx", p->extra_msr);
+ putc('\n', stderr);
+}
+
+void print_counters(PCC *cnt)
+{
+ PCC *pcc;
+
+ print_header();
+
+ if (num_cpus > 1)
+ print_pcc(pcc_average);
+
+ for (pcc = cnt; pcc != NULL; pcc = pcc->next)
+ print_pcc(pcc);
+
+}
+
+#define SUBTRACT_COUNTER(after, before, delta) (delta = (after - before), (before > after))
+
+
+int compute_delta(PCC *after, PCC *before, PCC *delta)
+{
+ int errors = 0;
+ int perf_err = 0;
+
+ skip_c0 = skip_c1 = 0;
+
+ for ( ; after && before && delta;
+ after = after->next, before = before->next, delta = delta->next) {
+ if (before->cpu != after->cpu) {
+ printf("cpu configuration changed: %d != %d\n",
+ before->cpu, after->cpu);
+ return -1;
+ }
+
+ if (SUBTRACT_COUNTER(after->tsc, before->tsc, delta->tsc)) {
+ fprintf(stderr, "cpu%d TSC went backwards %llX to %llX\n",
+ before->cpu, before->tsc, after->tsc);
+ errors++;
+ }
+ /* check for TSC < 1 Mcycles over interval */
+ if (delta->tsc < (1000 * 1000)) {
+ fprintf(stderr, "Insanely slow TSC rate,"
+ " TSC stops in idle?\n");
+ fprintf(stderr, "You can disable all c-states"
+ " by booting with \"idle=poll\"\n");
+ fprintf(stderr, "or just the deep ones with"
+ " \"processor.max_cstate=1\"\n");
+ exit(-3);
+ }
+ if (SUBTRACT_COUNTER(after->c3, before->c3, delta->c3)) {
+ fprintf(stderr, "cpu%d c3 counter went backwards %llX to %llX\n",
+ before->cpu, before->c3, after->c3);
+ errors++;
+ }
+ if (SUBTRACT_COUNTER(after->c6, before->c6, delta->c6)) {
+ fprintf(stderr, "cpu%d c6 counter went backwards %llX to %llX\n",
+ before->cpu, before->c6, after->c6);
+ errors++;
+ }
+ if (SUBTRACT_COUNTER(after->c7, before->c7, delta->c7)) {
+ fprintf(stderr, "cpu%d c7 counter went backwards %llX to %llX\n",
+ before->cpu, before->c7, after->c7);
+ errors++;
+ }
+ if (SUBTRACT_COUNTER(after->pc2, before->pc2, delta->pc2)) {
+ fprintf(stderr, "cpu%d pc2 counter went backwards %llX to %llX\n",
+ before->cpu, before->pc2, after->pc2);
+ errors++;
+ }
+ if (SUBTRACT_COUNTER(after->pc3, before->pc3, delta->pc3)) {
+ fprintf(stderr, "cpu%d pc3 counter went backwards %llX to %llX\n",
+ before->cpu, before->pc3, after->pc3);
+ errors++;
+ }
+ if (SUBTRACT_COUNTER(after->pc6, before->pc6, delta->pc6)) {
+ fprintf(stderr, "cpu%d pc6 counter went backwards %llX to %llX\n",
+ before->cpu, before->pc6, after->pc6);
+ errors++;
+ }
+ if (SUBTRACT_COUNTER(after->pc7, before->pc7, delta->pc7)) {
+ fprintf(stderr, "cpu%d pc7 counter went backwards %llX to %llX\n",
+ before->cpu, before->pc7, after->pc7);
+ errors++;
+ }
+
+ perf_err = SUBTRACT_COUNTER(after->aperf, before->aperf, delta->aperf);
+ if (perf_err) {
+ fprintf(stderr, "cpu%d aperf counter went backwards %llX to %llX\n",
+ before->cpu, before->aperf, after->aperf);
+ }
+ perf_err |= SUBTRACT_COUNTER(after->mperf, before->mperf, delta->mperf);
+ if (perf_err) {
+ fprintf(stderr, "cpu%d mperf counter went backwards %llX to %llX\n",
+ before->cpu, before->mperf, after->mperf);
+ }
+ if (perf_err) {
+ if (!aperf_mperf_unstable) {
+ fprintf(stderr, "%s: APERF or MPERF went backwards *\n", progname);
+ fprintf(stderr, "* Frequency results do not cover entire interval *\n");
+ fprintf(stderr, "* fix this by running Linux-2.6.30 or later *\n");
+
+ aperf_mperf_unstable = 1;
+ }
+ /*
+ * mperf delta is likely a huge "positive" number
+ * can not use it for calculating c0 time
+ */
+ skip_c0 = 1;
+ skip_c1 = 1;
+ }
+
+ /*
+ * As mperf and tsc collection are not atomic,
+ * it is possible for mperf's non-halted cycles
+ * to exceed TSC's all cycles: show c1 = 0% in that case.
+ */
+ if (delta->mperf > delta->tsc)
+ delta->c1 = 0;
+ else /* normal case, derive c1 */
+ delta->c1 = delta->tsc - delta->mperf
+ - delta->c3 - delta->c6 - delta->c7;
+
+ if (delta->mperf == 0)
+ delta->mperf = 1; /* divide by 0 protection */
+
+ /*
+ * for "extra msr", just copy the latest w/o subtracting
+ */
+ delta->extra_msr = after->extra_msr;
+ if (errors) {
+ fprintf(stderr, "ERROR cpu%d before:\n", before->cpu);
+ dump_pcc(before);
+ fprintf(stderr, "ERROR cpu%d after:\n", before->cpu);
+ dump_pcc(after);
+ errors = 0;
+ }
+ }
+ return 0;
+}
+
+void compute_average(PCC *delta, PCC *avg)
+{
+ PCC *sum;
+
+ sum = calloc(1, sizeof(PCC));
+ if (sum == NULL) {
+ perror("calloc sum");
+ exit(1);
+ }
+
+ for (; delta; delta = delta->next) {
+ sum->tsc += delta->tsc;
+ sum->c1 += delta->c1;
+ sum->c3 += delta->c3;
+ sum->c6 += delta->c6;
+ sum->c7 += delta->c7;
+ sum->aperf += delta->aperf;
+ sum->mperf += delta->mperf;
+ sum->pc2 += delta->pc2;
+ sum->pc3 += delta->pc3;
+ sum->pc6 += delta->pc6;
+ sum->pc7 += delta->pc7;
+ }
+ avg->tsc = sum->tsc/num_cpus;
+ avg->c1 = sum->c1/num_cpus;
+ avg->c3 = sum->c3/num_cpus;
+ avg->c6 = sum->c6/num_cpus;
+ avg->c7 = sum->c7/num_cpus;
+ avg->aperf = sum->aperf/num_cpus;
+ avg->mperf = sum->mperf/num_cpus;
+ avg->pc2 = sum->pc2/num_cpus;
+ avg->pc3 = sum->pc3/num_cpus;
+ avg->pc6 = sum->pc6/num_cpus;
+ avg->pc7 = sum->pc7/num_cpus;
+
+ free(sum);
+}
+
+void get_counters(PCC *pcc)
+{
+ for ( ; pcc; pcc = pcc->next) {
+ pcc->tsc = get_msr(pcc->cpu, MSR_TSC);
+ if (do_nhm_cstates)
+ pcc->c3 = get_msr(pcc->cpu, MSR_CORE_C3_RESIDENCY);
+ if (do_nhm_cstates)
+ pcc->c6 = get_msr(pcc->cpu, MSR_CORE_C6_RESIDENCY);
+ if (do_snb_cstates)
+ pcc->c7 = get_msr(pcc->cpu, MSR_CORE_C7_RESIDENCY);
+ if (has_aperf)
+ pcc->aperf = get_msr(pcc->cpu, MSR_APERF);
+ if (has_aperf)
+ pcc->mperf = get_msr(pcc->cpu, MSR_MPERF);
+ if (do_snb_cstates)
+ pcc->pc2 = get_msr(pcc->cpu, MSR_PKG_C2_RESIDENCY);
+ if (do_nhm_cstates)
+ pcc->pc3 = get_msr(pcc->cpu, MSR_PKG_C3_RESIDENCY);
+ if (do_nhm_cstates)
+ pcc->pc6 = get_msr(pcc->cpu, MSR_PKG_C6_RESIDENCY);
+ if (do_snb_cstates)
+ pcc->pc7 = get_msr(pcc->cpu, MSR_PKG_C7_RESIDENCY);
+ if (extra_msr_offset)
+ pcc->extra_msr = get_msr(pcc->cpu, extra_msr_offset);
+ }
+}
+
+
+void print_nehalem_info()
+{
+ unsigned long long msr;
+ unsigned int ratio;
+
+ if (!do_nehalem_platform_info)
+ return;
+
+ msr = get_msr(0, MSR_NEHALEM_PLATFORM_INFO);
+
+ ratio = (msr >> 40) & 0xFF;
+ fprintf(stderr, "%d * %.0f = %.0f MHz max efficiency\n",
+ ratio, bclk, ratio * bclk);
+
+ ratio = (msr >> 8) & 0xFF;
+ fprintf(stderr, "%d * %.0f = %.0f MHz TSC frequency\n",
+ ratio, bclk, ratio * bclk);
+
+ if (verbose > 1)
+ fprintf(stderr, "MSR_NEHALEM_PLATFORM_INFO: 0x%llx\n", msr);
+
+ if (!do_nehalem_turbo_ratio_limit)
+ return;
+
+ msr = get_msr(0, MSR_NEHALEM_TURBO_RATIO_LIMIT);
+
+ ratio = (msr >> 24) & 0xFF;
+ if (ratio)
+ fprintf(stderr, "%d * %.0f = %.0f MHz max turbo 4 active cores\n",
+ ratio, bclk, ratio * bclk);
+
+ ratio = (msr >> 16) & 0xFF;
+ if (ratio)
+ fprintf(stderr, "%d * %.0f = %.0f MHz max turbo 3 active cores\n",
+ ratio, bclk, ratio * bclk);
+
+ ratio = (msr >> 8) & 0xFF;
+ if (ratio)
+ fprintf(stderr, "%d * %.0f = %.0f MHz max turbo 2 active cores\n",
+ ratio, bclk, ratio * bclk);
+
+ ratio = (msr >> 0) & 0xFF;
+ if (ratio)
+ fprintf(stderr, "%d * %.0f = %.0f MHz max turbo 1 active cores\n",
+ ratio, bclk, ratio * bclk);
+
+}
+
+void free_counter_list(PCC *list)
+{
+ PCC *p;
+
+ for (p = list; p; ) {
+ PCC *free_me;
+
+ free_me = p;
+ p = p->next;
+ free(free_me);
+ }
+ return;
+}
+
+void free_all_counters(void)
+{
+ free_counter_list(pcc_even);
+ pcc_even = NULL;
+
+ free_counter_list(pcc_odd);
+ pcc_odd = NULL;
+
+ free_counter_list(pcc_delta);
+ pcc_delta = NULL;
+
+ free_counter_list(pcc_average);
+ pcc_average = NULL;
+}
+
+void insert_cpu_counters(PCC **list, PCC *new)
+{
+ PCC *prev;
+
+ /*
+ * list was empty
+ */
+ if (*list == NULL) {
+ new->next = *list;
+ *list = new;
+ return;
+ }
+
+ show_cpu = 1; /* there is more than one CPU */
+
+ /*
+ * insert on front of list.
+ * It is sorted by ascending package#, core#, cpu#
+ */
+ if (((*list)->pkg > new->pkg) ||
+ (((*list)->pkg == new->pkg) && ((*list)->core > new->core)) ||
+ (((*list)->pkg == new->pkg) && ((*list)->core == new->core) && ((*list)->cpu > new->cpu))) {
+ new->next = *list;
+ *list = new;
+ return;
+ }
+
+ prev = *list;
+
+ while (prev->next && (prev->next->pkg < new->pkg)) {
+ prev = prev->next;
+ show_pkg = 1; /* there is more than 1 package */
+ }
+
+ while (prev->next && (prev->next->pkg == new->pkg)
+ && (prev->next->core < new->core)) {
+ prev = prev->next;
+ show_core = 1; /* there is more than 1 core */
+ }
+
+ while (prev->next && (prev->next->pkg == new->pkg)
+ && (prev->next->core == new->core)
+ && (prev->next->cpu < new->cpu)) {
+ prev = prev->next;
+ }
+
+ /*
+ * insert after "prev"
+ */
+ new->next = prev->next;
+ prev->next = new;
+
+ return;
+}
+
+void alloc_new_cpu_counters(int pkg, int core, int cpu)
+{
+ PCC *new;
+
+ if (verbose > 1)
+ printf("pkg%d core%d, cpu%d\n", pkg, core, cpu);
+
+ new = (PCC *)calloc(1, sizeof(PCC));
+ if (new == NULL) {
+ perror("calloc");
+ exit(1);
+ }
+ new->pkg = pkg;
+ new->core = core;
+ new->cpu = cpu;
+ insert_cpu_counters(&pcc_odd, new);
+
+ new = (PCC *)calloc(1, sizeof(PCC));
+ if (new == NULL) {
+ perror("calloc");
+ exit(1);
+ }
+ new->pkg = pkg;
+ new->core = core;
+ new->cpu = cpu;
+ insert_cpu_counters(&pcc_even, new);
+
+ new = (PCC *)calloc(1, sizeof(PCC));
+ if (new == NULL) {
+ perror("calloc");
+ exit(1);
+ }
+ new->pkg = pkg;
+ new->core = core;
+ new->cpu = cpu;
+ insert_cpu_counters(&pcc_delta, new);
+
+ new = (PCC *)calloc(1, sizeof(PCC));
+ if (new == NULL) {
+ perror("calloc");
+ exit(1);
+ }
+ new->pkg = pkg;
+ new->core = core;
+ new->cpu = cpu;
+ pcc_average = new;
+}
+
+int get_physical_package_id(int cpu)
+{
+ char path[64];
+ FILE *filep;
+ int pkg;
+
+ sprintf(path, "/sys/devices/system/cpu/cpu%d/topology/physical_package_id", cpu);
+ filep = fopen(path, "r");
+ if (filep == NULL) {
+ perror(path);
+ exit(1);
+ }
+ fscanf(filep, "%d", &pkg);
+ fclose(filep);
+ return pkg;
+}
+
+int get_core_id(int cpu)
+{
+ char path[64];
+ FILE *filep;
+ int core;
+
+ sprintf(path, "/sys/devices/system/cpu/cpu%d/topology/core_id", cpu);
+ filep = fopen(path, "r");
+ if (filep == NULL) {
+ perror(path);
+ exit(1);
+ }
+ fscanf(filep, "%d", &core);
+ fclose(filep);
+ return core;
+}
+
+/*
+ * run func(index, cpu) on every cpu in /proc/stat
+ */
+
+int for_all_cpus(void (func)(int, int, int))
+{
+ FILE *fp;
+ int cpu_count;
+ int retval;
+
+ fp = fopen(proc_stat, "r");
+ if (fp == NULL) {
+ perror(proc_stat);
+ exit(1);
+ }
+
+ retval = fscanf(fp, "cpu %*d %*d %*d %*d %*d %*d %*d %*d %*d %*d\n");
+ if (retval != 0) {
+ perror("/proc/stat format");
+ exit(1);
+ }
+
+ for (cpu_count = 0; ; cpu_count++) {
+ int cpu;
+
+ retval = fscanf(fp, "cpu%u %*d %*d %*d %*d %*d %*d %*d %*d %*d %*d\n", &cpu);
+ if (retval != 1)
+ break;
+
+ func(get_physical_package_id(cpu), get_core_id(cpu), cpu);
+ }
+ fclose(fp);
+ return cpu_count;
+}
+
+void re_initialize(void)
+{
+ printf("turbostat: topology changed, re-initializing.\n");
+ free_all_counters();
+ num_cpus = for_all_cpus(alloc_new_cpu_counters);
+ need_reinitialize = 0;
+ printf("num_cpus is now %d\n", num_cpus);
+}
+
+void dummy(int pkg, int core, int cpu) { return; }
+/*
+ * check to see if a cpu came on-line
+ */
+void verify_num_cpus()
+{
+ int new_num_cpus;
+
+ new_num_cpus = for_all_cpus(dummy);
+
+ if (new_num_cpus != num_cpus) {
+ if (verbose)
+ printf("num_cpus was %d, is now %d\n",
+ num_cpus, new_num_cpus);
+ need_reinitialize = 1;
+ }
+
+ return;
+}
+
+void turbostat_loop()
+{
+restart:
+ get_counters(pcc_even);
+ gettimeofday(&tv_even, (struct timezone *)NULL);
+
+ while (1) {
+ verify_num_cpus();
+ if (need_reinitialize) {
+ re_initialize();
+ goto restart;
+ }
+ sleep(interval_sec);
+ get_counters(pcc_odd);
+ gettimeofday(&tv_odd, (struct timezone *)NULL);
+
+ compute_delta(pcc_odd, pcc_even, pcc_delta);
+ timersub(&tv_odd, &tv_even, &tv_delta);
+ compute_average(pcc_delta, pcc_average);
+ print_counters(pcc_delta);
+ if (need_reinitialize) {
+ re_initialize();
+ goto restart;
+ }
+ sleep(interval_sec);
+ get_counters(pcc_even);
+ gettimeofday(&tv_even, (struct timezone *)NULL);
+ compute_delta(pcc_even, pcc_odd, pcc_delta);
+ timersub(&tv_even, &tv_odd, &tv_delta);
+ compute_average(pcc_delta, pcc_average);
+ print_counters(pcc_delta);
+ }
+}
+
+void check_dev_msr()
+{
+ struct stat sb;
+
+ if (stat("/dev/cpu/0/msr", &sb)) {
+ fprintf(stderr, "no /dev/cpu/0/msr\n");
+ fprintf(stderr, "Try \"# modprobe msr\"\n");
+ exit(-5);
+ }
+}
+
+void check_super_user()
+{
+ if (getuid() != 0) {
+ fprintf(stderr, "must be root\n");
+ exit(-6);
+ }
+}
+
+int has_nehalem_turbo_ratio_limit(unsigned int family, unsigned int model)
+{
+ if (!genuine_intel)
+ return 0;
+
+ if (family != 6)
+ return 0;
+
+ switch (model) {
+ case 0x1A: /* Core i7, Xeon 5500 series - Bloomfield, Gainstown NHM-EP */
+ case 0x1E: /* Core i7 and i5 Processor - Clarksfield, Lynnfield, Jasper Forest */
+ case 0x1F: /* Core i7 and i5 Processor - Nehalem */
+ case 0x25: /* Westmere Client - Clarkdale, Arrandale */
+ case 0x2C: /* Westmere EP - Gulftown */
+ case 0x2A: /* SNB */
+ case 0x2D: /* SNB Xeon */
+ return 1;
+ case 0x2E: /* Nehalem-EX Xeon - Beckton */
+ case 0x2F: /* Westmere-EX Xeon - Eagleton */
+ default:
+ return 0;
+ }
+}
+
+int is_snb(unsigned int family, unsigned int model)
+{
+ if (!genuine_intel)
+ return 0;
+
+ switch (model) {
+ case 0x2A:
+ case 0x2D:
+ return 1;
+ }
+ return 0;
+}
+
+double discover_bclk(unsigned int family, unsigned int model)
+{
+ if (is_snb(family, model))
+ return 100.00;
+ else
+ return 133.33;
+}
+
+void check_cpuid()
+{
+ unsigned int eax, ebx, ecx, edx, max_level;
+ unsigned int fms, family, model, stepping;
+
+ eax = ebx = ecx = edx = 0;
+
+ asm("cpuid" : "=a" (max_level), "=b" (ebx), "=c" (ecx), "=d" (edx) : "a" (0));
+
+ if (ebx == 0x756e6547 && edx == 0x49656e69 && ecx == 0x6c65746e)
+ genuine_intel = 1;
+
+ if (verbose)
+ fprintf(stderr, "%.4s%.4s%.4s ",
+ (char *)&ebx, (char *)&edx, (char *)&ecx);
+
+ asm("cpuid" : "=a" (fms), "=c" (ecx), "=d" (edx) : "a" (1) : "ebx");
+ family = (fms >> 8) & 0xf;
+ model = (fms >> 4) & 0xf;
+ stepping = fms & 0xf;
+ if (family == 6 || family == 0xf)
+ model += ((fms >> 16) & 0xf) << 4;
+
+ if (verbose)
+ fprintf(stderr, "%d CPUID levels; family:model:stepping 0x%x:%x:%x (%d:%d:%d)\n",
+ max_level, family, model, stepping, family, model, stepping);
+
+ if (!(edx & (1 << 5))) {
+ fprintf(stderr, "CPUID: no MSR\n");
+ exit(1);
+ }
+
+ /*
+ * check max extended function levels of CPUID.
+ * This is needed to check for invariant TSC.
+ * This check is valid for both Intel and AMD.
+ */
+ ebx = ecx = edx = 0;
+ asm("cpuid" : "=a" (max_level), "=b" (ebx), "=c" (ecx), "=d" (edx) : "a" (0x80000000));
+
+ if (max_level < 0x80000007) {
+ fprintf(stderr, "CPUID: no invariant TSC (max_level 0x%x)\n", max_level);
+ exit(1);
+ }
+
+ /*
+ * Non-Stop TSC is advertised by CPUID.EAX=0x80000007: EDX.bit8
+ * this check is valid for both Intel and AMD
+ */
+ asm("cpuid" : "=a" (eax), "=b" (ebx), "=c" (ecx), "=d" (edx) : "a" (0x80000007));
+ has_invariant_tsc = edx && (1 << 8);
+
+ if (!has_invariant_tsc) {
+ fprintf(stderr, "No invariant TSC\n");
+ exit(1);
+ }
+
+ /*
+ * APERF/MPERF is advertised by CPUID.EAX=0x6: ECX.bit0
+ * this check is valid for both Intel and AMD
+ */
+
+ asm("cpuid" : "=a" (eax), "=b" (ebx), "=c" (ecx), "=d" (edx) : "a" (0x6));
+ has_aperf = ecx && (1 << 0);
+ if (!has_aperf) {
+ fprintf(stderr, "No APERF MSR\n");
+ exit(1);
+ }
+
+ do_nehalem_platform_info = genuine_intel && has_invariant_tsc;
+ do_nhm_cstates = genuine_intel; /* all Intel w/ non-stop TSC have NHM counters */
+ do_snb_cstates = is_snb(family, model);
+ bclk = discover_bclk(family, model);
+
+ do_nehalem_turbo_ratio_limit = has_nehalem_turbo_ratio_limit(family, model);
+}
+
+
+void usage()
+{
+ fprintf(stderr, "%s: [-v] [-M MSR#] [-i interval_sec | command ...]\n",
+ progname);
+ exit(1);
+}
+
+
+/*
+ * in /dev/cpu/ return success for names that are numbers
+ * ie. filter out ".", "..", "microcode".
+ */
+int dir_filter(const struct dirent *dirp)
+{
+ if (isdigit(dirp->d_name[0]))
+ return 1;
+ else
+ return 0;
+}
+
+int open_dev_cpu_msr(int dummy1)
+{
+ return 0;
+}
+
+void turbostat_init()
+{
+ check_cpuid();
+
+ check_dev_msr();
+ check_super_user();
+
+ num_cpus = for_all_cpus(alloc_new_cpu_counters);
+
+ if (verbose)
+ print_nehalem_info();
+}
+
+int fork_it(char **argv)
+{
+ int retval;
+ pid_t child_pid;
+ get_counters(pcc_even);
+ gettimeofday(&tv_even, (struct timezone *)NULL);
+
+ child_pid = fork();
+ if (!child_pid) {
+ /* child */
+ execvp(argv[0], argv);
+ } else {
+ int status;
+
+ /* parent */
+ if (child_pid == -1) {
+ perror("fork");
+ exit(1);
+ }
+
+ signal(SIGINT, SIG_IGN);
+ signal(SIGQUIT, SIG_IGN);
+ if (waitpid(child_pid, &status, 0) == -1) {
+ perror("wait");
+ exit(1);
+ }
+ }
+ get_counters(pcc_odd);
+ gettimeofday(&tv_odd, (struct timezone *)NULL);
+ retval = compute_delta(pcc_odd, pcc_even, pcc_delta);
+
+ timersub(&tv_odd, &tv_even, &tv_delta);
+ compute_average(pcc_delta, pcc_average);
+ if (!retval)
+ print_counters(pcc_delta);
+
+ fprintf(stderr, "%.6f sec\n", tv_delta.tv_sec + tv_delta.tv_usec/1000000.0);;
+
+ return 0;
+}
+
+void cmdline(int argc, char **argv)
+{
+ int opt;
+
+ progname = argv[0];
+
+ while ((opt = getopt(argc, argv, "+vi:M:")) != -1) {
+ switch (opt) {
+ case 'v':
+ verbose++;
+ break;
+ case 'i':
+ interval_sec = atoi(optarg);
+ break;
+ case 'M':
+ sscanf(optarg, "%x", &extra_msr_offset);
+ if (verbose > 1)
+ fprintf(stderr, "MSR 0x%X\n", extra_msr_offset);
+ break;
+ default:
+ usage();
+ }
+ }
+}
+
+int main(int argc, char **argv)
+{
+ cmdline(argc, argv);
+
+ if (verbose > 1)
+ fprintf(stderr, "turbostat Dec 6, 2010"
+ " - Len Brown <lenb@kernel.org>\n");
+ if (verbose > 1)
+ fprintf(stderr, "http://userweb.kernel.org/~lenb/acpi/utils/pmtools/turbostat/\n");
+
+ turbostat_init();
+
+ /*
+ * if any params left, it must be a command to fork
+ */
+ if (argc - optind)
+ return fork_it(argv + optind);
+ else
+ turbostat_loop();
+
+ return 0;
+}
diff --git a/tools/power/x86/x86_energy_perf_policy/Makefile b/tools/power/x86/x86_energy_perf_policy/Makefile
new file mode 100644
index 0000000..f458237
--- /dev/null
+++ b/tools/power/x86/x86_energy_perf_policy/Makefile
@@ -0,0 +1,8 @@
+x86_energy_perf_policy : x86_energy_perf_policy.c
+
+clean :
+ rm -f x86_energy_perf_policy
+
+install :
+ install x86_energy_perf_policy /usr/bin/
+ install x86_energy_perf_policy.8 /usr/share/man/man8/
diff --git a/tools/power/x86/x86_energy_perf_policy/x86_energy_perf_policy.8 b/tools/power/x86/x86_energy_perf_policy/x86_energy_perf_policy.8
new file mode 100644
index 0000000..8eaaad6
--- /dev/null
+++ b/tools/power/x86/x86_energy_perf_policy/x86_energy_perf_policy.8
@@ -0,0 +1,104 @@
+.\" This page Copyright (C) 2010 Len Brown <len.brown@intel.com>
+.\" Distributed under the GPL, Copyleft 1994.
+.TH X86_ENERGY_PERF_POLICY 8
+.SH NAME
+x86_energy_perf_policy \- read or write MSR_IA32_ENERGY_PERF_BIAS
+.SH SYNOPSIS
+.ft B
+.B x86_energy_perf_policy
+.RB [ "\-c cpu" ]
+.RB [ "\-v" ]
+.RB "\-r"
+.br
+.B x86_energy_perf_policy
+.RB [ "\-c cpu" ]
+.RB [ "\-v" ]
+.RB 'performance'
+.br
+.B x86_energy_perf_policy
+.RB [ "\-c cpu" ]
+.RB [ "\-v" ]
+.RB 'normal'
+.br
+.B x86_energy_perf_policy
+.RB [ "\-c cpu" ]
+.RB [ "\-v" ]
+.RB 'powersave'
+.br
+.B x86_energy_perf_policy
+.RB [ "\-c cpu" ]
+.RB [ "\-v" ]
+.RB n
+.br
+.SH DESCRIPTION
+\fBx86_energy_perf_policy\fP
+allows software to convey
+its policy for the relative importance of performance
+versus energy savings to the processor.
+
+The processor uses this information in model-specific ways
+when it must select trade-offs between performance and
+energy efficiency.
+
+This policy hint does not supersede Processor Performance states
+(P-states) or CPU Idle power states (C-states), but allows
+software to have influence where it would otherwise be unable
+to express a preference.
+
+For example, this setting may tell the hardware how
+aggressively or conservatively to control frequency
+in the "turbo range" above the explicitly OS-controlled
+P-state frequency range. It may also tell the hardware
+how aggressively is should enter the OS requested C-states.
+
+Support for this feature is indicated by CPUID.06H.ECX.bit3
+per the Intel Architectures Software Developer's Manual.
+
+.SS Options
+\fB-c\fP limits operation to a single CPU.
+The default is to operate on all CPUs.
+Note that MSR_IA32_ENERGY_PERF_BIAS is defined per
+logical processor, but that the initial implementations
+of the MSR were shared among all processors in each package.
+.PP
+\fB-v\fP increases verbosity. By default
+x86_energy_perf_policy is silent.
+.PP
+\fB-r\fP is for "read-only" mode - the unchanged state
+is read and displayed.
+.PP
+.I performance
+Set a policy where performance is paramount.
+The processor will be unwilling to sacrifice any performance
+for the sake of energy saving. This is the hardware default.
+.PP
+.I normal
+Set a policy with a normal balance between performance and energy efficiency.
+The processor will tolerate minor performance compromise
+for potentially significant energy savings.
+This reasonable default for most desktops and servers.
+.PP
+.I powersave
+Set a policy where the processor can accept
+a measurable performance hit to maximize energy efficiency.
+.PP
+.I n
+Set MSR_IA32_ENERGY_PERF_BIAS to the specified number.
+The range of valid numbers is 0-15, where 0 is maximum
+performance and 15 is maximum energy efficiency.
+
+.SH NOTES
+.B "x86_energy_perf_policy "
+runs only as root.
+.SH FILES
+.ta
+.nf
+/dev/cpu/*/msr
+.fi
+
+.SH "SEE ALSO"
+msr(4)
+.PP
+.SH AUTHORS
+.nf
+Written by Len Brown <len.brown@intel.com>
diff --git a/tools/power/x86/x86_energy_perf_policy/x86_energy_perf_policy.c b/tools/power/x86/x86_energy_perf_policy/x86_energy_perf_policy.c
new file mode 100644
index 0000000..d9678a3
--- /dev/null
+++ b/tools/power/x86/x86_energy_perf_policy/x86_energy_perf_policy.c
@@ -0,0 +1,325 @@
+/*
+ * x86_energy_perf_policy -- set the energy versus performance
+ * policy preference bias on recent X86 processors.
+ */
+/*
+ * Copyright (c) 2010, Intel Corporation.
+ * Len Brown <len.brown@intel.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope 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.,
+ * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include <stdio.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/resource.h>
+#include <fcntl.h>
+#include <signal.h>
+#include <sys/time.h>
+#include <stdlib.h>
+#include <string.h>
+
+unsigned int verbose; /* set with -v */
+unsigned int read_only; /* set with -r */
+char *progname;
+unsigned long long new_bias;
+int cpu = -1;
+
+/*
+ * Usage:
+ *
+ * -c cpu: limit action to a single CPU (default is all CPUs)
+ * -v: verbose output (can invoke more than once)
+ * -r: read-only, don't change any settings
+ *
+ * performance
+ * Performance is paramount.
+ * Unwilling to sacrafice any performance
+ * for the sake of energy saving. (hardware default)
+ *
+ * normal
+ * Can tolerate minor performance compromise
+ * for potentially significant energy savings.
+ * (reasonable default for most desktops and servers)
+ *
+ * powersave
+ * Can tolerate significant performance hit
+ * to maximize energy savings.
+ *
+ * n
+ * a numerical value to write to the underlying MSR.
+ */
+void usage(void)
+{
+ printf("%s: [-c cpu] [-v] "
+ "(-r | 'performance' | 'normal' | 'powersave' | n)\n",
+ progname);
+ exit(1);
+}
+
+#define MSR_IA32_ENERGY_PERF_BIAS 0x000001b0
+
+#define BIAS_PERFORMANCE 0
+#define BIAS_BALANCE 6
+#define BIAS_POWERSAVE 15
+
+void cmdline(int argc, char **argv)
+{
+ int opt;
+
+ progname = argv[0];
+
+ while ((opt = getopt(argc, argv, "+rvc:")) != -1) {
+ switch (opt) {
+ case 'c':
+ cpu = atoi(optarg);
+ break;
+ case 'r':
+ read_only = 1;
+ break;
+ case 'v':
+ verbose++;
+ break;
+ default:
+ usage();
+ }
+ }
+ /* if -r, then should be no additional optind */
+ if (read_only && (argc > optind))
+ usage();
+
+ /*
+ * if no -r , then must be one additional optind
+ */
+ if (!read_only) {
+
+ if (argc != optind + 1) {
+ printf("must supply -r or policy param\n");
+ usage();
+ }
+
+ if (!strcmp("performance", argv[optind])) {
+ new_bias = BIAS_PERFORMANCE;
+ } else if (!strcmp("normal", argv[optind])) {
+ new_bias = BIAS_BALANCE;
+ } else if (!strcmp("powersave", argv[optind])) {
+ new_bias = BIAS_POWERSAVE;
+ } else {
+ char *endptr;
+
+ new_bias = strtoull(argv[optind], &endptr, 0);
+ if (endptr == argv[optind] ||
+ new_bias > BIAS_POWERSAVE) {
+ fprintf(stderr, "invalid value: %s\n",
+ argv[optind]);
+ usage();
+ }
+ }
+ }
+}
+
+/*
+ * validate_cpuid()
+ * returns on success, quietly exits on failure (make verbose with -v)
+ */
+void validate_cpuid(void)
+{
+ unsigned int eax, ebx, ecx, edx, max_level;
+ char brand[16];
+ unsigned int fms, family, model, stepping;
+
+ eax = ebx = ecx = edx = 0;
+
+ asm("cpuid" : "=a" (max_level), "=b" (ebx), "=c" (ecx),
+ "=d" (edx) : "a" (0));
+
+ if (ebx != 0x756e6547 || edx != 0x49656e69 || ecx != 0x6c65746e) {
+ if (verbose)
+ fprintf(stderr, "%.4s%.4s%.4s != GenuineIntel",
+ (char *)&ebx, (char *)&edx, (char *)&ecx);
+ exit(1);
+ }
+
+ asm("cpuid" : "=a" (fms), "=c" (ecx), "=d" (edx) : "a" (1) : "ebx");
+ family = (fms >> 8) & 0xf;
+ model = (fms >> 4) & 0xf;
+ stepping = fms & 0xf;
+ if (family == 6 || family == 0xf)
+ model += ((fms >> 16) & 0xf) << 4;
+
+ if (verbose > 1)
+ printf("CPUID %s %d levels family:model:stepping "
+ "0x%x:%x:%x (%d:%d:%d)\n", brand, max_level,
+ family, model, stepping, family, model, stepping);
+
+ if (!(edx & (1 << 5))) {
+ if (verbose)
+ printf("CPUID: no MSR\n");
+ exit(1);
+ }
+
+ /*
+ * Support for MSR_IA32_ENERGY_PERF_BIAS
+ * is indicated by CPUID.06H.ECX.bit3
+ */
+ asm("cpuid" : "=a" (eax), "=b" (ebx), "=c" (ecx), "=d" (edx) : "a" (6));
+ if (verbose)
+ printf("CPUID.06H.ECX: 0x%x\n", ecx);
+ if (!(ecx & (1 << 3))) {
+ if (verbose)
+ printf("CPUID: No MSR_IA32_ENERGY_PERF_BIAS\n");
+ exit(1);
+ }
+ return; /* success */
+}
+
+unsigned long long get_msr(int cpu, int offset)
+{
+ unsigned long long msr;
+ char msr_path[32];
+ int retval;
+ int fd;
+
+ sprintf(msr_path, "/dev/cpu/%d/msr", cpu);
+ fd = open(msr_path, O_RDONLY);
+ if (fd < 0) {
+ printf("Try \"# modprobe msr\"\n");
+ perror(msr_path);
+ exit(1);
+ }
+
+ retval = pread(fd, &msr, sizeof msr, offset);
+
+ if (retval != sizeof msr) {
+ printf("pread cpu%d 0x%x = %d\n", cpu, offset, retval);
+ exit(-2);
+ }
+ close(fd);
+ return msr;
+}
+
+unsigned long long put_msr(int cpu, unsigned long long new_msr, int offset)
+{
+ unsigned long long old_msr;
+ char msr_path[32];
+ int retval;
+ int fd;
+
+ sprintf(msr_path, "/dev/cpu/%d/msr", cpu);
+ fd = open(msr_path, O_RDWR);
+ if (fd < 0) {
+ perror(msr_path);
+ exit(1);
+ }
+
+ retval = pread(fd, &old_msr, sizeof old_msr, offset);
+ if (retval != sizeof old_msr) {
+ perror("pwrite");
+ printf("pread cpu%d 0x%x = %d\n", cpu, offset, retval);
+ exit(-2);
+ }
+
+ retval = pwrite(fd, &new_msr, sizeof new_msr, offset);
+ if (retval != sizeof new_msr) {
+ perror("pwrite");
+ printf("pwrite cpu%d 0x%x = %d\n", cpu, offset, retval);
+ exit(-2);
+ }
+
+ close(fd);
+
+ return old_msr;
+}
+
+void print_msr(int cpu)
+{
+ printf("cpu%d: 0x%016llx\n",
+ cpu, get_msr(cpu, MSR_IA32_ENERGY_PERF_BIAS));
+}
+
+void update_msr(int cpu)
+{
+ unsigned long long previous_msr;
+
+ previous_msr = put_msr(cpu, new_bias, MSR_IA32_ENERGY_PERF_BIAS);
+
+ if (verbose)
+ printf("cpu%d msr0x%x 0x%016llx -> 0x%016llx\n",
+ cpu, MSR_IA32_ENERGY_PERF_BIAS, previous_msr, new_bias);
+
+ return;
+}
+
+char *proc_stat = "/proc/stat";
+/*
+ * run func() on every cpu in /dev/cpu
+ */
+void for_every_cpu(void (func)(int))
+{
+ FILE *fp;
+ int retval;
+
+ fp = fopen(proc_stat, "r");
+ if (fp == NULL) {
+ perror(proc_stat);
+ exit(1);
+ }
+
+ retval = fscanf(fp, "cpu %*d %*d %*d %*d %*d %*d %*d %*d %*d %*d\n");
+ if (retval != 0) {
+ perror("/proc/stat format");
+ exit(1);
+ }
+
+ while (1) {
+ int cpu;
+
+ retval = fscanf(fp,
+ "cpu%u %*d %*d %*d %*d %*d %*d %*d %*d %*d %*d\n",
+ &cpu);
+ if (retval != 1)
+ return;
+
+ func(cpu);
+ }
+ fclose(fp);
+}
+
+int main(int argc, char **argv)
+{
+ cmdline(argc, argv);
+
+ if (verbose > 1)
+ printf("x86_energy_perf_policy Nov 24, 2010"
+ " - Len Brown <lenb@kernel.org>\n");
+ if (verbose > 1 && !read_only)
+ printf("new_bias %lld\n", new_bias);
+
+ validate_cpuid();
+
+ if (cpu != -1) {
+ if (read_only)
+ print_msr(cpu);
+ else
+ update_msr(cpu);
+ } else {
+ if (read_only)
+ for_every_cpu(print_msr);
+ else
+ for_every_cpu(update_msr);
+ }
+
+ return 0;
+}
diff --git a/tools/testing/ktest/compare-ktest-sample.pl b/tools/testing/ktest/compare-ktest-sample.pl
new file mode 100755
index 0000000..9a571e7
--- /dev/null
+++ b/tools/testing/ktest/compare-ktest-sample.pl
@@ -0,0 +1,30 @@
+#!/usr/bin/perl
+
+open (IN,"ktest.pl");
+while (<IN>) {
+ if (/\$opt\{"?([A-Z].*?)(\[.*\])?"?\}/ ||
+ /set_test_option\("(.*?)"/) {
+ $opt{$1} = 1;
+ }
+}
+close IN;
+
+open (IN, "sample.conf");
+while (<IN>) {
+ if (/^\s*#?\s*(\S+)\s*=/) {
+ $samp{$1} = 1;
+ }
+}
+close IN;
+
+foreach $opt (keys %opt) {
+ if (!defined($samp{$opt})) {
+ print "opt = $opt\n";
+ }
+}
+
+foreach $samp (keys %samp) {
+ if (!defined($opt{$samp})) {
+ print "samp = $samp\n";
+ }
+}
diff --git a/tools/testing/ktest/ktest.pl b/tools/testing/ktest/ktest.pl
new file mode 100755
index 0000000..e1c62ee
--- /dev/null
+++ b/tools/testing/ktest/ktest.pl
@@ -0,0 +1,2023 @@
+#!/usr/bin/perl -w
+#
+# Copywrite 2010 - Steven Rostedt <srostedt@redhat.com>, Red Hat Inc.
+# Licensed under the terms of the GNU GPL License version 2
+#
+
+use strict;
+use IPC::Open2;
+use Fcntl qw(F_GETFL F_SETFL O_NONBLOCK);
+use File::Path qw(mkpath);
+use File::Copy qw(cp);
+use FileHandle;
+
+my $VERSION = "0.2";
+
+$| = 1;
+
+my %opt;
+my %repeat_tests;
+my %repeats;
+my %default;
+
+#default opts
+$default{"NUM_TESTS"} = 1;
+$default{"REBOOT_TYPE"} = "grub";
+$default{"TEST_TYPE"} = "test";
+$default{"BUILD_TYPE"} = "randconfig";
+$default{"MAKE_CMD"} = "make";
+$default{"TIMEOUT"} = 120;
+$default{"TMP_DIR"} = "/tmp/ktest";
+$default{"SLEEP_TIME"} = 60; # sleep time between tests
+$default{"BUILD_NOCLEAN"} = 0;
+$default{"REBOOT_ON_ERROR"} = 0;
+$default{"POWEROFF_ON_ERROR"} = 0;
+$default{"REBOOT_ON_SUCCESS"} = 1;
+$default{"POWEROFF_ON_SUCCESS"} = 0;
+$default{"BUILD_OPTIONS"} = "";
+$default{"BISECT_SLEEP_TIME"} = 60; # sleep time between bisects
+$default{"CLEAR_LOG"} = 0;
+$default{"SUCCESS_LINE"} = "login:";
+$default{"BOOTED_TIMEOUT"} = 1;
+$default{"DIE_ON_FAILURE"} = 1;
+$default{"SSH_EXEC"} = "ssh \$SSH_USER\@\$MACHINE \$SSH_COMMAND";
+$default{"SCP_TO_TARGET"} = "scp \$SRC_FILE \$SSH_USER\@\$MACHINE:\$DST_FILE";
+$default{"REBOOT"} = "ssh \$SSH_USER\@\$MACHINE reboot";
+$default{"STOP_AFTER_SUCCESS"} = 10;
+$default{"STOP_AFTER_FAILURE"} = 60;
+$default{"LOCALVERSION"} = "-test";
+
+my $ktest_config;
+my $version;
+my $machine;
+my $ssh_user;
+my $tmpdir;
+my $builddir;
+my $outputdir;
+my $output_config;
+my $test_type;
+my $build_type;
+my $build_options;
+my $reboot_type;
+my $reboot_script;
+my $power_cycle;
+my $reboot;
+my $reboot_on_error;
+my $poweroff_on_error;
+my $die_on_failure;
+my $powercycle_after_reboot;
+my $poweroff_after_halt;
+my $ssh_exec;
+my $scp_to_target;
+my $power_off;
+my $grub_menu;
+my $grub_number;
+my $target;
+my $make;
+my $post_install;
+my $noclean;
+my $minconfig;
+my $addconfig;
+my $in_bisect = 0;
+my $bisect_bad = "";
+my $reverse_bisect;
+my $in_patchcheck = 0;
+my $run_test;
+my $redirect;
+my $buildlog;
+my $dmesg;
+my $monitor_fp;
+my $monitor_pid;
+my $monitor_cnt = 0;
+my $sleep_time;
+my $bisect_sleep_time;
+my $store_failures;
+my $timeout;
+my $booted_timeout;
+my $console;
+my $success_line;
+my $stop_after_success;
+my $stop_after_failure;
+my $build_target;
+my $target_image;
+my $localversion;
+my $iteration = 0;
+my $successes = 0;
+
+my %entered_configs;
+my %config_help;
+
+$config_help{"MACHINE"} = << "EOF"
+ The machine hostname that you will test.
+EOF
+ ;
+$config_help{"SSH_USER"} = << "EOF"
+ The box is expected to have ssh on normal bootup, provide the user
+ (most likely root, since you need privileged operations)
+EOF
+ ;
+$config_help{"BUILD_DIR"} = << "EOF"
+ The directory that contains the Linux source code (full path).
+EOF
+ ;
+$config_help{"OUTPUT_DIR"} = << "EOF"
+ The directory that the objects will be built (full path).
+ (can not be same as BUILD_DIR)
+EOF
+ ;
+$config_help{"BUILD_TARGET"} = << "EOF"
+ The location of the compiled file to copy to the target.
+ (relative to OUTPUT_DIR)
+EOF
+ ;
+$config_help{"TARGET_IMAGE"} = << "EOF"
+ The place to put your image on the test machine.
+EOF
+ ;
+$config_help{"POWER_CYCLE"} = << "EOF"
+ A script or command to reboot the box.
+
+ Here is a digital loggers power switch example
+ POWER_CYCLE = wget --no-proxy -O /dev/null -q --auth-no-challenge 'http://admin:admin\@power/outlet?5=CCL'
+
+ Here is an example to reboot a virtual box on the current host
+ with the name "Guest".
+ POWER_CYCLE = virsh destroy Guest; sleep 5; virsh start Guest
+EOF
+ ;
+$config_help{"CONSOLE"} = << "EOF"
+ The script or command that reads the console
+
+ If you use ttywatch server, something like the following would work.
+CONSOLE = nc -d localhost 3001
+
+ For a virtual machine with guest name "Guest".
+CONSOLE = virsh console Guest
+EOF
+ ;
+$config_help{"LOCALVERSION"} = << "EOF"
+ Required version ending to differentiate the test
+ from other linux builds on the system.
+EOF
+ ;
+$config_help{"REBOOT_TYPE"} = << "EOF"
+ Way to reboot the box to the test kernel.
+ Only valid options so far are "grub" and "script".
+
+ If you specify grub, it will assume grub version 1
+ and will search in /boot/grub/menu.lst for the title \$GRUB_MENU
+ and select that target to reboot to the kernel. If this is not
+ your setup, then specify "script" and have a command or script
+ specified in REBOOT_SCRIPT to boot to the target.
+
+ The entry in /boot/grub/menu.lst must be entered in manually.
+ The test will not modify that file.
+EOF
+ ;
+$config_help{"GRUB_MENU"} = << "EOF"
+ The grub title name for the test kernel to boot
+ (Only mandatory if REBOOT_TYPE = grub)
+
+ Note, ktest.pl will not update the grub menu.lst, you need to
+ manually add an option for the test. ktest.pl will search
+ the grub menu.lst for this option to find what kernel to
+ reboot into.
+
+ For example, if in the /boot/grub/menu.lst the test kernel title has:
+ title Test Kernel
+ kernel vmlinuz-test
+ GRUB_MENU = Test Kernel
+EOF
+ ;
+$config_help{"REBOOT_SCRIPT"} = << "EOF"
+ A script to reboot the target into the test kernel
+ (Only mandatory if REBOOT_TYPE = script)
+EOF
+ ;
+
+
+sub get_ktest_config {
+ my ($config) = @_;
+
+ return if (defined($opt{$config}));
+
+ if (defined($config_help{$config})) {
+ print "\n";
+ print $config_help{$config};
+ }
+
+ for (;;) {
+ print "$config = ";
+ if (defined($default{$config})) {
+ print "\[$default{$config}\] ";
+ }
+ $entered_configs{$config} = <STDIN>;
+ $entered_configs{$config} =~ s/^\s*(.*\S)\s*$/$1/;
+ if ($entered_configs{$config} =~ /^\s*$/) {
+ if ($default{$config}) {
+ $entered_configs{$config} = $default{$config};
+ } else {
+ print "Your answer can not be blank\n";
+ next;
+ }
+ }
+ last;
+ }
+}
+
+sub get_ktest_configs {
+ get_ktest_config("MACHINE");
+ get_ktest_config("SSH_USER");
+ get_ktest_config("BUILD_DIR");
+ get_ktest_config("OUTPUT_DIR");
+ get_ktest_config("BUILD_TARGET");
+ get_ktest_config("TARGET_IMAGE");
+ get_ktest_config("POWER_CYCLE");
+ get_ktest_config("CONSOLE");
+ get_ktest_config("LOCALVERSION");
+
+ my $rtype = $opt{"REBOOT_TYPE"};
+
+ if (!defined($rtype)) {
+ if (!defined($opt{"GRUB_MENU"})) {
+ get_ktest_config("REBOOT_TYPE");
+ $rtype = $entered_configs{"REBOOT_TYPE"};
+ } else {
+ $rtype = "grub";
+ }
+ }
+
+ if ($rtype eq "grub") {
+ get_ktest_config("GRUB_MENU");
+ } else {
+ get_ktest_config("REBOOT_SCRIPT");
+ }
+}
+
+sub set_value {
+ my ($lvalue, $rvalue) = @_;
+
+ if (defined($opt{$lvalue})) {
+ die "Error: Option $lvalue defined more than once!\n";
+ }
+ if ($rvalue =~ /^\s*$/) {
+ delete $opt{$lvalue};
+ } else {
+ $opt{$lvalue} = $rvalue;
+ }
+}
+
+sub read_config {
+ my ($config) = @_;
+
+ open(IN, $config) || die "can't read file $config";
+
+ my $name = $config;
+ $name =~ s,.*/(.*),$1,;
+
+ my $test_num = 0;
+ my $default = 1;
+ my $repeat = 1;
+ my $num_tests_set = 0;
+ my $skip = 0;
+ my $rest;
+
+ while (<IN>) {
+
+ # ignore blank lines and comments
+ next if (/^\s*$/ || /\s*\#/);
+
+ if (/^\s*TEST_START(.*)/) {
+
+ $rest = $1;
+
+ if ($num_tests_set) {
+ die "$name: $.: Can not specify both NUM_TESTS and TEST_START\n";
+ }
+
+ my $old_test_num = $test_num;
+ my $old_repeat = $repeat;
+
+ $test_num += $repeat;
+ $default = 0;
+ $repeat = 1;
+
+ if ($rest =~ /\s+SKIP(.*)/) {
+ $rest = $1;
+ $skip = 1;
+ } else {
+ $skip = 0;
+ }
+
+ if ($rest =~ /\s+ITERATE\s+(\d+)(.*)$/) {
+ $repeat = $1;
+ $rest = $2;
+ $repeat_tests{"$test_num"} = $repeat;
+ }
+
+ if ($rest =~ /\s+SKIP(.*)/) {
+ $rest = $1;
+ $skip = 1;
+ }
+
+ if ($rest !~ /^\s*$/) {
+ die "$name: $.: Gargbage found after TEST_START\n$_";
+ }
+
+ if ($skip) {
+ $test_num = $old_test_num;
+ $repeat = $old_repeat;
+ }
+
+ } elsif (/^\s*DEFAULTS(.*)$/) {
+ $default = 1;
+
+ $rest = $1;
+
+ if ($rest =~ /\s+SKIP(.*)/) {
+ $rest = $1;
+ $skip = 1;
+ } else {
+ $skip = 0;
+ }
+
+ if ($rest !~ /^\s*$/) {
+ die "$name: $.: Gargbage found after DEFAULTS\n$_";
+ }
+
+ } elsif (/^\s*([A-Z_\[\]\d]+)\s*=\s*(.*?)\s*$/) {
+
+ next if ($skip);
+
+ my $lvalue = $1;
+ my $rvalue = $2;
+
+ if (!$default &&
+ ($lvalue eq "NUM_TESTS" ||
+ $lvalue eq "LOG_FILE" ||
+ $lvalue eq "CLEAR_LOG")) {
+ die "$name: $.: $lvalue must be set in DEFAULTS section\n";
+ }
+
+ if ($lvalue eq "NUM_TESTS") {
+ if ($test_num) {
+ die "$name: $.: Can not specify both NUM_TESTS and TEST_START\n";
+ }
+ if (!$default) {
+ die "$name: $.: NUM_TESTS must be set in default section\n";
+ }
+ $num_tests_set = 1;
+ }
+
+ if ($default || $lvalue =~ /\[\d+\]$/) {
+ set_value($lvalue, $rvalue);
+ } else {
+ my $val = "$lvalue\[$test_num\]";
+ set_value($val, $rvalue);
+
+ if ($repeat > 1) {
+ $repeats{$val} = $repeat;
+ }
+ }
+ } else {
+ die "$name: $.: Garbage found in config\n$_";
+ }
+ }
+
+ close(IN);
+
+ if ($test_num) {
+ $test_num += $repeat - 1;
+ $opt{"NUM_TESTS"} = $test_num;
+ }
+
+ # make sure we have all mandatory configs
+ get_ktest_configs;
+
+ # set any defaults
+
+ foreach my $default (keys %default) {
+ if (!defined($opt{$default})) {
+ $opt{$default} = $default{$default};
+ }
+ }
+}
+
+sub _logit {
+ if (defined($opt{"LOG_FILE"})) {
+ open(OUT, ">> $opt{LOG_FILE}") or die "Can't write to $opt{LOG_FILE}";
+ print OUT @_;
+ close(OUT);
+ }
+}
+
+sub logit {
+ if (defined($opt{"LOG_FILE"})) {
+ _logit @_;
+ } else {
+ print @_;
+ }
+}
+
+sub doprint {
+ print @_;
+ _logit @_;
+}
+
+sub run_command;
+
+sub reboot {
+ # try to reboot normally
+ if (run_command $reboot) {
+ if (defined($powercycle_after_reboot)) {
+ sleep $powercycle_after_reboot;
+ run_command "$power_cycle";
+ }
+ } else {
+ # nope? power cycle it.
+ run_command "$power_cycle";
+ }
+}
+
+sub do_not_reboot {
+ my $i = $iteration;
+
+ return $test_type eq "build" ||
+ ($test_type eq "patchcheck" && $opt{"PATCHCHECK_TYPE[$i]"} eq "build") ||
+ ($test_type eq "bisect" && $opt{"BISECT_TYPE[$i]"} eq "build");
+}
+
+sub dodie {
+ doprint "CRITICAL FAILURE... ", @_, "\n";
+
+ my $i = $iteration;
+
+ if ($reboot_on_error && !do_not_reboot) {
+
+ doprint "REBOOTING\n";
+ reboot;
+
+ } elsif ($poweroff_on_error && defined($power_off)) {
+ doprint "POWERING OFF\n";
+ `$power_off`;
+ }
+
+ die @_, "\n";
+}
+
+sub open_console {
+ my ($fp) = @_;
+
+ my $flags;
+
+ my $pid = open($fp, "$console|") or
+ dodie "Can't open console $console";
+
+ $flags = fcntl($fp, F_GETFL, 0) or
+ dodie "Can't get flags for the socket: $!";
+ $flags = fcntl($fp, F_SETFL, $flags | O_NONBLOCK) or
+ dodie "Can't set flags for the socket: $!";
+
+ return $pid;
+}
+
+sub close_console {
+ my ($fp, $pid) = @_;
+
+ doprint "kill child process $pid\n";
+ kill 2, $pid;
+
+ print "closing!\n";
+ close($fp);
+}
+
+sub start_monitor {
+ if ($monitor_cnt++) {
+ return;
+ }
+ $monitor_fp = \*MONFD;
+ $monitor_pid = open_console $monitor_fp;
+
+ return;
+
+ open(MONFD, "Stop perl from warning about single use of MONFD");
+}
+
+sub end_monitor {
+ if (--$monitor_cnt) {
+ return;
+ }
+ close_console($monitor_fp, $monitor_pid);
+}
+
+sub wait_for_monitor {
+ my ($time) = @_;
+ my $line;
+
+ doprint "** Wait for monitor to settle down **\n";
+
+ # read the monitor and wait for the system to calm down
+ do {
+ $line = wait_for_input($monitor_fp, $time);
+ print "$line" if (defined($line));
+ } while (defined($line));
+ print "** Monitor flushed **\n";
+}
+
+sub fail {
+
+ if ($die_on_failure) {
+ dodie @_;
+ }
+
+ doprint "FAILED\n";
+
+ my $i = $iteration;
+
+ # no need to reboot for just building.
+ if (!do_not_reboot) {
+ doprint "REBOOTING\n";
+ reboot;
+ start_monitor;
+ wait_for_monitor $sleep_time;
+ end_monitor;
+ }
+
+ doprint "%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%\n";
+ doprint "%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%\n";
+ doprint "KTEST RESULT: TEST $i Failed: ", @_, "\n";
+ doprint "%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%\n";
+ doprint "%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%\n";
+
+ return 1 if (!defined($store_failures));
+
+ my @t = localtime;
+ my $date = sprintf "%04d%02d%02d%02d%02d%02d",
+ 1900+$t[5],$t[4],$t[3],$t[2],$t[1],$t[0];
+
+ my $type = $build_type;
+ if ($type =~ /useconfig/) {
+ $type = "useconfig";
+ }
+
+ my $dir = "$machine-$test_type-$type-fail-$date";
+ my $faildir = "$store_failures/$dir";
+
+ if (!-d $faildir) {
+ mkpath($faildir) or
+ die "can't create $faildir";
+ }
+ if (-f "$output_config") {
+ cp "$output_config", "$faildir/config" or
+ die "failed to copy .config";
+ }
+ if (-f $buildlog) {
+ cp $buildlog, "$faildir/buildlog" or
+ die "failed to move $buildlog";
+ }
+ if (-f $dmesg) {
+ cp $dmesg, "$faildir/dmesg" or
+ die "failed to move $dmesg";
+ }
+
+ doprint "*** Saved info to $faildir ***\n";
+
+ return 1;
+}
+
+sub run_command {
+ my ($command) = @_;
+ my $dolog = 0;
+ my $dord = 0;
+ my $pid;
+
+ $command =~ s/\$SSH_USER/$ssh_user/g;
+ $command =~ s/\$MACHINE/$machine/g;
+
+ doprint("$command ... ");
+
+ $pid = open(CMD, "$command 2>&1 |") or
+ (fail "unable to exec $command" and return 0);
+
+ if (defined($opt{"LOG_FILE"})) {
+ open(LOG, ">>$opt{LOG_FILE}") or
+ dodie "failed to write to log";
+ $dolog = 1;
+ }
+
+ if (defined($redirect)) {
+ open (RD, ">$redirect") or
+ dodie "failed to write to redirect $redirect";
+ $dord = 1;
+ }
+
+ while (<CMD>) {
+ print LOG if ($dolog);
+ print RD if ($dord);
+ }
+
+ waitpid($pid, 0);
+ my $failed = $?;
+
+ close(CMD);
+ close(LOG) if ($dolog);
+ close(RD) if ($dord);
+
+ if ($failed) {
+ doprint "FAILED!\n";
+ } else {
+ doprint "SUCCESS\n";
+ }
+
+ return !$failed;
+}
+
+sub run_ssh {
+ my ($cmd) = @_;
+ my $cp_exec = $ssh_exec;
+
+ $cp_exec =~ s/\$SSH_COMMAND/$cmd/g;
+ return run_command "$cp_exec";
+}
+
+sub run_scp {
+ my ($src, $dst) = @_;
+ my $cp_scp = $scp_to_target;
+
+ $cp_scp =~ s/\$SRC_FILE/$src/g;
+ $cp_scp =~ s/\$DST_FILE/$dst/g;
+
+ return run_command "$cp_scp";
+}
+
+sub get_grub_index {
+
+ if ($reboot_type ne "grub") {
+ return;
+ }
+ return if (defined($grub_number));
+
+ doprint "Find grub menu ... ";
+ $grub_number = -1;
+
+ my $ssh_grub = $ssh_exec;
+ $ssh_grub =~ s,\$SSH_COMMAND,cat /boot/grub/menu.lst,g;
+
+ open(IN, "$ssh_grub |")
+ or die "unable to get menu.lst";
+
+ while (<IN>) {
+ if (/^\s*title\s+$grub_menu\s*$/) {
+ $grub_number++;
+ last;
+ } elsif (/^\s*title\s/) {
+ $grub_number++;
+ }
+ }
+ close(IN);
+
+ die "Could not find '$grub_menu' in /boot/grub/menu on $machine"
+ if ($grub_number < 0);
+ doprint "$grub_number\n";
+}
+
+sub wait_for_input
+{
+ my ($fp, $time) = @_;
+ my $rin;
+ my $ready;
+ my $line;
+ my $ch;
+
+ if (!defined($time)) {
+ $time = $timeout;
+ }
+
+ $rin = '';
+ vec($rin, fileno($fp), 1) = 1;
+ $ready = select($rin, undef, undef, $time);
+
+ $line = "";
+
+ # try to read one char at a time
+ while (sysread $fp, $ch, 1) {
+ $line .= $ch;
+ last if ($ch eq "\n");
+ }
+
+ if (!length($line)) {
+ return undef;
+ }
+
+ return $line;
+}
+
+sub reboot_to {
+ if ($reboot_type eq "grub") {
+ run_ssh "'(echo \"savedefault --default=$grub_number --once\" | grub --batch; reboot)'";
+ return;
+ }
+
+ run_command "$reboot_script";
+}
+
+sub get_sha1 {
+ my ($commit) = @_;
+
+ doprint "git rev-list --max-count=1 $commit ... ";
+ my $sha1 = `git rev-list --max-count=1 $commit`;
+ my $ret = $?;
+
+ logit $sha1;
+
+ if ($ret) {
+ doprint "FAILED\n";
+ dodie "Failed to get git $commit";
+ }
+
+ print "SUCCESS\n";
+
+ chomp $sha1;
+
+ return $sha1;
+}
+
+sub monitor {
+ my $booted = 0;
+ my $bug = 0;
+ my $skip_call_trace = 0;
+ my $loops;
+
+ wait_for_monitor 5;
+
+ my $line;
+ my $full_line = "";
+
+ open(DMESG, "> $dmesg") or
+ die "unable to write to $dmesg";
+
+ reboot_to;
+
+ my $success_start;
+ my $failure_start;
+
+ for (;;) {
+
+ if ($booted) {
+ $line = wait_for_input($monitor_fp, $booted_timeout);
+ } else {
+ $line = wait_for_input($monitor_fp);
+ }
+
+ last if (!defined($line));
+
+ doprint $line;
+ print DMESG $line;
+
+ # we are not guaranteed to get a full line
+ $full_line .= $line;
+
+ if ($full_line =~ /$success_line/) {
+ $booted = 1;
+ $success_start = time;
+ }
+
+ if ($booted && defined($stop_after_success) &&
+ $stop_after_success >= 0) {
+ my $now = time;
+ if ($now - $success_start >= $stop_after_success) {
+ doprint "Test forced to stop after $stop_after_success seconds after success\n";
+ last;
+ }
+ }
+
+ if ($full_line =~ /\[ backtrace testing \]/) {
+ $skip_call_trace = 1;
+ }
+
+ if ($full_line =~ /call trace:/i) {
+ if (!$skip_call_trace) {
+ $bug = 1;
+ $failure_start = time;
+ }
+ }
+
+ if ($bug && defined($stop_after_failure) &&
+ $stop_after_failure >= 0) {
+ my $now = time;
+ if ($now - $failure_start >= $stop_after_failure) {
+ doprint "Test forced to stop after $stop_after_failure seconds after failure\n";
+ last;
+ }
+ }
+
+ if ($full_line =~ /\[ end of backtrace testing \]/) {
+ $skip_call_trace = 0;
+ }
+
+ if ($full_line =~ /Kernel panic -/) {
+ $bug = 1;
+ }
+
+ if ($line =~ /\n/) {
+ $full_line = "";
+ }
+ }
+
+ close(DMESG);
+
+ if ($bug) {
+ return 0 if ($in_bisect);
+ fail "failed - got a bug report" and return 0;
+ }
+
+ if (!$booted) {
+ return 0 if ($in_bisect);
+ fail "failed - never got a boot prompt." and return 0;
+ }
+
+ return 1;
+}
+
+sub install {
+
+ run_scp "$outputdir/$build_target", "$target_image" or
+ dodie "failed to copy image";
+
+ my $install_mods = 0;
+
+ # should we process modules?
+ $install_mods = 0;
+ open(IN, "$output_config") or dodie("Can't read config file");
+ while (<IN>) {
+ if (/CONFIG_MODULES(=y)?/) {
+ $install_mods = 1 if (defined($1));
+ last;
+ }
+ }
+ close(IN);
+
+ if (!$install_mods) {
+ doprint "No modules needed\n";
+ return;
+ }
+
+ run_command "$make INSTALL_MOD_PATH=$tmpdir modules_install" or
+ dodie "Failed to install modules";
+
+ my $modlib = "/lib/modules/$version";
+ my $modtar = "ktest-mods.tar.bz2";
+
+ run_ssh "rm -rf $modlib" or
+ dodie "failed to remove old mods: $modlib";
+
+ # would be nice if scp -r did not follow symbolic links
+ run_command "cd $tmpdir && tar -cjf $modtar lib/modules/$version" or
+ dodie "making tarball";
+
+ run_scp "$tmpdir/$modtar", "/tmp" or
+ dodie "failed to copy modules";
+
+ unlink "$tmpdir/$modtar";
+
+ run_ssh "'(cd / && tar xf /tmp/$modtar)'" or
+ dodie "failed to tar modules";
+
+ run_ssh "rm -f /tmp/$modtar";
+
+ return if (!defined($post_install));
+
+ my $cp_post_install = $post_install;
+ $cp_post_install = s/\$KERNEL_VERSION/$version/g;
+ run_command "$cp_post_install" or
+ dodie "Failed to run post install";
+}
+
+sub check_buildlog {
+ my ($patch) = @_;
+
+ my @files = `git show $patch | diffstat -l`;
+
+ open(IN, "git show $patch |") or
+ dodie "failed to show $patch";
+ while (<IN>) {
+ if (m,^--- a/(.*),) {
+ chomp $1;
+ $files[$#files] = $1;
+ }
+ }
+ close(IN);
+
+ open(IN, $buildlog) or dodie "Can't open $buildlog";
+ while (<IN>) {
+ if (/^\s*(.*?):.*(warning|error)/) {
+ my $err = $1;
+ foreach my $file (@files) {
+ my $fullpath = "$builddir/$file";
+ if ($file eq $err || $fullpath eq $err) {
+ fail "$file built with warnings" and return 0;
+ }
+ }
+ }
+ }
+ close(IN);
+
+ return 1;
+}
+
+sub build {
+ my ($type) = @_;
+ my $defconfig = "";
+
+ unlink $buildlog;
+
+ if ($type =~ /^useconfig:(.*)/) {
+ run_command "cp $1 $output_config" or
+ dodie "could not copy $1 to .config";
+
+ $type = "oldconfig";
+ }
+
+ # old config can ask questions
+ if ($type eq "oldconfig") {
+ $type = "oldnoconfig";
+
+ # allow for empty configs
+ run_command "touch $output_config";
+
+ run_command "mv $output_config $outputdir/config_temp" or
+ dodie "moving .config";
+
+ if (!$noclean && !run_command "$make mrproper") {
+ dodie "make mrproper";
+ }
+
+ run_command "mv $outputdir/config_temp $output_config" or
+ dodie "moving config_temp";
+
+ } elsif (!$noclean) {
+ unlink "$output_config";
+ run_command "$make mrproper" or
+ dodie "make mrproper";
+ }
+
+ # add something to distinguish this build
+ open(OUT, "> $outputdir/localversion") or dodie("Can't make localversion file");
+ print OUT "$localversion\n";
+ close(OUT);
+
+ if (defined($minconfig)) {
+ $defconfig = "KCONFIG_ALLCONFIG=$minconfig";
+ }
+
+ run_command "$defconfig $make $type" or
+ dodie "failed make config";
+
+ $redirect = "$buildlog";
+ if (!run_command "$make $build_options") {
+ undef $redirect;
+ # bisect may need this to pass
+ return 0 if ($in_bisect);
+ fail "failed build" and return 0;
+ }
+ undef $redirect;
+
+ return 1;
+}
+
+sub halt {
+ if (!run_ssh "halt" or defined($power_off)) {
+ if (defined($poweroff_after_halt)) {
+ sleep $poweroff_after_halt;
+ run_command "$power_off";
+ }
+ } else {
+ # nope? the zap it!
+ run_command "$power_off";
+ }
+}
+
+sub success {
+ my ($i) = @_;
+
+ $successes++;
+
+ doprint "\n\n*******************************************\n";
+ doprint "*******************************************\n";
+ doprint "KTEST RESULT: TEST $i SUCCESS!!!! **\n";
+ doprint "*******************************************\n";
+ doprint "*******************************************\n";
+
+ if ($i != $opt{"NUM_TESTS"} && !do_not_reboot) {
+ doprint "Reboot and wait $sleep_time seconds\n";
+ reboot;
+ start_monitor;
+ wait_for_monitor $sleep_time;
+ end_monitor;
+ }
+}
+
+sub get_version {
+ # get the release name
+ doprint "$make kernelrelease ... ";
+ $version = `$make kernelrelease | tail -1`;
+ chomp($version);
+ doprint "$version\n";
+}
+
+sub child_run_test {
+ my $failed = 0;
+
+ # child should have no power
+ $reboot_on_error = 0;
+ $poweroff_on_error = 0;
+ $die_on_failure = 1;
+
+ run_command $run_test or $failed = 1;
+ exit $failed;
+}
+
+my $child_done;
+
+sub child_finished {
+ $child_done = 1;
+}
+
+sub do_run_test {
+ my $child_pid;
+ my $child_exit;
+ my $line;
+ my $full_line;
+ my $bug = 0;
+
+ wait_for_monitor 1;
+
+ doprint "run test $run_test\n";
+
+ $child_done = 0;
+
+ $SIG{CHLD} = qw(child_finished);
+
+ $child_pid = fork;
+
+ child_run_test if (!$child_pid);
+
+ $full_line = "";
+
+ do {
+ $line = wait_for_input($monitor_fp, 1);
+ if (defined($line)) {
+
+ # we are not guaranteed to get a full line
+ $full_line .= $line;
+
+ if ($full_line =~ /call trace:/i) {
+ $bug = 1;
+ }
+
+ if ($full_line =~ /Kernel panic -/) {
+ $bug = 1;
+ }
+
+ if ($line =~ /\n/) {
+ $full_line = "";
+ }
+ }
+ } while (!$child_done && !$bug);
+
+ if ($bug) {
+ doprint "Detected kernel crash!\n";
+ # kill the child with extreme prejudice
+ kill 9, $child_pid;
+ }
+
+ waitpid $child_pid, 0;
+ $child_exit = $?;
+
+ if ($bug || $child_exit) {
+ return 0 if $in_bisect;
+ fail "test failed" and return 0;
+ }
+ return 1;
+}
+
+sub run_git_bisect {
+ my ($command) = @_;
+
+ doprint "$command ... ";
+
+ my $output = `$command 2>&1`;
+ my $ret = $?;
+
+ logit $output;
+
+ if ($ret) {
+ doprint "FAILED\n";
+ dodie "Failed to git bisect";
+ }
+
+ doprint "SUCCESS\n";
+ if ($output =~ m/^(Bisecting: .*\(roughly \d+ steps?\))\s+\[([[:xdigit:]]+)\]/) {
+ doprint "$1 [$2]\n";
+ } elsif ($output =~ m/^([[:xdigit:]]+) is the first bad commit/) {
+ $bisect_bad = $1;
+ doprint "Found bad commit... $1\n";
+ return 0;
+ } else {
+ # we already logged it, just print it now.
+ print $output;
+ }
+
+ return 1;
+}
+
+# returns 1 on success, 0 on failure
+sub run_bisect_test {
+ my ($type, $buildtype) = @_;
+
+ my $failed = 0;
+ my $result;
+ my $output;
+ my $ret;
+
+ $in_bisect = 1;
+
+ build $buildtype or $failed = 1;
+
+ if ($type ne "build") {
+ dodie "Failed on build" if $failed;
+
+ # Now boot the box
+ get_grub_index;
+ get_version;
+ install;
+
+ start_monitor;
+ monitor or $failed = 1;
+
+ if ($type ne "boot") {
+ dodie "Failed on boot" if $failed;
+
+ do_run_test or $failed = 1;
+ }
+ end_monitor;
+ }
+
+ if ($failed) {
+ $result = 0;
+
+ # reboot the box to a good kernel
+ if ($type ne "build") {
+ doprint "Reboot and sleep $bisect_sleep_time seconds\n";
+ reboot;
+ start_monitor;
+ wait_for_monitor $bisect_sleep_time;
+ end_monitor;
+ }
+ } else {
+ $result = 1;
+ }
+ $in_bisect = 0;
+
+ return $result;
+}
+
+sub run_bisect {
+ my ($type) = @_;
+ my $buildtype = "oldconfig";
+
+ # We should have a minconfig to use?
+ if (defined($minconfig)) {
+ $buildtype = "useconfig:$minconfig";
+ }
+
+ my $ret = run_bisect_test $type, $buildtype;
+
+
+ # Are we looking for where it worked, not failed?
+ if ($reverse_bisect) {
+ $ret = !$ret;
+ }
+
+ if ($ret) {
+ return "good";
+ } else {
+ return "bad";
+ }
+}
+
+sub bisect {
+ my ($i) = @_;
+
+ my $result;
+
+ die "BISECT_GOOD[$i] not defined\n" if (!defined($opt{"BISECT_GOOD[$i]"}));
+ die "BISECT_BAD[$i] not defined\n" if (!defined($opt{"BISECT_BAD[$i]"}));
+ die "BISECT_TYPE[$i] not defined\n" if (!defined($opt{"BISECT_TYPE[$i]"}));
+
+ my $good = $opt{"BISECT_GOOD[$i]"};
+ my $bad = $opt{"BISECT_BAD[$i]"};
+ my $type = $opt{"BISECT_TYPE[$i]"};
+ my $start = $opt{"BISECT_START[$i]"};
+ my $replay = $opt{"BISECT_REPLAY[$i]"};
+
+ # convert to true sha1's
+ $good = get_sha1($good);
+ $bad = get_sha1($bad);
+
+ if (defined($opt{"BISECT_REVERSE[$i]"}) &&
+ $opt{"BISECT_REVERSE[$i]"} == 1) {
+ doprint "Performing a reverse bisect (bad is good, good is bad!)\n";
+ $reverse_bisect = 1;
+ } else {
+ $reverse_bisect = 0;
+ }
+
+ # Can't have a test without having a test to run
+ if ($type eq "test" && !defined($run_test)) {
+ $type = "boot";
+ }
+
+ my $check = $opt{"BISECT_CHECK[$i]"};
+ if (defined($check) && $check ne "0") {
+
+ # get current HEAD
+ my $head = get_sha1("HEAD");
+
+ if ($check ne "good") {
+ doprint "TESTING BISECT BAD [$bad]\n";
+ run_command "git checkout $bad" or
+ die "Failed to checkout $bad";
+
+ $result = run_bisect $type;
+
+ if ($result ne "bad") {
+ fail "Tested BISECT_BAD [$bad] and it succeeded" and return 0;
+ }
+ }
+
+ if ($check ne "bad") {
+ doprint "TESTING BISECT GOOD [$good]\n";
+ run_command "git checkout $good" or
+ die "Failed to checkout $good";
+
+ $result = run_bisect $type;
+
+ if ($result ne "good") {
+ fail "Tested BISECT_GOOD [$good] and it failed" and return 0;
+ }
+ }
+
+ # checkout where we started
+ run_command "git checkout $head" or
+ die "Failed to checkout $head";
+ }
+
+ run_command "git bisect start" or
+ dodie "could not start bisect";
+
+ run_command "git bisect good $good" or
+ dodie "could not set bisect good to $good";
+
+ run_git_bisect "git bisect bad $bad" or
+ dodie "could not set bisect bad to $bad";
+
+ if (defined($replay)) {
+ run_command "git bisect replay $replay" or
+ dodie "failed to run replay";
+ }
+
+ if (defined($start)) {
+ run_command "git checkout $start" or
+ dodie "failed to checkout $start";
+ }
+
+ my $test;
+ do {
+ $result = run_bisect $type;
+ $test = run_git_bisect "git bisect $result";
+ } while ($test);
+
+ run_command "git bisect log" or
+ dodie "could not capture git bisect log";
+
+ run_command "git bisect reset" or
+ dodie "could not reset git bisect";
+
+ doprint "Bad commit was [$bisect_bad]\n";
+
+ success $i;
+}
+
+my %config_ignore;
+my %config_set;
+
+my %config_list;
+my %null_config;
+
+my %dependency;
+
+sub process_config_ignore {
+ my ($config) = @_;
+
+ open (IN, $config)
+ or dodie "Failed to read $config";
+
+ while (<IN>) {
+ if (/^(.*?(CONFIG\S*)(=.*| is not set))/) {
+ $config_ignore{$2} = $1;
+ }
+ }
+
+ close(IN);
+}
+
+sub read_current_config {
+ my ($config_ref) = @_;
+
+ %{$config_ref} = ();
+ undef %{$config_ref};
+
+ my @key = keys %{$config_ref};
+ if ($#key >= 0) {
+ print "did not delete!\n";
+ exit;
+ }
+ open (IN, "$output_config");
+
+ while (<IN>) {
+ if (/^(CONFIG\S+)=(.*)/) {
+ ${$config_ref}{$1} = $2;
+ }
+ }
+ close(IN);
+}
+
+sub get_dependencies {
+ my ($config) = @_;
+
+ my $arr = $dependency{$config};
+ if (!defined($arr)) {
+ return ();
+ }
+
+ my @deps = @{$arr};
+
+ foreach my $dep (@{$arr}) {
+ print "ADD DEP $dep\n";
+ @deps = (@deps, get_dependencies $dep);
+ }
+
+ return @deps;
+}
+
+sub create_config {
+ my @configs = @_;
+
+ open(OUT, ">$output_config") or dodie "Can not write to $output_config";
+
+ foreach my $config (@configs) {
+ print OUT "$config_set{$config}\n";
+ my @deps = get_dependencies $config;
+ foreach my $dep (@deps) {
+ print OUT "$config_set{$dep}\n";
+ }
+ }
+
+ foreach my $config (keys %config_ignore) {
+ print OUT "$config_ignore{$config}\n";
+ }
+ close(OUT);
+
+# exit;
+ run_command "$make oldnoconfig" or
+ dodie "failed make config oldconfig";
+
+}
+
+sub compare_configs {
+ my (%a, %b) = @_;
+
+ foreach my $item (keys %a) {
+ if (!defined($b{$item})) {
+ print "diff $item\n";
+ return 1;
+ }
+ delete $b{$item};
+ }
+
+ my @keys = keys %b;
+ if ($#keys) {
+ print "diff2 $keys[0]\n";
+ }
+ return -1 if ($#keys >= 0);
+
+ return 0;
+}
+
+sub run_config_bisect_test {
+ my ($type) = @_;
+
+ return run_bisect_test $type, "oldconfig";
+}
+
+sub process_passed {
+ my (%configs) = @_;
+
+ doprint "These configs had no failure: (Enabling them for further compiles)\n";
+ # Passed! All these configs are part of a good compile.
+ # Add them to the min options.
+ foreach my $config (keys %configs) {
+ if (defined($config_list{$config})) {
+ doprint " removing $config\n";
+ $config_ignore{$config} = $config_list{$config};
+ delete $config_list{$config};
+ }
+ }
+ doprint "config copied to $outputdir/config_good\n";
+ run_command "cp -f $output_config $outputdir/config_good";
+}
+
+sub process_failed {
+ my ($config) = @_;
+
+ doprint "\n\n***************************************\n";
+ doprint "Found bad config: $config\n";
+ doprint "***************************************\n\n";
+}
+
+sub run_config_bisect {
+
+ my @start_list = keys %config_list;
+
+ if ($#start_list < 0) {
+ doprint "No more configs to test!!!\n";
+ return -1;
+ }
+
+ doprint "***** RUN TEST ***\n";
+ my $type = $opt{"CONFIG_BISECT_TYPE[$iteration]"};
+ my $ret;
+ my %current_config;
+
+ my $count = $#start_list + 1;
+ doprint " $count configs to test\n";
+
+ my $half = int($#start_list / 2);
+
+ do {
+ my @tophalf = @start_list[0 .. $half];
+
+ create_config @tophalf;
+ read_current_config \%current_config;
+
+ $count = $#tophalf + 1;
+ doprint "Testing $count configs\n";
+ my $found = 0;
+ # make sure we test something
+ foreach my $config (@tophalf) {
+ if (defined($current_config{$config})) {
+ logit " $config\n";
+ $found = 1;
+ }
+ }
+ if (!$found) {
+ # try the other half
+ doprint "Top half produced no set configs, trying bottom half\n";
+ @tophalf = @start_list[$half .. $#start_list];
+ create_config @tophalf;
+ read_current_config \%current_config;
+ foreach my $config (@tophalf) {
+ if (defined($current_config{$config})) {
+ logit " $config\n";
+ $found = 1;
+ }
+ }
+ if (!$found) {
+ doprint "Failed: Can't make new config with current configs\n";
+ foreach my $config (@start_list) {
+ doprint " CONFIG: $config\n";
+ }
+ return -1;
+ }
+ $count = $#tophalf + 1;
+ doprint "Testing $count configs\n";
+ }
+
+ $ret = run_config_bisect_test $type;
+
+ if ($ret) {
+ process_passed %current_config;
+ return 0;
+ }
+
+ doprint "This config had a failure.\n";
+ doprint "Removing these configs that were not set in this config:\n";
+ doprint "config copied to $outputdir/config_bad\n";
+ run_command "cp -f $output_config $outputdir/config_bad";
+
+ # A config exists in this group that was bad.
+ foreach my $config (keys %config_list) {
+ if (!defined($current_config{$config})) {
+ doprint " removing $config\n";
+ delete $config_list{$config};
+ }
+ }
+
+ @start_list = @tophalf;
+
+ if ($#start_list == 0) {
+ process_failed $start_list[0];
+ return 1;
+ }
+
+ # remove half the configs we are looking at and see if
+ # they are good.
+ $half = int($#start_list / 2);
+ } while ($half > 0);
+
+ # we found a single config, try it again
+ my @tophalf = @start_list[0 .. 0];
+
+ $ret = run_config_bisect_test $type;
+ if ($ret) {
+ process_passed %current_config;
+ return 0;
+ }
+
+ process_failed $start_list[0];
+ return 1;
+}
+
+sub config_bisect {
+ my ($i) = @_;
+
+ my $start_config = $opt{"CONFIG_BISECT[$i]"};
+
+ my $tmpconfig = "$tmpdir/use_config";
+
+ # Make the file with the bad config and the min config
+ if (defined($minconfig)) {
+ # read the min config for things to ignore
+ run_command "cp $minconfig $tmpconfig" or
+ dodie "failed to copy $minconfig to $tmpconfig";
+ } else {
+ unlink $tmpconfig;
+ }
+
+ # Add other configs
+ if (defined($addconfig)) {
+ run_command "cat $addconfig >> $tmpconfig" or
+ dodie "failed to append $addconfig";
+ }
+
+ my $defconfig = "";
+ if (-f $tmpconfig) {
+ $defconfig = "KCONFIG_ALLCONFIG=$tmpconfig";
+ process_config_ignore $tmpconfig;
+ }
+
+ # now process the start config
+ run_command "cp $start_config $output_config" or
+ dodie "failed to copy $start_config to $output_config";
+
+ # read directly what we want to check
+ my %config_check;
+ open (IN, $output_config)
+ or dodie "faied to open $output_config";
+
+ while (<IN>) {
+ if (/^((CONFIG\S*)=.*)/) {
+ $config_check{$2} = $1;
+ }
+ }
+ close(IN);
+
+ # Now run oldconfig with the minconfig (and addconfigs)
+ run_command "$defconfig $make oldnoconfig" or
+ dodie "failed make config oldconfig";
+
+ # check to see what we lost (or gained)
+ open (IN, $output_config)
+ or dodie "Failed to read $start_config";
+
+ my %removed_configs;
+ my %added_configs;
+
+ while (<IN>) {
+ if (/^((CONFIG\S*)=.*)/) {
+ # save off all options
+ $config_set{$2} = $1;
+ if (defined($config_check{$2})) {
+ if (defined($config_ignore{$2})) {
+ $removed_configs{$2} = $1;
+ } else {
+ $config_list{$2} = $1;
+ }
+ } elsif (!defined($config_ignore{$2})) {
+ $added_configs{$2} = $1;
+ $config_list{$2} = $1;
+ }
+ }
+ }
+ close(IN);
+
+ my @confs = keys %removed_configs;
+ if ($#confs >= 0) {
+ doprint "Configs overridden by default configs and removed from check:\n";
+ foreach my $config (@confs) {
+ doprint " $config\n";
+ }
+ }
+ @confs = keys %added_configs;
+ if ($#confs >= 0) {
+ doprint "Configs appearing in make oldconfig and added:\n";
+ foreach my $config (@confs) {
+ doprint " $config\n";
+ }
+ }
+
+ my %config_test;
+ my $once = 0;
+
+ # Sometimes kconfig does weird things. We must make sure
+ # that the config we autocreate has everything we need
+ # to test, otherwise we may miss testing configs, or
+ # may not be able to create a new config.
+ # Here we create a config with everything set.
+ create_config (keys %config_list);
+ read_current_config \%config_test;
+ foreach my $config (keys %config_list) {
+ if (!defined($config_test{$config})) {
+ if (!$once) {
+ $once = 1;
+ doprint "Configs not produced by kconfig (will not be checked):\n";
+ }
+ doprint " $config\n";
+ delete $config_list{$config};
+ }
+ }
+ my $ret;
+ do {
+ $ret = run_config_bisect;
+ } while (!$ret);
+
+ return $ret if ($ret < 0);
+
+ success $i;
+}
+
+sub patchcheck {
+ my ($i) = @_;
+
+ die "PATCHCHECK_START[$i] not defined\n"
+ if (!defined($opt{"PATCHCHECK_START[$i]"}));
+ die "PATCHCHECK_TYPE[$i] not defined\n"
+ if (!defined($opt{"PATCHCHECK_TYPE[$i]"}));
+
+ my $start = $opt{"PATCHCHECK_START[$i]"};
+
+ my $end = "HEAD";
+ if (defined($opt{"PATCHCHECK_END[$i]"})) {
+ $end = $opt{"PATCHCHECK_END[$i]"};
+ }
+
+ # Get the true sha1's since we can use things like HEAD~3
+ $start = get_sha1($start);
+ $end = get_sha1($end);
+
+ my $type = $opt{"PATCHCHECK_TYPE[$i]"};
+
+ # Can't have a test without having a test to run
+ if ($type eq "test" && !defined($run_test)) {
+ $type = "boot";
+ }
+
+ open (IN, "git log --pretty=oneline $end|") or
+ dodie "could not get git list";
+
+ my @list;
+
+ while (<IN>) {
+ chomp;
+ $list[$#list+1] = $_;
+ last if (/^$start/);
+ }
+ close(IN);
+
+ if ($list[$#list] !~ /^$start/) {
+ fail "SHA1 $start not found";
+ }
+
+ # go backwards in the list
+ @list = reverse @list;
+
+ my $save_clean = $noclean;
+
+ $in_patchcheck = 1;
+ foreach my $item (@list) {
+ my $sha1 = $item;
+ $sha1 =~ s/^([[:xdigit:]]+).*/$1/;
+
+ doprint "\nProcessing commit $item\n\n";
+
+ run_command "git checkout $sha1" or
+ die "Failed to checkout $sha1";
+
+ # only clean on the first and last patch
+ if ($item eq $list[0] ||
+ $item eq $list[$#list]) {
+ $noclean = $save_clean;
+ } else {
+ $noclean = 1;
+ }
+
+ if (defined($minconfig)) {
+ build "useconfig:$minconfig" or return 0;
+ } else {
+ # ?? no config to use?
+ build "oldconfig" or return 0;
+ }
+
+ check_buildlog $sha1 or return 0;
+
+ next if ($type eq "build");
+
+ get_grub_index;
+ get_version;
+ install;
+
+ my $failed = 0;
+
+ start_monitor;
+ monitor or $failed = 1;
+
+ if (!$failed && $type ne "boot"){
+ do_run_test or $failed = 1;
+ }
+ end_monitor;
+ return 0 if ($failed);
+
+ }
+ $in_patchcheck = 0;
+ success $i;
+
+ return 1;
+}
+
+$#ARGV < 1 or die "ktest.pl version: $VERSION\n usage: ktest.pl config-file\n";
+
+if ($#ARGV == 0) {
+ $ktest_config = $ARGV[0];
+ if (! -f $ktest_config) {
+ print "$ktest_config does not exist.\n";
+ my $ans;
+ for (;;) {
+ print "Create it? [Y/n] ";
+ $ans = <STDIN>;
+ chomp $ans;
+ if ($ans =~ /^\s*$/) {
+ $ans = "y";
+ }
+ last if ($ans =~ /^y$/i || $ans =~ /^n$/i);
+ print "Please answer either 'y' or 'n'.\n";
+ }
+ if ($ans !~ /^y$/i) {
+ exit 0;
+ }
+ }
+} else {
+ $ktest_config = "ktest.conf";
+}
+
+if (! -f $ktest_config) {
+ open(OUT, ">$ktest_config") or die "Can not create $ktest_config";
+ print OUT << "EOF"
+# Generated by ktest.pl
+#
+# Define each test with TEST_START
+# The config options below it will override the defaults
+TEST_START
+
+DEFAULTS
+EOF
+;
+ close(OUT);
+}
+read_config $ktest_config;
+
+# Append any configs entered in manually to the config file.
+my @new_configs = keys %entered_configs;
+if ($#new_configs >= 0) {
+ print "\nAppending entered in configs to $ktest_config\n";
+ open(OUT, ">>$ktest_config") or die "Can not append to $ktest_config";
+ foreach my $config (@new_configs) {
+ print OUT "$config = $entered_configs{$config}\n";
+ $opt{$config} = $entered_configs{$config};
+ }
+}
+
+if ($opt{"CLEAR_LOG"} && defined($opt{"LOG_FILE"})) {
+ unlink $opt{"LOG_FILE"};
+}
+
+doprint "\n\nSTARTING AUTOMATED TESTS\n\n";
+
+for (my $i = 0, my $repeat = 1; $i <= $opt{"NUM_TESTS"}; $i += $repeat) {
+
+ if (!$i) {
+ doprint "DEFAULT OPTIONS:\n";
+ } else {
+ doprint "\nTEST $i OPTIONS";
+ if (defined($repeat_tests{$i})) {
+ $repeat = $repeat_tests{$i};
+ doprint " ITERATE $repeat";
+ }
+ doprint "\n";
+ }
+
+ foreach my $option (sort keys %opt) {
+
+ if ($option =~ /\[(\d+)\]$/) {
+ next if ($i != $1);
+ } else {
+ next if ($i);
+ }
+
+ doprint "$option = $opt{$option}\n";
+ }
+}
+
+sub set_test_option {
+ my ($name, $i) = @_;
+
+ my $option = "$name\[$i\]";
+
+ if (defined($opt{$option})) {
+ return $opt{$option};
+ }
+
+ foreach my $test (keys %repeat_tests) {
+ if ($i >= $test &&
+ $i < $test + $repeat_tests{$test}) {
+ $option = "$name\[$test\]";
+ if (defined($opt{$option})) {
+ return $opt{$option};
+ }
+ }
+ }
+
+ if (defined($opt{$name})) {
+ return $opt{$name};
+ }
+
+ return undef;
+}
+
+# First we need to do is the builds
+for (my $i = 1; $i <= $opt{"NUM_TESTS"}; $i++) {
+
+ $iteration = $i;
+
+ my $makecmd = set_test_option("MAKE_CMD", $i);
+
+ $machine = set_test_option("MACHINE", $i);
+ $ssh_user = set_test_option("SSH_USER", $i);
+ $tmpdir = set_test_option("TMP_DIR", $i);
+ $outputdir = set_test_option("OUTPUT_DIR", $i);
+ $builddir = set_test_option("BUILD_DIR", $i);
+ $test_type = set_test_option("TEST_TYPE", $i);
+ $build_type = set_test_option("BUILD_TYPE", $i);
+ $build_options = set_test_option("BUILD_OPTIONS", $i);
+ $power_cycle = set_test_option("POWER_CYCLE", $i);
+ $reboot = set_test_option("REBOOT", $i);
+ $noclean = set_test_option("BUILD_NOCLEAN", $i);
+ $minconfig = set_test_option("MIN_CONFIG", $i);
+ $run_test = set_test_option("TEST", $i);
+ $addconfig = set_test_option("ADD_CONFIG", $i);
+ $reboot_type = set_test_option("REBOOT_TYPE", $i);
+ $grub_menu = set_test_option("GRUB_MENU", $i);
+ $post_install = set_test_option("POST_INSTALL", $i);
+ $reboot_script = set_test_option("REBOOT_SCRIPT", $i);
+ $reboot_on_error = set_test_option("REBOOT_ON_ERROR", $i);
+ $poweroff_on_error = set_test_option("POWEROFF_ON_ERROR", $i);
+ $die_on_failure = set_test_option("DIE_ON_FAILURE", $i);
+ $power_off = set_test_option("POWER_OFF", $i);
+ $powercycle_after_reboot = set_test_option("POWERCYCLE_AFTER_REBOOT", $i);
+ $poweroff_after_halt = set_test_option("POWEROFF_AFTER_HALT", $i);
+ $sleep_time = set_test_option("SLEEP_TIME", $i);
+ $bisect_sleep_time = set_test_option("BISECT_SLEEP_TIME", $i);
+ $store_failures = set_test_option("STORE_FAILURES", $i);
+ $timeout = set_test_option("TIMEOUT", $i);
+ $booted_timeout = set_test_option("BOOTED_TIMEOUT", $i);
+ $console = set_test_option("CONSOLE", $i);
+ $success_line = set_test_option("SUCCESS_LINE", $i);
+ $stop_after_success = set_test_option("STOP_AFTER_SUCCESS", $i);
+ $stop_after_failure = set_test_option("STOP_AFTER_FAILURE", $i);
+ $build_target = set_test_option("BUILD_TARGET", $i);
+ $ssh_exec = set_test_option("SSH_EXEC", $i);
+ $scp_to_target = set_test_option("SCP_TO_TARGET", $i);
+ $target_image = set_test_option("TARGET_IMAGE", $i);
+ $localversion = set_test_option("LOCALVERSION", $i);
+
+ chdir $builddir || die "can't change directory to $builddir";
+
+ if (!-d $tmpdir) {
+ mkpath($tmpdir) or
+ die "can't create $tmpdir";
+ }
+
+ $ENV{"SSH_USER"} = $ssh_user;
+ $ENV{"MACHINE"} = $machine;
+
+ $target = "$ssh_user\@$machine";
+
+ $buildlog = "$tmpdir/buildlog-$machine";
+ $dmesg = "$tmpdir/dmesg-$machine";
+ $make = "$makecmd O=$outputdir";
+ $output_config = "$outputdir/.config";
+
+ if ($reboot_type eq "grub") {
+ dodie "GRUB_MENU not defined" if (!defined($grub_menu));
+ } elsif (!defined($reboot_script)) {
+ dodie "REBOOT_SCRIPT not defined"
+ }
+
+ my $run_type = $build_type;
+ if ($test_type eq "patchcheck") {
+ $run_type = $opt{"PATCHCHECK_TYPE[$i]"};
+ } elsif ($test_type eq "bisect") {
+ $run_type = $opt{"BISECT_TYPE[$i]"};
+ } elsif ($test_type eq "config_bisect") {
+ $run_type = $opt{"CONFIG_BISECT_TYPE[$i]"};
+ }
+
+ # mistake in config file?
+ if (!defined($run_type)) {
+ $run_type = "ERROR";
+ }
+
+ doprint "\n\n";
+ doprint "RUNNING TEST $i of $opt{NUM_TESTS} with option $test_type $run_type\n\n";
+
+ unlink $dmesg;
+ unlink $buildlog;
+
+ if (!defined($minconfig)) {
+ $minconfig = $addconfig;
+
+ } elsif (defined($addconfig)) {
+ run_command "cat $addconfig $minconfig > $tmpdir/add_config" or
+ dodie "Failed to create temp config";
+ $minconfig = "$tmpdir/add_config";
+ }
+
+ my $checkout = $opt{"CHECKOUT[$i]"};
+ if (defined($checkout)) {
+ run_command "git checkout $checkout" or
+ die "failed to checkout $checkout";
+ }
+
+ if ($test_type eq "bisect") {
+ bisect $i;
+ next;
+ } elsif ($test_type eq "config_bisect") {
+ config_bisect $i;
+ next;
+ } elsif ($test_type eq "patchcheck") {
+ patchcheck $i;
+ next;
+ }
+
+ if ($build_type ne "nobuild") {
+ build $build_type or next;
+ }
+
+ if ($test_type ne "build") {
+ get_grub_index;
+ get_version;
+ install;
+
+ my $failed = 0;
+ start_monitor;
+ monitor or $failed = 1;;
+
+ if (!$failed && $test_type ne "boot" && defined($run_test)) {
+ do_run_test or $failed = 1;
+ }
+ end_monitor;
+ next if ($failed);
+ }
+
+ success $i;
+}
+
+if ($opt{"POWEROFF_ON_SUCCESS"}) {
+ halt;
+} elsif ($opt{"REBOOT_ON_SUCCESS"} && !do_not_reboot) {
+ reboot;
+}
+
+doprint "\n $successes of $opt{NUM_TESTS} tests were successful\n\n";
+
+exit 0;
diff --git a/tools/testing/ktest/sample.conf b/tools/testing/ktest/sample.conf
new file mode 100644
index 0000000..3408c59
--- /dev/null
+++ b/tools/testing/ktest/sample.conf
@@ -0,0 +1,622 @@
+#
+# Config file for ktest.pl
+#
+# Note, all paths must be absolute
+#
+
+# Options set in the beginning of the file are considered to be
+# default options. These options can be overriden by test specific
+# options, with the following exceptions:
+#
+# LOG_FILE
+# CLEAR_LOG
+# POWEROFF_ON_SUCCESS
+# REBOOT_ON_SUCCESS
+#
+# Test specific options are set after the label:
+#
+# TEST_START
+#
+# The options after a TEST_START label are specific to that test.
+# Each TEST_START label will set up a new test. If you want to
+# perform a test more than once, you can add the ITERATE label
+# to it followed by the number of times you want that test
+# to iterate. If the ITERATE is left off, the test will only
+# be performed once.
+#
+# TEST_START ITERATE 10
+#
+# You can skip a test by adding SKIP (before or after the ITERATE
+# and number)
+#
+# TEST_START SKIP
+#
+# TEST_START SKIP ITERATE 10
+#
+# TEST_START ITERATE 10 SKIP
+#
+# The SKIP label causes the options and the test itself to be ignored.
+# This is useful to set up several different tests in one config file, and
+# only enabling the ones you want to use for a current test run.
+#
+# You can add default options anywhere in the file as well
+# with the DEFAULTS tag. This allows you to have default options
+# after the test options to keep the test options at the top
+# of the file. You can even place the DEFAULTS tag between
+# test cases (but not in the middle of a single test case)
+#
+# TEST_START
+# MIN_CONFIG = /home/test/config-test1
+#
+# DEFAULTS
+# MIN_CONFIG = /home/test/config-default
+#
+# TEST_START ITERATE 10
+#
+# The above will run the first test with MIN_CONFIG set to
+# /home/test/config-test-1. Then 10 tests will be executed
+# with MIN_CONFIG with /home/test/config-default.
+#
+# You can also disable defaults with the SKIP option
+#
+# DEFAULTS SKIP
+# MIN_CONFIG = /home/test/config-use-sometimes
+#
+# DEFAULTS
+# MIN_CONFIG = /home/test/config-most-times
+#
+# The above will ignore the first MIN_CONFIG. If you want to
+# use the first MIN_CONFIG, remove the SKIP from the first
+# DEFAULTS tag and add it to the second. Be careful, options
+# may only be declared once per test or default. If you have
+# the same option name under the same test or as default
+# ktest will fail to execute, and no tests will run.
+#
+
+
+#### Mandatory Default Options ####
+
+# These options must be in the default section, although most
+# may be overridden by test options.
+
+# The machine hostname that you will test
+#MACHINE = target
+
+# The box is expected to have ssh on normal bootup, provide the user
+# (most likely root, since you need privileged operations)
+#SSH_USER = root
+
+# The directory that contains the Linux source code
+#BUILD_DIR = /home/test/linux.git
+
+# The directory that the objects will be built
+# (can not be same as BUILD_DIR)
+#OUTPUT_DIR = /home/test/build/target
+
+# The location of the compiled file to copy to the target
+# (relative to OUTPUT_DIR)
+#BUILD_TARGET = arch/x86/boot/bzImage
+
+# The place to put your image on the test machine
+#TARGET_IMAGE = /boot/vmlinuz-test
+
+# A script or command to reboot the box
+#
+# Here is a digital loggers power switch example
+#POWER_CYCLE = wget --no-proxy -O /dev/null -q --auth-no-challenge 'http://admin:admin@power/outlet?5=CCL'
+#
+# Here is an example to reboot a virtual box on the current host
+# with the name "Guest".
+#POWER_CYCLE = virsh destroy Guest; sleep 5; virsh start Guest
+
+# The script or command that reads the console
+#
+# If you use ttywatch server, something like the following would work.
+#CONSOLE = nc -d localhost 3001
+#
+# For a virtual machine with guest name "Guest".
+#CONSOLE = virsh console Guest
+
+# Required version ending to differentiate the test
+# from other linux builds on the system.
+#LOCALVERSION = -test
+
+# The grub title name for the test kernel to boot
+# (Only mandatory if REBOOT_TYPE = grub)
+#
+# Note, ktest.pl will not update the grub menu.lst, you need to
+# manually add an option for the test. ktest.pl will search
+# the grub menu.lst for this option to find what kernel to
+# reboot into.
+#
+# For example, if in the /boot/grub/menu.lst the test kernel title has:
+# title Test Kernel
+# kernel vmlinuz-test
+#GRUB_MENU = Test Kernel
+
+# A script to reboot the target into the test kernel
+# (Only mandatory if REBOOT_TYPE = script)
+#REBOOT_SCRIPT =
+
+#### Optional Config Options (all have defaults) ####
+
+# Start a test setup. If you leave this off, all options
+# will be default and the test will run once.
+# This is a label and not really an option (it takes no value).
+# You can append ITERATE and a number after it to iterate the
+# test a number of times, or SKIP to ignore this test.
+#
+#TEST_START
+#TEST_START ITERATE 5
+#TEST_START SKIP
+
+# Have the following options as default again. Used after tests
+# have already been defined by TEST_START. Optionally, you can
+# just define all default options before the first TEST_START
+# and you do not need this option.
+#
+# This is a label and not really an option (it takes no value).
+# You can append SKIP to this label and the options within this
+# section will be ignored.
+#
+# DEFAULTS
+# DEFAULTS SKIP
+
+# The default test type (default test)
+# The test types may be:
+# build - only build the kernel, do nothing else
+# boot - build and boot the kernel
+# test - build, boot and if TEST is set, run the test script
+# (If TEST is not set, it defaults back to boot)
+# bisect - Perform a bisect on the kernel (see BISECT_TYPE below)
+# patchcheck - Do a test on a series of commits in git (see PATCHCHECK below)
+#TEST_TYPE = test
+
+# Test to run if there is a successful boot and TEST_TYPE is test.
+# Must exit with 0 on success and non zero on error
+# default (undefined)
+#TEST = ssh user@machine /root/run_test
+
+# The build type is any make config type or special command
+# (default randconfig)
+# nobuild - skip the clean and build step
+# useconfig:/path/to/config - use the given config and run
+# oldconfig on it.
+# This option is ignored if TEST_TYPE is patchcheck or bisect
+#BUILD_TYPE = randconfig
+
+# The make command (default make)
+# If you are building a 32bit x86 on a 64 bit host
+#MAKE_CMD = CC=i386-gcc AS=i386-as make ARCH=i386
+
+# Any build options for the make of the kernel (not for other makes, like configs)
+# (default "")
+#BUILD_OPTIONS = -j20
+
+# If you need an initrd, you can add a script or code here to install
+# it. The environment variable KERNEL_VERSION will be set to the
+# kernel version that is used. Remember to add the initrd line
+# to your grub menu.lst file.
+#
+# Here's a couple of examples to use:
+#POST_INSTALL = ssh user@target /sbin/mkinitrd --allow-missing -f /boot/initramfs-test.img $KERNEL_VERSION
+#
+# or on some systems:
+#POST_INSTALL = ssh user@target /sbin/dracut -f /boot/initramfs-test.img $KERNEL_VERSION
+
+# Way to reboot the box to the test kernel.
+# Only valid options so far are "grub" and "script"
+# (default grub)
+# If you specify grub, it will assume grub version 1
+# and will search in /boot/grub/menu.lst for the title $GRUB_MENU
+# and select that target to reboot to the kernel. If this is not
+# your setup, then specify "script" and have a command or script
+# specified in REBOOT_SCRIPT to boot to the target.
+#
+# The entry in /boot/grub/menu.lst must be entered in manually.
+# The test will not modify that file.
+#REBOOT_TYPE = grub
+
+# The min config that is needed to build for the machine
+# A nice way to create this is with the following:
+#
+# $ ssh target
+# $ lsmod > mymods
+# $ scp mymods host:/tmp
+# $ exit
+# $ cd linux.git
+# $ rm .config
+# $ make LSMOD=mymods localyesconfig
+# $ grep '^CONFIG' .config > /home/test/config-min
+#
+# If you want even less configs:
+#
+# log in directly to target (do not ssh)
+#
+# $ su
+# # lsmod | cut -d' ' -f1 | xargs rmmod
+#
+# repeat the above several times
+#
+# # lsmod > mymods
+# # reboot
+#
+# May need to reboot to get your network back to copy the mymods
+# to the host, and then remove the previous .config and run the
+# localyesconfig again. The CONFIG_MIN generated like this will
+# not guarantee network activity to the box so the TEST_TYPE of
+# test may fail.
+#
+# You might also want to set:
+# CONFIG_CMDLINE="<your options here>"
+# randconfig may set the above and override your real command
+# line options.
+# (default undefined)
+#MIN_CONFIG = /home/test/config-min
+
+# Sometimes there's options that just break the boot and
+# you do not care about. Here are a few:
+# # CONFIG_STAGING is not set
+# Staging drivers are horrible, and can break the build.
+# # CONFIG_SCSI_DEBUG is not set
+# SCSI_DEBUG may change your root partition
+# # CONFIG_KGDB_SERIAL_CONSOLE is not set
+# KGDB may cause oops waiting for a connection that's not there.
+# This option points to the file containing config options that will be prepended
+# to the MIN_CONFIG (or be the MIN_CONFIG if it is not set)
+#
+# Note, config options in MIN_CONFIG will override these options.
+#
+# (default undefined)
+#ADD_CONFIG = /home/test/config-broken
+
+# The location on the host where to write temp files
+# (default /tmp/ktest)
+#TMP_DIR = /tmp/ktest
+
+# Optional log file to write the status (recommended)
+# Note, this is a DEFAULT section only option.
+# (default undefined)
+#LOG_FILE = /home/test/logfiles/target.log
+
+# Remove old logfile if it exists before starting all tests.
+# Note, this is a DEFAULT section only option.
+# (default 0)
+#CLEAR_LOG = 0
+
+# Line to define a successful boot up in console output.
+# This is what the line contains, not the entire line. If you need
+# the entire line to match, then use regural expression syntax like:
+# (do not add any quotes around it)
+#
+# SUCCESS_LINE = ^MyBox Login:$
+#
+# (default "login:")
+#SUCCESS_LINE = login:
+
+# In case the console constantly fills the screen, having
+# a specified time to stop the test after success is recommended.
+# (in seconds)
+# (default 10)
+#STOP_AFTER_SUCCESS = 10
+
+# In case the console constantly fills the screen, having
+# a specified time to stop the test after failure is recommended.
+# (in seconds)
+# (default 60)
+#STOP_AFTER_FAILURE = 60
+
+# Stop testing if a build fails. If set, the script will end if
+# a failure is detected, otherwise it will save off the .config,
+# dmesg and bootlog in a directory called
+# MACHINE-TEST_TYPE_BUILD_TYPE-fail-yyyymmddhhmmss
+# if the STORE_FAILURES directory is set.
+# (default 1)
+# Note, even if this is set to zero, there are some errors that still
+# stop the tests.
+#DIE_ON_FAILURE = 1
+
+# Directory to store failure directories on failure. If this is not
+# set, DIE_ON_FAILURE=0 will not save off the .config, dmesg and
+# bootlog. This option is ignored if DIE_ON_FAILURE is not set.
+# (default undefined)
+#STORE_FAILURES = /home/test/failures
+
+# Build without doing a make mrproper, or removing .config
+# (default 0)
+#BUILD_NOCLEAN = 0
+
+# As the test reads the console, after it hits the SUCCESS_LINE
+# the time it waits for the monitor to settle down between reads
+# can usually be lowered.
+# (in seconds) (default 1)
+#BOOTED_TIMEOUT = 1
+
+# The timeout in seconds when we consider the box hung after
+# the console stop producing output. Be sure to leave enough
+# time here to get pass a reboot. Some machines may not produce
+# any console output for a long time during a reboot. You do
+# not want the test to fail just because the system was in
+# the process of rebooting to the test kernel.
+# (default 120)
+#TIMEOUT = 120
+
+# In between tests, a reboot of the box may occur, and this
+# is the time to wait for the console after it stops producing
+# output. Some machines may not produce a large lag on reboot
+# so this should accommodate it.
+# The difference between this and TIMEOUT, is that TIMEOUT happens
+# when rebooting to the test kernel. This sleep time happens
+# after a test has completed and we are about to start running
+# another test. If a reboot to the reliable kernel happens,
+# we wait SLEEP_TIME for the console to stop producing output
+# before starting the next test.
+# (default 60)
+#SLEEP_TIME = 60
+
+# The time in between bisects to sleep (in seconds)
+# (default 60)
+#BISECT_SLEEP_TIME = 60
+
+# Reboot the target box on error (default 0)
+#REBOOT_ON_ERROR = 0
+
+# Power off the target on error (ignored if REBOOT_ON_ERROR is set)
+# Note, this is a DEFAULT section only option.
+# (default 0)
+#POWEROFF_ON_ERROR = 0
+
+# Power off the target after all tests have completed successfully
+# Note, this is a DEFAULT section only option.
+# (default 0)
+#POWEROFF_ON_SUCCESS = 0
+
+# Reboot the target after all test completed successfully (default 1)
+# (ignored if POWEROFF_ON_SUCCESS is set)
+#REBOOT_ON_SUCCESS = 1
+
+# In case there are isses with rebooting, you can specify this
+# to always powercycle after this amount of time after calling
+# reboot.
+# Note, POWERCYCLE_AFTER_REBOOT = 0 does NOT disable it. It just
+# makes it powercycle immediately after rebooting. Do not define
+# it if you do not want it.
+# (default undefined)
+#POWERCYCLE_AFTER_REBOOT = 5
+
+# In case there's isses with halting, you can specify this
+# to always poweroff after this amount of time after calling
+# halt.
+# Note, POWEROFF_AFTER_HALT = 0 does NOT disable it. It just
+# makes it poweroff immediately after halting. Do not define
+# it if you do not want it.
+# (default undefined)
+#POWEROFF_AFTER_HALT = 20
+
+# A script or command to power off the box (default undefined)
+# Needed for POWEROFF_ON_ERROR and SUCCESS
+#
+# Example for digital loggers power switch:
+#POWER_OFF = wget --no-proxy -O /dev/null -q --auth-no-challenge 'http://admin:admin@power/outlet?5=OFF'
+#
+# Example for a virtual guest call "Guest".
+#POWER_OFF = virsh destroy Guest
+
+# The way to execute a command on the target
+# (default ssh $SSH_USER@$MACHINE $SSH_COMMAND";)
+# The variables SSH_USER, MACHINE and SSH_COMMAND are defined
+#SSH_EXEC = ssh $SSH_USER@$MACHINE $SSH_COMMAND";
+
+# The way to copy a file to the target
+# (default scp $SRC_FILE $SSH_USER@$MACHINE:$DST_FILE)
+# The variables SSH_USER, MACHINE, SRC_FILE and DST_FILE are defined.
+#SCP_TO_TARGET = scp $SRC_FILE $SSH_USER@$MACHINE:$DST_FILE
+
+# The nice way to reboot the target
+# (default ssh $SSH_USER@$MACHINE reboot)
+# The variables SSH_USER and MACHINE are defined.
+#REBOOT = ssh $SSH_USER@$MACHINE reboot
+
+#### Per test run options ####
+# The following options are only allowed in TEST_START sections.
+# They are ignored in the DEFAULTS sections.
+#
+# All of these are optional and undefined by default, although
+# some of these options are required for TEST_TYPE of patchcheck
+# and bisect.
+#
+#
+# CHECKOUT = branch
+#
+# If the BUILD_DIR is a git repository, then you can set this option
+# to checkout the given branch before running the TEST. If you
+# specify this for the first run, that branch will be used for
+# all preceding tests until a new CHECKOUT is set.
+#
+#
+#
+# For TEST_TYPE = patchcheck
+#
+# This expects the BUILD_DIR to be a git repository, and
+# will checkout the PATCHCHECK_START commit.
+#
+# The option BUILD_TYPE will be ignored.
+#
+# The MIN_CONFIG will be used for all builds of the patchcheck. The build type
+# used for patchcheck is oldconfig.
+#
+# PATCHCHECK_START is required and is the first patch to
+# test (the SHA1 of the commit). You may also specify anything
+# that git checkout allows (branch name, tage, HEAD~3).
+#
+# PATCHCHECK_END is the last patch to check (default HEAD)
+#
+# PATCHCHECK_TYPE is required and is the type of test to run:
+# build, boot, test.
+#
+# Note, the build test will look for warnings, if a warning occurred
+# in a file that a commit touches, the build will fail.
+#
+# If BUILD_NOCLEAN is set, then make mrproper will not be run on
+# any of the builds, just like all other TEST_TYPE tests. But
+# what makes patchcheck different from the other tests, is if
+# BUILD_NOCLEAN is not set, only the first and last patch run
+# make mrproper. This helps speed up the test.
+#
+# Example:
+# TEST_START
+# TEST_TYPE = patchcheck
+# CHECKOUT = mybranch
+# PATCHCHECK_TYPE = boot
+# PATCHCHECK_START = 747e94ae3d1b4c9bf5380e569f614eb9040b79e7
+# PATCHCHECK_END = HEAD~2
+#
+#
+#
+# For TEST_TYPE = bisect
+#
+# You can specify a git bisect if the BUILD_DIR is a git repository.
+# The MIN_CONFIG will be used for all builds of the bisect. The build type
+# used for bisecting is oldconfig.
+#
+# The option BUILD_TYPE will be ignored.
+#
+# BISECT_TYPE is the type of test to perform:
+# build - bad fails to build
+# boot - bad builds but fails to boot
+# test - bad boots but fails a test
+#
+# BISECT_GOOD is the commit (SHA1) to label as good (accepts all git good commit types)
+# BISECT_BAD is the commit to label as bad (accepts all git bad commit types)
+#
+# The above three options are required for a bisect operation.
+#
+# BISECT_REPLAY = /path/to/replay/file (optional, default undefined)
+#
+# If an operation failed in the bisect that was not expected to
+# fail. Then the test ends. The state of the BUILD_DIR will be
+# left off at where the failure occurred. You can examine the
+# reason for the failure, and perhaps even find a git commit
+# that would work to continue with. You can run:
+#
+# git bisect log > /path/to/replay/file
+#
+# The adding:
+#
+# BISECT_REPLAY= /path/to/replay/file
+#
+# And running the test again. The test will perform the initial
+# git bisect start, git bisect good, and git bisect bad, and
+# then it will run git bisect replay on this file, before
+# continuing with the bisect.
+#
+# BISECT_START = commit (optional, default undefined)
+#
+# As with BISECT_REPLAY, if the test failed on a commit that
+# just happen to have a bad commit in the middle of the bisect,
+# and you need to skip it. If BISECT_START is defined, it
+# will checkout that commit after doing the initial git bisect start,
+# git bisect good, git bisect bad, and running the git bisect replay
+# if the BISECT_REPLAY is set.
+#
+# BISECT_REVERSE = 1 (optional, default 0)
+#
+# In those strange instances where it was broken forever
+# and you are trying to find where it started to work!
+# Set BISECT_GOOD to the commit that was last known to fail
+# Set BISECT_BAD to the commit that is known to start working.
+# With BISECT_REVERSE = 1, The test will consider failures as
+# good, and success as bad.
+#
+# BISECT_CHECK = 1 (optional, default 0)
+#
+# Just to be sure the good is good and bad is bad, setting
+# BISECT_CHECK to 1 will start the bisect by first checking
+# out BISECT_BAD and makes sure it fails, then it will check
+# out BISECT_GOOD and makes sure it succeeds before starting
+# the bisect (it works for BISECT_REVERSE too).
+#
+# You can limit the test to just check BISECT_GOOD or
+# BISECT_BAD with BISECT_CHECK = good or
+# BISECT_CHECK = bad, respectively.
+#
+# Example:
+# TEST_START
+# TEST_TYPE = bisect
+# BISECT_GOOD = v2.6.36
+# BISECT_BAD = b5153163ed580e00c67bdfecb02b2e3843817b3e
+# BISECT_TYPE = build
+# MIN_CONFIG = /home/test/config-bisect
+#
+#
+#
+# For TEST_TYPE = config_bisect
+#
+# In those cases that you have two different configs. One of them
+# work, the other does not, and you do not know what config causes
+# the problem.
+# The TEST_TYPE config_bisect will bisect the bad config looking for
+# what config causes the failure.
+#
+# The way it works is this:
+#
+# First it finds a config to work with. Since a different version, or
+# MIN_CONFIG may cause different dependecies, it must run through this
+# preparation.
+#
+# Overwrites any config set in the bad config with a config set in
+# either the MIN_CONFIG or ADD_CONFIG. Thus, make sure these configs
+# are minimal and do not disable configs you want to test:
+# (ie. # CONFIG_FOO is not set).
+#
+# An oldconfig is run on the bad config and any new config that
+# appears will be added to the configs to test.
+#
+# Finally, it generates a config with the above result and runs it
+# again through make oldconfig to produce a config that should be
+# satisfied by kconfig.
+#
+# Then it starts the bisect.
+#
+# The configs to test are cut in half. If all the configs in this
+# half depend on a config in the other half, then the other half
+# is tested instead. If no configs are enabled by either half, then
+# this means a circular dependency exists and the test fails.
+#
+# A config is created with the test half, and the bisect test is run.
+#
+# If the bisect succeeds, then all configs in the generated config
+# are removed from the configs to test and added to the configs that
+# will be enabled for all builds (they will be enabled, but not be part
+# of the configs to examine).
+#
+# If the bisect fails, then all test configs that were not enabled by
+# the config file are removed from the test. These configs will not
+# be enabled in future tests. Since current config failed, we consider
+# this to be a subset of the config that we started with.
+#
+# When we are down to one config, it is considered the bad config.
+#
+# Note, the config chosen may not be the true bad config. Due to
+# dependencies and selections of the kbuild system, mulitple
+# configs may be needed to cause a failure. If you disable the
+# config that was found and restart the test, if the test fails
+# again, it is recommended to rerun the config_bisect with a new
+# bad config without the found config enabled.
+#
+# The option BUILD_TYPE will be ignored.
+#
+# CONFIG_BISECT_TYPE is the type of test to perform:
+# build - bad fails to build
+# boot - bad builds but fails to boot
+# test - bad boots but fails a test
+#
+# CONFIG_BISECT is the config that failed to boot
+#
+# Example:
+# TEST_START
+# TEST_TYPE = config_bisect
+# CONFIG_BISECT_TYPE = build
+# CONFIG_BISECT = /home/test/¢onfig-bad
+# MIN_CONFIG = /home/test/config-min
+#
diff --git a/usr/Kconfig b/usr/Kconfig
index c2c7fe2..4780dea 100644
--- a/usr/Kconfig
+++ b/usr/Kconfig
@@ -72,6 +72,15 @@
Support loading of a LZMA encoded initial ramdisk or cpio buffer
If unsure, say N.
+config RD_XZ
+ bool "Support initial ramdisks compressed using XZ" if EMBEDDED
+ default !EMBEDDED
+ depends on BLK_DEV_INITRD
+ select DECOMPRESS_XZ
+ help
+ Support loading of a XZ encoded initial ramdisk or cpio buffer.
+ If unsure, say N.
+
config RD_LZO
bool "Support initial ramdisks compressed using LZO" if EMBEDDED
default !EMBEDDED
@@ -139,6 +148,15 @@
three. Compression is slowest. The initramfs size is about 33%
smaller with LZMA in comparison to gzip.
+config INITRAMFS_COMPRESSION_XZ
+ bool "XZ"
+ depends on RD_XZ
+ help
+ XZ uses the LZMA2 algorithm. The initramfs size is about 30%
+ smaller with XZ in comparison to gzip. Decompression speed
+ is better than that of bzip2 but worse than gzip and LZO.
+ Compression is slow.
+
config INITRAMFS_COMPRESSION_LZO
bool "LZO"
depends on RD_LZO
diff --git a/usr/Makefile b/usr/Makefile
index 6faa444..029ffe6 100644
--- a/usr/Makefile
+++ b/usr/Makefile
@@ -15,6 +15,9 @@
# Lzma
suffix_$(CONFIG_INITRAMFS_COMPRESSION_LZMA) = .lzma
+# XZ
+suffix_$(CONFIG_INITRAMFS_COMPRESSION_XZ) = .xz
+
# Lzo
suffix_$(CONFIG_INITRAMFS_COMPRESSION_LZO) = .lzo
@@ -50,7 +53,7 @@
quiet_cmd_initfs = GEN $@
cmd_initfs = $(initramfs) -o $@ $(ramfs-args) $(ramfs-input)
-targets := initramfs_data.cpio.gz initramfs_data.cpio.bz2 initramfs_data.cpio.lzma initramfs_data.cpio.lzo initramfs_data.cpio
+targets := initramfs_data.cpio.gz initramfs_data.cpio.bz2 initramfs_data.cpio.lzma initramfs_data.cpio.xz initramfs_data.cpio.lzo initramfs_data.cpio
# do not try to update files included in initramfs
$(deps_initramfs): ;
diff --git a/virt/kvm/Kconfig b/virt/kvm/Kconfig
index 7f1178f..f63ccb0 100644
--- a/virt/kvm/Kconfig
+++ b/virt/kvm/Kconfig
@@ -15,3 +15,6 @@
config KVM_MMIO
bool
+
+config KVM_ASYNC_PF
+ bool
diff --git a/virt/kvm/assigned-dev.c b/virt/kvm/assigned-dev.c
index 7c98928..ae72ae60 100644
--- a/virt/kvm/assigned-dev.c
+++ b/virt/kvm/assigned-dev.c
@@ -55,58 +55,31 @@
return index;
}
-static void kvm_assigned_dev_interrupt_work_handler(struct work_struct *work)
+static irqreturn_t kvm_assigned_dev_thread(int irq, void *dev_id)
{
- struct kvm_assigned_dev_kernel *assigned_dev;
- int i;
+ struct kvm_assigned_dev_kernel *assigned_dev = dev_id;
+ u32 vector;
+ int index;
- assigned_dev = container_of(work, struct kvm_assigned_dev_kernel,
- interrupt_work);
+ if (assigned_dev->irq_requested_type & KVM_DEV_IRQ_HOST_INTX) {
+ spin_lock(&assigned_dev->intx_lock);
+ disable_irq_nosync(irq);
+ assigned_dev->host_irq_disabled = true;
+ spin_unlock(&assigned_dev->intx_lock);
+ }
- spin_lock_irq(&assigned_dev->assigned_dev_lock);
if (assigned_dev->irq_requested_type & KVM_DEV_IRQ_HOST_MSIX) {
- struct kvm_guest_msix_entry *guest_entries =
- assigned_dev->guest_msix_entries;
- for (i = 0; i < assigned_dev->entries_nr; i++) {
- if (!(guest_entries[i].flags &
- KVM_ASSIGNED_MSIX_PENDING))
- continue;
- guest_entries[i].flags &= ~KVM_ASSIGNED_MSIX_PENDING;
+ index = find_index_from_host_irq(assigned_dev, irq);
+ if (index >= 0) {
+ vector = assigned_dev->
+ guest_msix_entries[index].vector;
kvm_set_irq(assigned_dev->kvm,
- assigned_dev->irq_source_id,
- guest_entries[i].vector, 1);
+ assigned_dev->irq_source_id, vector, 1);
}
} else
kvm_set_irq(assigned_dev->kvm, assigned_dev->irq_source_id,
assigned_dev->guest_irq, 1);
- spin_unlock_irq(&assigned_dev->assigned_dev_lock);
-}
-
-static irqreturn_t kvm_assigned_dev_intr(int irq, void *dev_id)
-{
- unsigned long flags;
- struct kvm_assigned_dev_kernel *assigned_dev =
- (struct kvm_assigned_dev_kernel *) dev_id;
-
- spin_lock_irqsave(&assigned_dev->assigned_dev_lock, flags);
- if (assigned_dev->irq_requested_type & KVM_DEV_IRQ_HOST_MSIX) {
- int index = find_index_from_host_irq(assigned_dev, irq);
- if (index < 0)
- goto out;
- assigned_dev->guest_msix_entries[index].flags |=
- KVM_ASSIGNED_MSIX_PENDING;
- }
-
- schedule_work(&assigned_dev->interrupt_work);
-
- if (assigned_dev->irq_requested_type & KVM_DEV_IRQ_GUEST_INTX) {
- disable_irq_nosync(irq);
- assigned_dev->host_irq_disabled = true;
- }
-
-out:
- spin_unlock_irqrestore(&assigned_dev->assigned_dev_lock, flags);
return IRQ_HANDLED;
}
@@ -114,7 +87,6 @@
static void kvm_assigned_dev_ack_irq(struct kvm_irq_ack_notifier *kian)
{
struct kvm_assigned_dev_kernel *dev;
- unsigned long flags;
if (kian->gsi == -1)
return;
@@ -127,12 +99,12 @@
/* The guest irq may be shared so this ack may be
* from another device.
*/
- spin_lock_irqsave(&dev->assigned_dev_lock, flags);
+ spin_lock(&dev->intx_lock);
if (dev->host_irq_disabled) {
enable_irq(dev->host_irq);
dev->host_irq_disabled = false;
}
- spin_unlock_irqrestore(&dev->assigned_dev_lock, flags);
+ spin_unlock(&dev->intx_lock);
}
static void deassign_guest_irq(struct kvm *kvm,
@@ -141,6 +113,9 @@
kvm_unregister_irq_ack_notifier(kvm, &assigned_dev->ack_notifier);
assigned_dev->ack_notifier.gsi = -1;
+ kvm_set_irq(assigned_dev->kvm, assigned_dev->irq_source_id,
+ assigned_dev->guest_irq, 0);
+
if (assigned_dev->irq_source_id != -1)
kvm_free_irq_source_id(kvm, assigned_dev->irq_source_id);
assigned_dev->irq_source_id = -1;
@@ -152,28 +127,19 @@
struct kvm_assigned_dev_kernel *assigned_dev)
{
/*
- * In kvm_free_device_irq, cancel_work_sync return true if:
- * 1. work is scheduled, and then cancelled.
- * 2. work callback is executed.
- *
- * The first one ensured that the irq is disabled and no more events
- * would happen. But for the second one, the irq may be enabled (e.g.
- * for MSI). So we disable irq here to prevent further events.
+ * We disable irq here to prevent further events.
*
* Notice this maybe result in nested disable if the interrupt type is
* INTx, but it's OK for we are going to free it.
*
* If this function is a part of VM destroy, please ensure that till
* now, the kvm state is still legal for probably we also have to wait
- * interrupt_work done.
+ * on a currently running IRQ handler.
*/
if (assigned_dev->irq_requested_type & KVM_DEV_IRQ_HOST_MSIX) {
int i;
for (i = 0; i < assigned_dev->entries_nr; i++)
- disable_irq_nosync(assigned_dev->
- host_msix_entries[i].vector);
-
- cancel_work_sync(&assigned_dev->interrupt_work);
+ disable_irq(assigned_dev->host_msix_entries[i].vector);
for (i = 0; i < assigned_dev->entries_nr; i++)
free_irq(assigned_dev->host_msix_entries[i].vector,
@@ -185,8 +151,7 @@
pci_disable_msix(assigned_dev->dev);
} else {
/* Deal with MSI and INTx */
- disable_irq_nosync(assigned_dev->host_irq);
- cancel_work_sync(&assigned_dev->interrupt_work);
+ disable_irq(assigned_dev->host_irq);
free_irq(assigned_dev->host_irq, (void *)assigned_dev);
@@ -232,7 +197,8 @@
{
kvm_free_assigned_irq(kvm, assigned_dev);
- pci_reset_function(assigned_dev->dev);
+ __pci_reset_function(assigned_dev->dev);
+ pci_restore_state(assigned_dev->dev);
pci_release_regions(assigned_dev->dev);
pci_disable_device(assigned_dev->dev);
@@ -265,8 +231,8 @@
* on the same interrupt line is not a happy situation: there
* are going to be long delays in accepting, acking, etc.
*/
- if (request_irq(dev->host_irq, kvm_assigned_dev_intr,
- 0, "kvm_assigned_intx_device", (void *)dev))
+ if (request_threaded_irq(dev->host_irq, NULL, kvm_assigned_dev_thread,
+ IRQF_ONESHOT, dev->irq_name, (void *)dev))
return -EIO;
return 0;
}
@@ -284,8 +250,8 @@
}
dev->host_irq = dev->dev->irq;
- if (request_irq(dev->host_irq, kvm_assigned_dev_intr, 0,
- "kvm_assigned_msi_device", (void *)dev)) {
+ if (request_threaded_irq(dev->host_irq, NULL, kvm_assigned_dev_thread,
+ 0, dev->irq_name, (void *)dev)) {
pci_disable_msi(dev->dev);
return -EIO;
}
@@ -310,10 +276,9 @@
return r;
for (i = 0; i < dev->entries_nr; i++) {
- r = request_irq(dev->host_msix_entries[i].vector,
- kvm_assigned_dev_intr, 0,
- "kvm_assigned_msix_device",
- (void *)dev);
+ r = request_threaded_irq(dev->host_msix_entries[i].vector,
+ NULL, kvm_assigned_dev_thread,
+ 0, dev->irq_name, (void *)dev);
if (r)
goto err;
}
@@ -370,6 +335,9 @@
if (dev->irq_requested_type & KVM_DEV_IRQ_HOST_MASK)
return r;
+ snprintf(dev->irq_name, sizeof(dev->irq_name), "kvm:%s",
+ pci_name(dev->dev));
+
switch (host_irq_type) {
case KVM_DEV_IRQ_HOST_INTX:
r = assigned_device_enable_host_intx(kvm, dev);
@@ -547,6 +515,7 @@
}
pci_reset_function(dev);
+ pci_save_state(dev);
match->assigned_dev_id = assigned_dev->assigned_dev_id;
match->host_segnr = assigned_dev->segnr;
@@ -554,12 +523,10 @@
match->host_devfn = assigned_dev->devfn;
match->flags = assigned_dev->flags;
match->dev = dev;
- spin_lock_init(&match->assigned_dev_lock);
+ spin_lock_init(&match->intx_lock);
match->irq_source_id = -1;
match->kvm = kvm;
match->ack_notifier.irq_acked = kvm_assigned_dev_ack_irq;
- INIT_WORK(&match->interrupt_work,
- kvm_assigned_dev_interrupt_work_handler);
list_add(&match->list, &kvm->arch.assigned_dev_head);
@@ -579,6 +546,7 @@
mutex_unlock(&kvm->lock);
return r;
out_list_del:
+ pci_restore_state(dev);
list_del(&match->list);
pci_release_regions(dev);
out_disable:
@@ -651,9 +619,9 @@
r = -ENOMEM;
goto msix_nr_out;
}
- adev->guest_msix_entries = kzalloc(
- sizeof(struct kvm_guest_msix_entry) *
- entry_nr->entry_nr, GFP_KERNEL);
+ adev->guest_msix_entries =
+ kzalloc(sizeof(struct msix_entry) * entry_nr->entry_nr,
+ GFP_KERNEL);
if (!adev->guest_msix_entries) {
kfree(adev->host_msix_entries);
r = -ENOMEM;
@@ -706,7 +674,7 @@
unsigned long arg)
{
void __user *argp = (void __user *)arg;
- int r = -ENOTTY;
+ int r;
switch (ioctl) {
case KVM_ASSIGN_PCI_DEVICE: {
@@ -724,7 +692,6 @@
r = -EOPNOTSUPP;
break;
}
-#ifdef KVM_CAP_ASSIGN_DEV_IRQ
case KVM_ASSIGN_DEV_IRQ: {
struct kvm_assigned_irq assigned_irq;
@@ -747,8 +714,6 @@
goto out;
break;
}
-#endif
-#ifdef KVM_CAP_DEVICE_DEASSIGNMENT
case KVM_DEASSIGN_PCI_DEVICE: {
struct kvm_assigned_pci_dev assigned_dev;
@@ -760,7 +725,6 @@
goto out;
break;
}
-#endif
#ifdef KVM_CAP_IRQ_ROUTING
case KVM_SET_GSI_ROUTING: {
struct kvm_irq_routing routing;
@@ -813,6 +777,9 @@
break;
}
#endif
+ default:
+ r = -ENOTTY;
+ break;
}
out:
return r;
diff --git a/virt/kvm/async_pf.c b/virt/kvm/async_pf.c
new file mode 100644
index 0000000..74268b4
--- /dev/null
+++ b/virt/kvm/async_pf.c
@@ -0,0 +1,216 @@
+/*
+ * kvm asynchronous fault support
+ *
+ * Copyright 2010 Red Hat, Inc.
+ *
+ * Author:
+ * Gleb Natapov <gleb@redhat.com>
+ *
+ * This file is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License
+ * as published by the Free Software Foundation.
+ *
+ * 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#include <linux/kvm_host.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/mmu_context.h>
+
+#include "async_pf.h"
+#include <trace/events/kvm.h>
+
+static struct kmem_cache *async_pf_cache;
+
+int kvm_async_pf_init(void)
+{
+ async_pf_cache = KMEM_CACHE(kvm_async_pf, 0);
+
+ if (!async_pf_cache)
+ return -ENOMEM;
+
+ return 0;
+}
+
+void kvm_async_pf_deinit(void)
+{
+ if (async_pf_cache)
+ kmem_cache_destroy(async_pf_cache);
+ async_pf_cache = NULL;
+}
+
+void kvm_async_pf_vcpu_init(struct kvm_vcpu *vcpu)
+{
+ INIT_LIST_HEAD(&vcpu->async_pf.done);
+ INIT_LIST_HEAD(&vcpu->async_pf.queue);
+ spin_lock_init(&vcpu->async_pf.lock);
+}
+
+static void async_pf_execute(struct work_struct *work)
+{
+ struct page *page = NULL;
+ struct kvm_async_pf *apf =
+ container_of(work, struct kvm_async_pf, work);
+ struct mm_struct *mm = apf->mm;
+ struct kvm_vcpu *vcpu = apf->vcpu;
+ unsigned long addr = apf->addr;
+ gva_t gva = apf->gva;
+
+ might_sleep();
+
+ use_mm(mm);
+ down_read(&mm->mmap_sem);
+ get_user_pages(current, mm, addr, 1, 1, 0, &page, NULL);
+ up_read(&mm->mmap_sem);
+ unuse_mm(mm);
+
+ spin_lock(&vcpu->async_pf.lock);
+ list_add_tail(&apf->link, &vcpu->async_pf.done);
+ apf->page = page;
+ apf->done = true;
+ spin_unlock(&vcpu->async_pf.lock);
+
+ /*
+ * apf may be freed by kvm_check_async_pf_completion() after
+ * this point
+ */
+
+ trace_kvm_async_pf_completed(addr, page, gva);
+
+ if (waitqueue_active(&vcpu->wq))
+ wake_up_interruptible(&vcpu->wq);
+
+ mmdrop(mm);
+ kvm_put_kvm(vcpu->kvm);
+}
+
+void kvm_clear_async_pf_completion_queue(struct kvm_vcpu *vcpu)
+{
+ /* cancel outstanding work queue item */
+ while (!list_empty(&vcpu->async_pf.queue)) {
+ struct kvm_async_pf *work =
+ list_entry(vcpu->async_pf.queue.next,
+ typeof(*work), queue);
+ cancel_work_sync(&work->work);
+ list_del(&work->queue);
+ if (!work->done) /* work was canceled */
+ kmem_cache_free(async_pf_cache, work);
+ }
+
+ spin_lock(&vcpu->async_pf.lock);
+ while (!list_empty(&vcpu->async_pf.done)) {
+ struct kvm_async_pf *work =
+ list_entry(vcpu->async_pf.done.next,
+ typeof(*work), link);
+ list_del(&work->link);
+ if (work->page)
+ put_page(work->page);
+ kmem_cache_free(async_pf_cache, work);
+ }
+ spin_unlock(&vcpu->async_pf.lock);
+
+ vcpu->async_pf.queued = 0;
+}
+
+void kvm_check_async_pf_completion(struct kvm_vcpu *vcpu)
+{
+ struct kvm_async_pf *work;
+
+ while (!list_empty_careful(&vcpu->async_pf.done) &&
+ kvm_arch_can_inject_async_page_present(vcpu)) {
+ spin_lock(&vcpu->async_pf.lock);
+ work = list_first_entry(&vcpu->async_pf.done, typeof(*work),
+ link);
+ list_del(&work->link);
+ spin_unlock(&vcpu->async_pf.lock);
+
+ if (work->page)
+ kvm_arch_async_page_ready(vcpu, work);
+ kvm_arch_async_page_present(vcpu, work);
+
+ list_del(&work->queue);
+ vcpu->async_pf.queued--;
+ if (work->page)
+ put_page(work->page);
+ kmem_cache_free(async_pf_cache, work);
+ }
+}
+
+int kvm_setup_async_pf(struct kvm_vcpu *vcpu, gva_t gva, gfn_t gfn,
+ struct kvm_arch_async_pf *arch)
+{
+ struct kvm_async_pf *work;
+
+ if (vcpu->async_pf.queued >= ASYNC_PF_PER_VCPU)
+ return 0;
+
+ /* setup delayed work */
+
+ /*
+ * do alloc nowait since if we are going to sleep anyway we
+ * may as well sleep faulting in page
+ */
+ work = kmem_cache_zalloc(async_pf_cache, GFP_NOWAIT);
+ if (!work)
+ return 0;
+
+ work->page = NULL;
+ work->done = false;
+ work->vcpu = vcpu;
+ work->gva = gva;
+ work->addr = gfn_to_hva(vcpu->kvm, gfn);
+ work->arch = *arch;
+ work->mm = current->mm;
+ atomic_inc(&work->mm->mm_count);
+ kvm_get_kvm(work->vcpu->kvm);
+
+ /* this can't really happen otherwise gfn_to_pfn_async
+ would succeed */
+ if (unlikely(kvm_is_error_hva(work->addr)))
+ goto retry_sync;
+
+ INIT_WORK(&work->work, async_pf_execute);
+ if (!schedule_work(&work->work))
+ goto retry_sync;
+
+ list_add_tail(&work->queue, &vcpu->async_pf.queue);
+ vcpu->async_pf.queued++;
+ kvm_arch_async_page_not_present(vcpu, work);
+ return 1;
+retry_sync:
+ kvm_put_kvm(work->vcpu->kvm);
+ mmdrop(work->mm);
+ kmem_cache_free(async_pf_cache, work);
+ return 0;
+}
+
+int kvm_async_pf_wakeup_all(struct kvm_vcpu *vcpu)
+{
+ struct kvm_async_pf *work;
+
+ if (!list_empty_careful(&vcpu->async_pf.done))
+ return 0;
+
+ work = kmem_cache_zalloc(async_pf_cache, GFP_ATOMIC);
+ if (!work)
+ return -ENOMEM;
+
+ work->page = bad_page;
+ get_page(bad_page);
+ INIT_LIST_HEAD(&work->queue); /* for list_del to work */
+
+ spin_lock(&vcpu->async_pf.lock);
+ list_add_tail(&work->link, &vcpu->async_pf.done);
+ spin_unlock(&vcpu->async_pf.lock);
+
+ vcpu->async_pf.queued++;
+ return 0;
+}
diff --git a/virt/kvm/async_pf.h b/virt/kvm/async_pf.h
new file mode 100644
index 0000000..e7ef6447
--- /dev/null
+++ b/virt/kvm/async_pf.h
@@ -0,0 +1,36 @@
+/*
+ * kvm asynchronous fault support
+ *
+ * Copyright 2010 Red Hat, Inc.
+ *
+ * Author:
+ * Gleb Natapov <gleb@redhat.com>
+ *
+ * This file is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License
+ * as published by the Free Software Foundation.
+ *
+ * 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#ifndef __KVM_ASYNC_PF_H__
+#define __KVM_ASYNC_PF_H__
+
+#ifdef CONFIG_KVM_ASYNC_PF
+int kvm_async_pf_init(void);
+void kvm_async_pf_deinit(void);
+void kvm_async_pf_vcpu_init(struct kvm_vcpu *vcpu);
+#else
+#define kvm_async_pf_init() (0)
+#define kvm_async_pf_deinit() do{}while(0)
+#define kvm_async_pf_vcpu_init(C) do{}while(0)
+#endif
+
+#endif
diff --git a/virt/kvm/eventfd.c b/virt/kvm/eventfd.c
index c1f1e3c..2ca4535 100644
--- a/virt/kvm/eventfd.c
+++ b/virt/kvm/eventfd.c
@@ -44,14 +44,19 @@
*/
struct _irqfd {
- struct kvm *kvm;
- struct eventfd_ctx *eventfd;
- int gsi;
- struct list_head list;
- poll_table pt;
- wait_queue_t wait;
- struct work_struct inject;
- struct work_struct shutdown;
+ /* Used for MSI fast-path */
+ struct kvm *kvm;
+ wait_queue_t wait;
+ /* Update side is protected by irqfds.lock */
+ struct kvm_kernel_irq_routing_entry __rcu *irq_entry;
+ /* Used for level IRQ fast-path */
+ int gsi;
+ struct work_struct inject;
+ /* Used for setup/shutdown */
+ struct eventfd_ctx *eventfd;
+ struct list_head list;
+ poll_table pt;
+ struct work_struct shutdown;
};
static struct workqueue_struct *irqfd_cleanup_wq;
@@ -125,14 +130,22 @@
{
struct _irqfd *irqfd = container_of(wait, struct _irqfd, wait);
unsigned long flags = (unsigned long)key;
+ struct kvm_kernel_irq_routing_entry *irq;
+ struct kvm *kvm = irqfd->kvm;
- if (flags & POLLIN)
+ if (flags & POLLIN) {
+ rcu_read_lock();
+ irq = rcu_dereference(irqfd->irq_entry);
/* An event has been signaled, inject an interrupt */
- schedule_work(&irqfd->inject);
+ if (irq)
+ kvm_set_msi(irq, kvm, KVM_USERSPACE_IRQ_SOURCE_ID, 1);
+ else
+ schedule_work(&irqfd->inject);
+ rcu_read_unlock();
+ }
if (flags & POLLHUP) {
/* The eventfd is closing, detach from KVM */
- struct kvm *kvm = irqfd->kvm;
unsigned long flags;
spin_lock_irqsave(&kvm->irqfds.lock, flags);
@@ -163,9 +176,31 @@
add_wait_queue(wqh, &irqfd->wait);
}
+/* Must be called under irqfds.lock */
+static void irqfd_update(struct kvm *kvm, struct _irqfd *irqfd,
+ struct kvm_irq_routing_table *irq_rt)
+{
+ struct kvm_kernel_irq_routing_entry *e;
+ struct hlist_node *n;
+
+ if (irqfd->gsi >= irq_rt->nr_rt_entries) {
+ rcu_assign_pointer(irqfd->irq_entry, NULL);
+ return;
+ }
+
+ hlist_for_each_entry(e, n, &irq_rt->map[irqfd->gsi], link) {
+ /* Only fast-path MSI. */
+ if (e->type == KVM_IRQ_ROUTING_MSI)
+ rcu_assign_pointer(irqfd->irq_entry, e);
+ else
+ rcu_assign_pointer(irqfd->irq_entry, NULL);
+ }
+}
+
static int
kvm_irqfd_assign(struct kvm *kvm, int fd, int gsi)
{
+ struct kvm_irq_routing_table *irq_rt;
struct _irqfd *irqfd, *tmp;
struct file *file = NULL;
struct eventfd_ctx *eventfd = NULL;
@@ -215,6 +250,10 @@
goto fail;
}
+ irq_rt = rcu_dereference_protected(kvm->irq_routing,
+ lockdep_is_held(&kvm->irqfds.lock));
+ irqfd_update(kvm, irqfd, irq_rt);
+
events = file->f_op->poll(file, &irqfd->pt);
list_add_tail(&irqfd->list, &kvm->irqfds.items);
@@ -271,8 +310,17 @@
spin_lock_irq(&kvm->irqfds.lock);
list_for_each_entry_safe(irqfd, tmp, &kvm->irqfds.items, list) {
- if (irqfd->eventfd == eventfd && irqfd->gsi == gsi)
+ if (irqfd->eventfd == eventfd && irqfd->gsi == gsi) {
+ /*
+ * This rcu_assign_pointer is needed for when
+ * another thread calls kvm_irqfd_update before
+ * we flush workqueue below.
+ * It is paired with synchronize_rcu done by caller
+ * of that function.
+ */
+ rcu_assign_pointer(irqfd->irq_entry, NULL);
irqfd_deactivate(irqfd);
+ }
}
spin_unlock_irq(&kvm->irqfds.lock);
@@ -322,6 +370,25 @@
}
/*
+ * Change irq_routing and irqfd.
+ * Caller must invoke synchronize_rcu afterwards.
+ */
+void kvm_irq_routing_update(struct kvm *kvm,
+ struct kvm_irq_routing_table *irq_rt)
+{
+ struct _irqfd *irqfd;
+
+ spin_lock_irq(&kvm->irqfds.lock);
+
+ rcu_assign_pointer(kvm->irq_routing, irq_rt);
+
+ list_for_each_entry(irqfd, &kvm->irqfds.items, list)
+ irqfd_update(kvm, irqfd, irq_rt);
+
+ spin_unlock_irq(&kvm->irqfds.lock);
+}
+
+/*
* create a host-wide workqueue for issuing deferred shutdown requests
* aggregated from all vm* instances. We need our own isolated single-thread
* queue to prevent deadlock against flushing the normal work-queue.
diff --git a/virt/kvm/irq_comm.c b/virt/kvm/irq_comm.c
index 8edca91..9f614b4 100644
--- a/virt/kvm/irq_comm.c
+++ b/virt/kvm/irq_comm.c
@@ -114,8 +114,8 @@
return r;
}
-static int kvm_set_msi(struct kvm_kernel_irq_routing_entry *e,
- struct kvm *kvm, int irq_source_id, int level)
+int kvm_set_msi(struct kvm_kernel_irq_routing_entry *e,
+ struct kvm *kvm, int irq_source_id, int level)
{
struct kvm_lapic_irq irq;
@@ -409,8 +409,9 @@
mutex_lock(&kvm->irq_lock);
old = kvm->irq_routing;
- rcu_assign_pointer(kvm->irq_routing, new);
+ kvm_irq_routing_update(kvm, new);
mutex_unlock(&kvm->irq_lock);
+
synchronize_rcu();
new = old;
diff --git a/virt/kvm/kvm_main.c b/virt/kvm/kvm_main.c
index 5225052..7f68625 100644
--- a/virt/kvm/kvm_main.c
+++ b/virt/kvm/kvm_main.c
@@ -55,6 +55,7 @@
#include <asm-generic/bitops/le.h>
#include "coalesced_mmio.h"
+#include "async_pf.h"
#define CREATE_TRACE_POINTS
#include <trace/events/kvm.h>
@@ -89,7 +90,8 @@
static void kvm_io_bus_destroy(struct kvm_io_bus *bus);
-static bool kvm_rebooting;
+bool kvm_rebooting;
+EXPORT_SYMBOL_GPL(kvm_rebooting);
static bool largepages_enabled = true;
@@ -167,8 +169,12 @@
void kvm_flush_remote_tlbs(struct kvm *kvm)
{
+ int dirty_count = kvm->tlbs_dirty;
+
+ smp_mb();
if (make_all_cpus_request(kvm, KVM_REQ_TLB_FLUSH))
++kvm->stat.remote_tlb_flush;
+ cmpxchg(&kvm->tlbs_dirty, dirty_count, 0);
}
void kvm_reload_remote_mmus(struct kvm *kvm)
@@ -186,6 +192,7 @@
vcpu->kvm = kvm;
vcpu->vcpu_id = id;
init_waitqueue_head(&vcpu->wq);
+ kvm_async_pf_vcpu_init(vcpu);
page = alloc_page(GFP_KERNEL | __GFP_ZERO);
if (!page) {
@@ -247,7 +254,7 @@
idx = srcu_read_lock(&kvm->srcu);
spin_lock(&kvm->mmu_lock);
kvm->mmu_notifier_seq++;
- need_tlb_flush = kvm_unmap_hva(kvm, address);
+ need_tlb_flush = kvm_unmap_hva(kvm, address) | kvm->tlbs_dirty;
spin_unlock(&kvm->mmu_lock);
srcu_read_unlock(&kvm->srcu, idx);
@@ -291,6 +298,7 @@
kvm->mmu_notifier_count++;
for (; start < end; start += PAGE_SIZE)
need_tlb_flush |= kvm_unmap_hva(kvm, start);
+ need_tlb_flush |= kvm->tlbs_dirty;
spin_unlock(&kvm->mmu_lock);
srcu_read_unlock(&kvm->srcu, idx);
@@ -381,11 +389,15 @@
static struct kvm *kvm_create_vm(void)
{
- int r = 0, i;
- struct kvm *kvm = kvm_arch_create_vm();
+ int r, i;
+ struct kvm *kvm = kvm_arch_alloc_vm();
- if (IS_ERR(kvm))
- goto out;
+ if (!kvm)
+ return ERR_PTR(-ENOMEM);
+
+ r = kvm_arch_init_vm(kvm);
+ if (r)
+ goto out_err_nodisable;
r = hardware_enable_all();
if (r)
@@ -399,23 +411,19 @@
r = -ENOMEM;
kvm->memslots = kzalloc(sizeof(struct kvm_memslots), GFP_KERNEL);
if (!kvm->memslots)
- goto out_err;
+ goto out_err_nosrcu;
if (init_srcu_struct(&kvm->srcu))
- goto out_err;
+ goto out_err_nosrcu;
for (i = 0; i < KVM_NR_BUSES; i++) {
kvm->buses[i] = kzalloc(sizeof(struct kvm_io_bus),
GFP_KERNEL);
- if (!kvm->buses[i]) {
- cleanup_srcu_struct(&kvm->srcu);
+ if (!kvm->buses[i])
goto out_err;
- }
}
r = kvm_init_mmu_notifier(kvm);
- if (r) {
- cleanup_srcu_struct(&kvm->srcu);
+ if (r)
goto out_err;
- }
kvm->mm = current->mm;
atomic_inc(&kvm->mm->mm_count);
@@ -429,19 +437,35 @@
spin_lock(&kvm_lock);
list_add(&kvm->vm_list, &vm_list);
spin_unlock(&kvm_lock);
-out:
+
return kvm;
out_err:
+ cleanup_srcu_struct(&kvm->srcu);
+out_err_nosrcu:
hardware_disable_all();
out_err_nodisable:
for (i = 0; i < KVM_NR_BUSES; i++)
kfree(kvm->buses[i]);
kfree(kvm->memslots);
- kfree(kvm);
+ kvm_arch_free_vm(kvm);
return ERR_PTR(r);
}
+static void kvm_destroy_dirty_bitmap(struct kvm_memory_slot *memslot)
+{
+ if (!memslot->dirty_bitmap)
+ return;
+
+ if (2 * kvm_dirty_bitmap_bytes(memslot) > PAGE_SIZE)
+ vfree(memslot->dirty_bitmap_head);
+ else
+ kfree(memslot->dirty_bitmap_head);
+
+ memslot->dirty_bitmap = NULL;
+ memslot->dirty_bitmap_head = NULL;
+}
+
/*
* Free any memory in @free but not in @dont.
*/
@@ -454,7 +478,7 @@
vfree(free->rmap);
if (!dont || free->dirty_bitmap != dont->dirty_bitmap)
- vfree(free->dirty_bitmap);
+ kvm_destroy_dirty_bitmap(free);
for (i = 0; i < KVM_NR_PAGE_SIZES - 1; ++i) {
@@ -465,7 +489,6 @@
}
free->npages = 0;
- free->dirty_bitmap = NULL;
free->rmap = NULL;
}
@@ -499,6 +522,9 @@
kvm_arch_flush_shadow(kvm);
#endif
kvm_arch_destroy_vm(kvm);
+ kvm_free_physmem(kvm);
+ cleanup_srcu_struct(&kvm->srcu);
+ kvm_arch_free_vm(kvm);
hardware_disable_all();
mmdrop(mm);
}
@@ -528,6 +554,27 @@
}
/*
+ * Allocation size is twice as large as the actual dirty bitmap size.
+ * This makes it possible to do double buffering: see x86's
+ * kvm_vm_ioctl_get_dirty_log().
+ */
+static int kvm_create_dirty_bitmap(struct kvm_memory_slot *memslot)
+{
+ unsigned long dirty_bytes = 2 * kvm_dirty_bitmap_bytes(memslot);
+
+ if (dirty_bytes > PAGE_SIZE)
+ memslot->dirty_bitmap = vzalloc(dirty_bytes);
+ else
+ memslot->dirty_bitmap = kzalloc(dirty_bytes, GFP_KERNEL);
+
+ if (!memslot->dirty_bitmap)
+ return -ENOMEM;
+
+ memslot->dirty_bitmap_head = memslot->dirty_bitmap;
+ return 0;
+}
+
+/*
* Allocate some memory and give it an address in the guest physical address
* space.
*
@@ -604,13 +651,11 @@
/* Allocate if a slot is being created */
#ifndef CONFIG_S390
if (npages && !new.rmap) {
- new.rmap = vmalloc(npages * sizeof(*new.rmap));
+ new.rmap = vzalloc(npages * sizeof(*new.rmap));
if (!new.rmap)
goto out_free;
- memset(new.rmap, 0, npages * sizeof(*new.rmap));
-
new.user_alloc = user_alloc;
new.userspace_addr = mem->userspace_addr;
}
@@ -633,14 +678,11 @@
>> KVM_HPAGE_GFN_SHIFT(level));
lpages -= base_gfn >> KVM_HPAGE_GFN_SHIFT(level);
- new.lpage_info[i] = vmalloc(lpages * sizeof(*new.lpage_info[i]));
+ new.lpage_info[i] = vzalloc(lpages * sizeof(*new.lpage_info[i]));
if (!new.lpage_info[i])
goto out_free;
- memset(new.lpage_info[i], 0,
- lpages * sizeof(*new.lpage_info[i]));
-
if (base_gfn & (KVM_PAGES_PER_HPAGE(level) - 1))
new.lpage_info[i][0].write_count = 1;
if ((base_gfn+npages) & (KVM_PAGES_PER_HPAGE(level) - 1))
@@ -661,12 +703,8 @@
/* Allocate page dirty bitmap if needed */
if ((new.flags & KVM_MEM_LOG_DIRTY_PAGES) && !new.dirty_bitmap) {
- unsigned long dirty_bytes = kvm_dirty_bitmap_bytes(&new);
-
- new.dirty_bitmap = vmalloc(dirty_bytes);
- if (!new.dirty_bitmap)
+ if (kvm_create_dirty_bitmap(&new) < 0)
goto out_free;
- memset(new.dirty_bitmap, 0, dirty_bytes);
/* destroy any largepage mappings for dirty tracking */
if (old.npages)
flush_shadow = 1;
@@ -685,6 +723,7 @@
memcpy(slots, kvm->memslots, sizeof(struct kvm_memslots));
if (mem->slot >= slots->nmemslots)
slots->nmemslots = mem->slot + 1;
+ slots->generation++;
slots->memslots[mem->slot].flags |= KVM_MEMSLOT_INVALID;
old_memslots = kvm->memslots;
@@ -719,6 +758,7 @@
memcpy(slots, kvm->memslots, sizeof(struct kvm_memslots));
if (mem->slot >= slots->nmemslots)
slots->nmemslots = mem->slot + 1;
+ slots->generation++;
/* actual memory is freed via old in kvm_free_physmem_slot below */
if (!npages) {
@@ -849,10 +889,10 @@
}
EXPORT_SYMBOL_GPL(kvm_is_error_hva);
-struct kvm_memory_slot *gfn_to_memslot(struct kvm *kvm, gfn_t gfn)
+static struct kvm_memory_slot *__gfn_to_memslot(struct kvm_memslots *slots,
+ gfn_t gfn)
{
int i;
- struct kvm_memslots *slots = kvm_memslots(kvm);
for (i = 0; i < slots->nmemslots; ++i) {
struct kvm_memory_slot *memslot = &slots->memslots[i];
@@ -863,6 +903,11 @@
}
return NULL;
}
+
+struct kvm_memory_slot *gfn_to_memslot(struct kvm *kvm, gfn_t gfn)
+{
+ return __gfn_to_memslot(kvm_memslots(kvm), gfn);
+}
EXPORT_SYMBOL_GPL(gfn_to_memslot);
int kvm_is_visible_gfn(struct kvm *kvm, gfn_t gfn)
@@ -925,12 +970,9 @@
return memslot - slots->memslots;
}
-static unsigned long gfn_to_hva_many(struct kvm *kvm, gfn_t gfn,
+static unsigned long gfn_to_hva_many(struct kvm_memory_slot *slot, gfn_t gfn,
gfn_t *nr_pages)
{
- struct kvm_memory_slot *slot;
-
- slot = gfn_to_memslot(kvm, gfn);
if (!slot || slot->flags & KVM_MEMSLOT_INVALID)
return bad_hva();
@@ -942,28 +984,61 @@
unsigned long gfn_to_hva(struct kvm *kvm, gfn_t gfn)
{
- return gfn_to_hva_many(kvm, gfn, NULL);
+ return gfn_to_hva_many(gfn_to_memslot(kvm, gfn), gfn, NULL);
}
EXPORT_SYMBOL_GPL(gfn_to_hva);
-static pfn_t hva_to_pfn(struct kvm *kvm, unsigned long addr, bool atomic)
+static pfn_t get_fault_pfn(void)
+{
+ get_page(fault_page);
+ return fault_pfn;
+}
+
+static pfn_t hva_to_pfn(struct kvm *kvm, unsigned long addr, bool atomic,
+ bool *async, bool write_fault, bool *writable)
{
struct page *page[1];
- int npages;
+ int npages = 0;
pfn_t pfn;
- if (atomic)
+ /* we can do it either atomically or asynchronously, not both */
+ BUG_ON(atomic && async);
+
+ BUG_ON(!write_fault && !writable);
+
+ if (writable)
+ *writable = true;
+
+ if (atomic || async)
npages = __get_user_pages_fast(addr, 1, 1, page);
- else {
+
+ if (unlikely(npages != 1) && !atomic) {
might_sleep();
- npages = get_user_pages_fast(addr, 1, 1, page);
+
+ if (writable)
+ *writable = write_fault;
+
+ npages = get_user_pages_fast(addr, 1, write_fault, page);
+
+ /* map read fault as writable if possible */
+ if (unlikely(!write_fault) && npages == 1) {
+ struct page *wpage[1];
+
+ npages = __get_user_pages_fast(addr, 1, 1, wpage);
+ if (npages == 1) {
+ *writable = true;
+ put_page(page[0]);
+ page[0] = wpage[0];
+ }
+ npages = 1;
+ }
}
if (unlikely(npages != 1)) {
struct vm_area_struct *vma;
if (atomic)
- goto return_fault_page;
+ return get_fault_pfn();
down_read(¤t->mm->mmap_sem);
if (is_hwpoison_address(addr)) {
@@ -972,19 +1047,20 @@
return page_to_pfn(hwpoison_page);
}
- vma = find_vma(current->mm, addr);
+ vma = find_vma_intersection(current->mm, addr, addr+1);
- if (vma == NULL || addr < vma->vm_start ||
- !(vma->vm_flags & VM_PFNMAP)) {
- up_read(¤t->mm->mmap_sem);
-return_fault_page:
- get_page(fault_page);
- return page_to_pfn(fault_page);
+ if (vma == NULL)
+ pfn = get_fault_pfn();
+ else if ((vma->vm_flags & VM_PFNMAP)) {
+ pfn = ((addr - vma->vm_start) >> PAGE_SHIFT) +
+ vma->vm_pgoff;
+ BUG_ON(!kvm_is_mmio_pfn(pfn));
+ } else {
+ if (async && (vma->vm_flags & VM_WRITE))
+ *async = true;
+ pfn = get_fault_pfn();
}
-
- pfn = ((addr - vma->vm_start) >> PAGE_SHIFT) + vma->vm_pgoff;
up_read(¤t->mm->mmap_sem);
- BUG_ON(!kvm_is_mmio_pfn(pfn));
} else
pfn = page_to_pfn(page[0]);
@@ -993,40 +1069,58 @@
pfn_t hva_to_pfn_atomic(struct kvm *kvm, unsigned long addr)
{
- return hva_to_pfn(kvm, addr, true);
+ return hva_to_pfn(kvm, addr, true, NULL, true, NULL);
}
EXPORT_SYMBOL_GPL(hva_to_pfn_atomic);
-static pfn_t __gfn_to_pfn(struct kvm *kvm, gfn_t gfn, bool atomic)
+static pfn_t __gfn_to_pfn(struct kvm *kvm, gfn_t gfn, bool atomic, bool *async,
+ bool write_fault, bool *writable)
{
unsigned long addr;
+ if (async)
+ *async = false;
+
addr = gfn_to_hva(kvm, gfn);
if (kvm_is_error_hva(addr)) {
get_page(bad_page);
return page_to_pfn(bad_page);
}
- return hva_to_pfn(kvm, addr, atomic);
+ return hva_to_pfn(kvm, addr, atomic, async, write_fault, writable);
}
pfn_t gfn_to_pfn_atomic(struct kvm *kvm, gfn_t gfn)
{
- return __gfn_to_pfn(kvm, gfn, true);
+ return __gfn_to_pfn(kvm, gfn, true, NULL, true, NULL);
}
EXPORT_SYMBOL_GPL(gfn_to_pfn_atomic);
+pfn_t gfn_to_pfn_async(struct kvm *kvm, gfn_t gfn, bool *async,
+ bool write_fault, bool *writable)
+{
+ return __gfn_to_pfn(kvm, gfn, false, async, write_fault, writable);
+}
+EXPORT_SYMBOL_GPL(gfn_to_pfn_async);
+
pfn_t gfn_to_pfn(struct kvm *kvm, gfn_t gfn)
{
- return __gfn_to_pfn(kvm, gfn, false);
+ return __gfn_to_pfn(kvm, gfn, false, NULL, true, NULL);
}
EXPORT_SYMBOL_GPL(gfn_to_pfn);
+pfn_t gfn_to_pfn_prot(struct kvm *kvm, gfn_t gfn, bool write_fault,
+ bool *writable)
+{
+ return __gfn_to_pfn(kvm, gfn, false, NULL, write_fault, writable);
+}
+EXPORT_SYMBOL_GPL(gfn_to_pfn_prot);
+
pfn_t gfn_to_pfn_memslot(struct kvm *kvm,
struct kvm_memory_slot *slot, gfn_t gfn)
{
unsigned long addr = gfn_to_hva_memslot(slot, gfn);
- return hva_to_pfn(kvm, addr, false);
+ return hva_to_pfn(kvm, addr, false, NULL, true, NULL);
}
int gfn_to_page_many_atomic(struct kvm *kvm, gfn_t gfn, struct page **pages,
@@ -1035,7 +1129,7 @@
unsigned long addr;
gfn_t entry;
- addr = gfn_to_hva_many(kvm, gfn, &entry);
+ addr = gfn_to_hva_many(gfn_to_memslot(kvm, gfn), gfn, &entry);
if (kvm_is_error_hva(addr))
return -1;
@@ -1219,9 +1313,51 @@
return 0;
}
+int kvm_gfn_to_hva_cache_init(struct kvm *kvm, struct gfn_to_hva_cache *ghc,
+ gpa_t gpa)
+{
+ struct kvm_memslots *slots = kvm_memslots(kvm);
+ int offset = offset_in_page(gpa);
+ gfn_t gfn = gpa >> PAGE_SHIFT;
+
+ ghc->gpa = gpa;
+ ghc->generation = slots->generation;
+ ghc->memslot = __gfn_to_memslot(slots, gfn);
+ ghc->hva = gfn_to_hva_many(ghc->memslot, gfn, NULL);
+ if (!kvm_is_error_hva(ghc->hva))
+ ghc->hva += offset;
+ else
+ return -EFAULT;
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(kvm_gfn_to_hva_cache_init);
+
+int kvm_write_guest_cached(struct kvm *kvm, struct gfn_to_hva_cache *ghc,
+ void *data, unsigned long len)
+{
+ struct kvm_memslots *slots = kvm_memslots(kvm);
+ int r;
+
+ if (slots->generation != ghc->generation)
+ kvm_gfn_to_hva_cache_init(kvm, ghc, ghc->gpa);
+
+ if (kvm_is_error_hva(ghc->hva))
+ return -EFAULT;
+
+ r = copy_to_user((void __user *)ghc->hva, data, len);
+ if (r)
+ return -EFAULT;
+ mark_page_dirty_in_slot(kvm, ghc->memslot, ghc->gpa >> PAGE_SHIFT);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(kvm_write_guest_cached);
+
int kvm_clear_guest_page(struct kvm *kvm, gfn_t gfn, int offset, int len)
{
- return kvm_write_guest_page(kvm, gfn, empty_zero_page, offset, len);
+ return kvm_write_guest_page(kvm, gfn, (const void *) empty_zero_page,
+ offset, len);
}
EXPORT_SYMBOL_GPL(kvm_clear_guest_page);
@@ -1244,11 +1380,9 @@
}
EXPORT_SYMBOL_GPL(kvm_clear_guest);
-void mark_page_dirty(struct kvm *kvm, gfn_t gfn)
+void mark_page_dirty_in_slot(struct kvm *kvm, struct kvm_memory_slot *memslot,
+ gfn_t gfn)
{
- struct kvm_memory_slot *memslot;
-
- memslot = gfn_to_memslot(kvm, gfn);
if (memslot && memslot->dirty_bitmap) {
unsigned long rel_gfn = gfn - memslot->base_gfn;
@@ -1256,6 +1390,14 @@
}
}
+void mark_page_dirty(struct kvm *kvm, gfn_t gfn)
+{
+ struct kvm_memory_slot *memslot;
+
+ memslot = gfn_to_memslot(kvm, gfn);
+ mark_page_dirty_in_slot(kvm, memslot, gfn);
+}
+
/*
* The vCPU has executed a HLT instruction with in-kernel mode enabled.
*/
@@ -1457,6 +1599,7 @@
if (arg)
goto out;
r = kvm_arch_vcpu_ioctl_run(vcpu, vcpu->run);
+ trace_kvm_userspace_exit(vcpu->run->exit_reason, r);
break;
case KVM_GET_REGS: {
struct kvm_regs *kvm_regs;
@@ -1824,7 +1967,7 @@
static int kvm_dev_ioctl_create_vm(void)
{
- int fd, r;
+ int r;
struct kvm *kvm;
kvm = kvm_create_vm();
@@ -1837,11 +1980,11 @@
return r;
}
#endif
- fd = anon_inode_getfd("kvm-vm", &kvm_vm_fops, kvm, O_RDWR);
- if (fd < 0)
+ r = anon_inode_getfd("kvm-vm", &kvm_vm_fops, kvm, O_RDWR);
+ if (r < 0)
kvm_put_kvm(kvm);
- return fd;
+ return r;
}
static long kvm_dev_ioctl_check_extension_generic(long arg)
@@ -1922,7 +2065,7 @@
&kvm_chardev_ops,
};
-static void hardware_enable(void *junk)
+static void hardware_enable_nolock(void *junk)
{
int cpu = raw_smp_processor_id();
int r;
@@ -1942,7 +2085,14 @@
}
}
-static void hardware_disable(void *junk)
+static void hardware_enable(void *junk)
+{
+ spin_lock(&kvm_lock);
+ hardware_enable_nolock(junk);
+ spin_unlock(&kvm_lock);
+}
+
+static void hardware_disable_nolock(void *junk)
{
int cpu = raw_smp_processor_id();
@@ -1952,13 +2102,20 @@
kvm_arch_hardware_disable(NULL);
}
+static void hardware_disable(void *junk)
+{
+ spin_lock(&kvm_lock);
+ hardware_disable_nolock(junk);
+ spin_unlock(&kvm_lock);
+}
+
static void hardware_disable_all_nolock(void)
{
BUG_ON(!kvm_usage_count);
kvm_usage_count--;
if (!kvm_usage_count)
- on_each_cpu(hardware_disable, NULL, 1);
+ on_each_cpu(hardware_disable_nolock, NULL, 1);
}
static void hardware_disable_all(void)
@@ -1977,7 +2134,7 @@
kvm_usage_count++;
if (kvm_usage_count == 1) {
atomic_set(&hardware_enable_failed, 0);
- on_each_cpu(hardware_enable, NULL, 1);
+ on_each_cpu(hardware_enable_nolock, NULL, 1);
if (atomic_read(&hardware_enable_failed)) {
hardware_disable_all_nolock();
@@ -2008,27 +2165,19 @@
case CPU_STARTING:
printk(KERN_INFO "kvm: enabling virtualization on CPU%d\n",
cpu);
- spin_lock(&kvm_lock);
hardware_enable(NULL);
- spin_unlock(&kvm_lock);
break;
}
return NOTIFY_OK;
}
-asmlinkage void kvm_handle_fault_on_reboot(void)
+asmlinkage void kvm_spurious_fault(void)
{
- if (kvm_rebooting) {
- /* spin while reset goes on */
- local_irq_enable();
- while (true)
- cpu_relax();
- }
/* Fault while not rebooting. We want the trace. */
BUG();
}
-EXPORT_SYMBOL_GPL(kvm_handle_fault_on_reboot);
+EXPORT_SYMBOL_GPL(kvm_spurious_fault);
static int kvm_reboot(struct notifier_block *notifier, unsigned long val,
void *v)
@@ -2041,7 +2190,7 @@
*/
printk(KERN_INFO "kvm: exiting hardware virtualization\n");
kvm_rebooting = true;
- on_each_cpu(hardware_disable, NULL, 1);
+ on_each_cpu(hardware_disable_nolock, NULL, 1);
return NOTIFY_OK;
}
@@ -2211,7 +2360,7 @@
static int kvm_suspend(struct sys_device *dev, pm_message_t state)
{
if (kvm_usage_count)
- hardware_disable(NULL);
+ hardware_disable_nolock(NULL);
return 0;
}
@@ -2219,7 +2368,7 @@
{
if (kvm_usage_count) {
WARN_ON(spin_is_locked(&kvm_lock));
- hardware_enable(NULL);
+ hardware_enable_nolock(NULL);
}
return 0;
}
@@ -2336,6 +2485,10 @@
goto out_free_5;
}
+ r = kvm_async_pf_init();
+ if (r)
+ goto out_free;
+
kvm_chardev_ops.owner = module;
kvm_vm_fops.owner = module;
kvm_vcpu_fops.owner = module;
@@ -2343,7 +2496,7 @@
r = misc_register(&kvm_dev);
if (r) {
printk(KERN_ERR "kvm: misc device register failed\n");
- goto out_free;
+ goto out_unreg;
}
kvm_preempt_ops.sched_in = kvm_sched_in;
@@ -2353,6 +2506,8 @@
return 0;
+out_unreg:
+ kvm_async_pf_deinit();
out_free:
kmem_cache_destroy(kvm_vcpu_cache);
out_free_5:
@@ -2385,11 +2540,12 @@
kvm_exit_debug();
misc_deregister(&kvm_dev);
kmem_cache_destroy(kvm_vcpu_cache);
+ kvm_async_pf_deinit();
sysdev_unregister(&kvm_sysdev);
sysdev_class_unregister(&kvm_sysdev_class);
unregister_reboot_notifier(&kvm_reboot_notifier);
unregister_cpu_notifier(&kvm_cpu_notifier);
- on_each_cpu(hardware_disable, NULL, 1);
+ on_each_cpu(hardware_disable_nolock, NULL, 1);
kvm_arch_hardware_unsetup();
kvm_arch_exit();
free_cpumask_var(cpus_hardware_enabled);