Merge branch 'for-linus' of git://git.kernel.dk/linux-block
* 'for-linus' of git://git.kernel.dk/linux-block: (23 commits)
Revert "cfq: Remove special treatment for metadata rqs."
block: fix flush machinery for stacking drivers with differring flush flags
block: improve rq_affinity placement
blktrace: add FLUSH/FUA support
Move some REQ flags to the common bio/request area
allow blk_flush_policy to return REQ_FSEQ_DATA independent of *FLUSH
xen/blkback: Make description more obvious.
cfq-iosched: Add documentation about idling
block: Make rq_affinity = 1 work as expected
block: swim3: fix unterminated of_device_id table
block/genhd.c: remove useless cast in diskstats_show()
drivers/cdrom/cdrom.c: relax check on dvd manufacturer value
drivers/block/drbd/drbd_nl.c: use bitmap_parse instead of __bitmap_parse
bsg-lib: add module.h include
cfq-iosched: Reduce linked group count upon group destruction
blk-throttle: correctly determine sync bio
loop: fix deadlock when sysfs and LOOP_CLR_FD race against each other
loop: add BLK_DEV_LOOP_MIN_COUNT=%i to allow distros 0 pre-allocated loop devices
loop: add management interface for on-demand device allocation
loop: replace linked list of allocated devices with an idr index
...
diff --git a/Documentation/00-INDEX b/Documentation/00-INDEX
index 1f89424..65bbd26 100644
--- a/Documentation/00-INDEX
+++ b/Documentation/00-INDEX
@@ -272,6 +272,8 @@
- how to get printk format specifiers right
prio_tree.txt
- info on radix-priority-search-tree use for indexing vmas.
+ramoops.txt
+ - documentation of the ramoops oops/panic logging module.
rbtree.txt
- info on what red-black trees are and what they are for.
robust-futex-ABI.txt
diff --git a/Documentation/ABI/testing/pstore b/Documentation/ABI/testing/pstore
index ddf451e..ff1df4e 100644
--- a/Documentation/ABI/testing/pstore
+++ b/Documentation/ABI/testing/pstore
@@ -39,3 +39,9 @@
multiple) files based on the record size of the underlying
persistent storage until at least this amount is reached.
Default is 10 Kbytes.
+
+ Pstore only supports one backend at a time. If multiple
+ backends are available, the preferred backend may be
+ set by passing the pstore.backend= argument to the kernel at
+ boot time.
+
diff --git a/Documentation/ABI/testing/sysfs-platform-ideapad-laptop b/Documentation/ABI/testing/sysfs-platform-ideapad-laptop
index 807fca2..ff53183 100644
--- a/Documentation/ABI/testing/sysfs-platform-ideapad-laptop
+++ b/Documentation/ABI/testing/sysfs-platform-ideapad-laptop
@@ -4,3 +4,20 @@
Contact: "Ike Panhc <ike.pan@canonical.com>"
Description:
Control the power of camera module. 1 means on, 0 means off.
+
+What: /sys/devices/platform/ideapad/cfg
+Date: Jun 2011
+KernelVersion: 3.1
+Contact: "Ike Panhc <ike.pan@canonical.com>"
+Description:
+ Ideapad capability bits.
+ Bit 8-10: 1 - Intel graphic only
+ 2 - ATI graphic only
+ 3 - Nvidia graphic only
+ 4 - Intel and ATI graphic
+ 5 - Intel and Nvidia graphic
+ Bit 16: Bluetooth exist (1 for exist)
+ Bit 17: 3G exist (1 for exist)
+ Bit 18: Wifi exist (1 for exist)
+ Bit 19: Camera exist (1 for exist)
+
diff --git a/Documentation/CodingStyle b/Documentation/CodingStyle
index fa6e25b..c940239 100644
--- a/Documentation/CodingStyle
+++ b/Documentation/CodingStyle
@@ -80,22 +80,13 @@
The limit on the length of lines is 80 columns and this is a strongly
preferred limit.
-Statements longer than 80 columns will be broken into sensible chunks.
-Descendants are always substantially shorter than the parent and are placed
-substantially to the right. The same applies to function headers with a long
-argument list. Long strings are as well broken into shorter strings. The
-only exception to this is where exceeding 80 columns significantly increases
-readability and does not hide information.
+Statements longer than 80 columns will be broken into sensible chunks, unless
+exceeding 80 columns significantly increases readability and does not hide
+information. Descendants are always substantially shorter than the parent and
+are placed substantially to the right. The same applies to function headers
+with a long argument list. However, never break user-visible strings such as
+printk messages, because that breaks the ability to grep for them.
-void fun(int a, int b, int c)
-{
- if (condition)
- printk(KERN_WARNING "Warning this is a long printk with "
- "3 parameters a: %u b: %u "
- "c: %u \n", a, b, c);
- else
- next_statement;
-}
Chapter 3: Placing Braces and Spaces
diff --git a/Documentation/PCI/MSI-HOWTO.txt b/Documentation/PCI/MSI-HOWTO.txt
index 3f5e0b0..53e6fca 100644
--- a/Documentation/PCI/MSI-HOWTO.txt
+++ b/Documentation/PCI/MSI-HOWTO.txt
@@ -45,7 +45,7 @@
bridges). In order to ensure that all the data has arrived in memory,
the interrupt handler must read a register on the device which raised
the interrupt. PCI transaction ordering rules require that all the data
-arrives in memory before the value can be returned from the register.
+arrive in memory before the value may be returned from the register.
Using MSIs avoids this problem as the interrupt-generating write cannot
pass the data writes, so by the time the interrupt is raised, the driver
knows that all the data has arrived in memory.
@@ -86,13 +86,13 @@
int pci_enable_msi(struct pci_dev *dev)
-A successful call will allocate ONE interrupt to the device, regardless
-of how many MSIs the device supports. The device will be switched from
+A successful call allocates ONE interrupt to the device, regardless
+of how many MSIs the device supports. The device is switched from
pin-based interrupt mode to MSI mode. The dev->irq number is changed
-to a new number which represents the message signaled interrupt.
-This function should be called before the driver calls request_irq()
-since enabling MSIs disables the pin-based IRQ and the driver will not
-receive interrupts on the old interrupt.
+to a new number which represents the message signaled interrupt;
+consequently, this function should be called before the driver calls
+request_irq(), because an MSI is delivered via a vector that is
+different from the vector of a pin-based interrupt.
4.2.2 pci_enable_msi_block
@@ -111,20 +111,20 @@
If this function returns a negative number, it indicates an error and
the driver should not attempt to request any more MSI interrupts for
-this device. If this function returns a positive number, it will be
-less than 'count' and indicate the number of interrupts that could have
-been allocated. In neither case will the irq value have been
-updated, nor will the device have been switched into MSI mode.
+this device. If this function returns a positive number, it is
+less than 'count' and indicates the number of interrupts that could have
+been allocated. In neither case is the irq value updated or the device
+switched into MSI mode.
The device driver must decide what action to take if
-pci_enable_msi_block() returns a value less than the number asked for.
-Some devices can make use of fewer interrupts than the maximum they
-request; in this case the driver should call pci_enable_msi_block()
+pci_enable_msi_block() returns a value less than the number requested.
+For instance, the driver could still make use of fewer interrupts;
+in this case the driver should call pci_enable_msi_block()
again. Note that it is not guaranteed to succeed, even when the
'count' has been reduced to the value returned from a previous call to
pci_enable_msi_block(). This is because there are multiple constraints
on the number of vectors that can be allocated; pci_enable_msi_block()
-will return as soon as it finds any constraint that doesn't allow the
+returns as soon as it finds any constraint that doesn't allow the
call to succeed.
4.2.3 pci_disable_msi
@@ -137,10 +137,10 @@
interrupt(s). The interrupt may subsequently be assigned to another
device, so drivers should not cache the value of dev->irq.
-A device driver must always call free_irq() on the interrupt(s)
-for which it has called request_irq() before calling this function.
-Failure to do so will result in a BUG_ON(), the device will be left with
-MSI enabled and will leak its vector.
+Before calling this function, a device driver must always call free_irq()
+on any interrupt for which it previously called request_irq().
+Failure to do so results in a BUG_ON(), leaving the device with
+MSI enabled and thus leaking its vector.
4.3 Using MSI-X
@@ -155,10 +155,10 @@
};
This allows for the device to use these interrupts in a sparse fashion;
-for example it could use interrupts 3 and 1027 and allocate only a
+for example, it could use interrupts 3 and 1027 and yet allocate only a
two-element array. The driver is expected to fill in the 'entry' value
-in each element of the array to indicate which entries it wants the kernel
-to assign interrupts for. It is invalid to fill in two entries with the
+in each element of the array to indicate for which entries the kernel
+should assign interrupts; it is invalid to fill in two entries with the
same number.
4.3.1 pci_enable_msix
@@ -168,10 +168,11 @@
Calling this function asks the PCI subsystem to allocate 'nvec' MSIs.
The 'entries' argument is a pointer to an array of msix_entry structs
which should be at least 'nvec' entries in size. On success, the
-function will return 0 and the device will have been switched into
-MSI-X interrupt mode. The 'vector' elements in each entry will have
-been filled in with the interrupt number. The driver should then call
-request_irq() for each 'vector' that it decides to use.
+device is switched into MSI-X mode and the function returns 0.
+The 'vector' member in each entry is populated with the interrupt number;
+the driver should then call request_irq() for each 'vector' that it
+decides to use. The device driver is responsible for keeping track of the
+interrupts assigned to the MSI-X vectors so it can free them again later.
If this function returns a negative number, it indicates an error and
the driver should not attempt to allocate any more MSI-X interrupts for
@@ -181,16 +182,14 @@
This function, in contrast with pci_enable_msi(), does not adjust
dev->irq. The device will not generate interrupts for this interrupt
-number once MSI-X is enabled. The device driver is responsible for
-keeping track of the interrupts assigned to the MSI-X vectors so it can
-free them again later.
+number once MSI-X is enabled.
Device drivers should normally call this function once per device
during the initialization phase.
-It is ideal if drivers can cope with a variable number of MSI-X interrupts,
+It is ideal if drivers can cope with a variable number of MSI-X interrupts;
there are many reasons why the platform may not be able to provide the
-exact number a driver asks for.
+exact number that a driver asks for.
A request loop to achieve that might look like:
@@ -212,15 +211,15 @@
void pci_disable_msix(struct pci_dev *dev)
-This API should be used to undo the effect of pci_enable_msix(). It frees
+This function should be used to undo the effect of pci_enable_msix(). It frees
the previously allocated message signaled interrupts. The interrupts may
subsequently be assigned to another device, so drivers should not cache
the value of the 'vector' elements over a call to pci_disable_msix().
-A device driver must always call free_irq() on the interrupt(s)
-for which it has called request_irq() before calling this function.
-Failure to do so will result in a BUG_ON(), the device will be left with
-MSI enabled and will leak its vector.
+Before calling this function, a device driver must always call free_irq()
+on any interrupt for which it previously called request_irq().
+Failure to do so results in a BUG_ON(), leaving the device with
+MSI-X enabled and thus leaking its vector.
4.3.3 The MSI-X Table
@@ -232,10 +231,10 @@
4.4 Handling devices implementing both MSI and MSI-X capabilities
If a device implements both MSI and MSI-X capabilities, it can
-run in either MSI mode or MSI-X mode but not both simultaneously.
+run in either MSI mode or MSI-X mode, but not both simultaneously.
This is a requirement of the PCI spec, and it is enforced by the
PCI layer. Calling pci_enable_msi() when MSI-X is already enabled or
-pci_enable_msix() when MSI is already enabled will result in an error.
+pci_enable_msix() when MSI is already enabled results in an error.
If a device driver wishes to switch between MSI and MSI-X at runtime,
it must first quiesce the device, then switch it back to pin-interrupt
mode, before calling pci_enable_msi() or pci_enable_msix() and resuming
@@ -251,7 +250,7 @@
above, MSI-X supports any number of interrupts between 1 and 2048.
In constrast, MSI is restricted to a maximum of 32 interrupts (and
must be a power of two). In addition, the MSI interrupt vectors must
-be allocated consecutively, so the system may not be able to allocate
+be allocated consecutively, so the system might not be able to allocate
as many vectors for MSI as it could for MSI-X. On some platforms, MSI
interrupts must all be targeted at the same set of CPUs whereas MSI-X
interrupts can all be targeted at different CPUs.
@@ -281,7 +280,7 @@
Using 'lspci -v' (as root) may show some devices with "MSI", "Message
Signalled Interrupts" or "MSI-X" capabilities. Each of these capabilities
-has an 'Enable' flag which will be followed with either "+" (enabled)
+has an 'Enable' flag which is followed with either "+" (enabled)
or "-" (disabled).
@@ -298,7 +297,7 @@
Some host chipsets simply don't support MSIs properly. If we're
lucky, the manufacturer knows this and has indicated it in the ACPI
-FADT table. In this case, Linux will automatically disable MSIs.
+FADT table. In this case, Linux automatically disables MSIs.
Some boards don't include this information in the table and so we have
to detect them ourselves. The complete list of these is found near the
quirk_disable_all_msi() function in drivers/pci/quirks.c.
@@ -317,7 +316,7 @@
PCI configuration space (especially the Hypertransport chipsets such
as the nVidia nForce and Serverworks HT2000). As with host chipsets,
Linux mostly knows about them and automatically enables MSIs if it can.
-If you have a bridge which Linux doesn't yet know about, you can enable
+If you have a bridge unknown to Linux, you can enable
MSIs in configuration space using whatever method you know works, then
enable MSIs on that bridge by doing:
@@ -327,7 +326,7 @@
0000:00:0e.0).
To disable MSIs, echo 0 instead of 1. Changing this value should be
-done with caution as it can break interrupt handling for all devices
+done with caution as it could break interrupt handling for all devices
below this bridge.
Again, please notify linux-pci@vger.kernel.org of any bridges that need
@@ -336,7 +335,7 @@
5.3. Disabling MSIs on a single device
Some devices are known to have faulty MSI implementations. Usually this
-is handled in the individual device driver but occasionally it's necessary
+is handled in the individual device driver, but occasionally it's necessary
to handle this with a quirk. Some drivers have an option to disable use
of MSI. While this is a convenient workaround for the driver author,
it is not good practise, and should not be emulated.
@@ -350,7 +349,7 @@
have enabled CONFIG_PCI_MSI.
Then, 'lspci -t' gives the list of bridges above a device. Reading
-/sys/bus/pci/devices/*/msi_bus will tell you whether MSI are enabled (1)
+/sys/bus/pci/devices/*/msi_bus will tell you whether MSIs are enabled (1)
or disabled (0). If 0 is found in any of the msi_bus files belonging
to bridges between the PCI root and the device, MSIs are disabled.
diff --git a/Documentation/SubmittingDrivers b/Documentation/SubmittingDrivers
index 319baa8..36d16bb 100644
--- a/Documentation/SubmittingDrivers
+++ b/Documentation/SubmittingDrivers
@@ -130,7 +130,7 @@
ftp.??.kernel.org:/pub/linux/kernel/...
?? == your country code, such as "us", "uk", "fr", etc.
- http://git.kernel.org/?p=linux/kernel/git/torvalds/linux-2.6.git
+ http://git.kernel.org/?p=linux/kernel/git/torvalds/linux.git
Linux kernel mailing list:
linux-kernel@vger.kernel.org
diff --git a/Documentation/SubmittingPatches b/Documentation/SubmittingPatches
index 569f353..4468ce2 100644
--- a/Documentation/SubmittingPatches
+++ b/Documentation/SubmittingPatches
@@ -303,7 +303,7 @@
The sign-off is a simple line at the end of the explanation for the
patch, which certifies that you wrote it or otherwise have the right to
-pass it on as a open-source patch. The rules are pretty simple: if you
+pass it on as an open-source patch. The rules are pretty simple: if you
can certify the below:
Developer's Certificate of Origin 1.1
diff --git a/Documentation/acpi/apei/einj.txt b/Documentation/acpi/apei/einj.txt
index dfab718..5cc699b 100644
--- a/Documentation/acpi/apei/einj.txt
+++ b/Documentation/acpi/apei/einj.txt
@@ -48,12 +48,19 @@
- param1
This file is used to set the first error parameter value. Effect of
parameter depends on error_type specified. For memory error, this is
- physical memory address.
+ physical memory address. Only available if param_extension module
+ parameter is specified.
- param2
This file is used to set the second error parameter value. Effect of
parameter depends on error_type specified. For memory error, this is
- physical memory address mask.
+ physical memory address mask. Only available if param_extension
+ module parameter is specified.
+
+Injecting parameter support is a BIOS version specific extension, that
+is, it only works on some BIOS version. If you want to use it, please
+make sure your BIOS version has the proper support and specify
+"param_extension=y" in module parameter.
For more information about EINJ, please refer to ACPI specification
version 4.0, section 17.5.
diff --git a/Documentation/device-mapper/dm-crypt.txt b/Documentation/device-mapper/dm-crypt.txt
index 6b5c42d..2c656ae 100644
--- a/Documentation/device-mapper/dm-crypt.txt
+++ b/Documentation/device-mapper/dm-crypt.txt
@@ -4,7 +4,8 @@
Device-Mapper's "crypt" target provides transparent encryption of block devices
using the kernel crypto API.
-Parameters: <cipher> <key> <iv_offset> <device path> <offset>
+Parameters: <cipher> <key> <iv_offset> <device path> \
+ <offset> [<#opt_params> <opt_params>]
<cipher>
Encryption cipher and an optional IV generation mode.
@@ -37,6 +38,24 @@
<offset>
Starting sector within the device where the encrypted data begins.
+<#opt_params>
+ Number of optional parameters. If there are no optional parameters,
+ the optional paramaters section can be skipped or #opt_params can be zero.
+ Otherwise #opt_params is the number of following arguments.
+
+ Example of optional parameters section:
+ 1 allow_discards
+
+allow_discards
+ Block discard requests (a.k.a. TRIM) are passed through the crypt device.
+ The default is to ignore discard requests.
+
+ WARNING: Assess the specific security risks carefully before enabling this
+ option. For example, allowing discards on encrypted devices may lead to
+ the leak of information about the ciphertext device (filesystem type,
+ used space etc.) if the discarded blocks can be located easily on the
+ device later.
+
Example scripts
===============
LUKS (Linux Unified Key Setup) is now the preferred way to set up disk
diff --git a/Documentation/device-mapper/dm-flakey.txt b/Documentation/device-mapper/dm-flakey.txt
index c8efdfd..6ff5c23 100644
--- a/Documentation/device-mapper/dm-flakey.txt
+++ b/Documentation/device-mapper/dm-flakey.txt
@@ -1,17 +1,53 @@
dm-flakey
=========
-This target is the same as the linear target except that it returns I/O
-errors periodically. It's been found useful in simulating failing
-devices for testing purposes.
+This target is the same as the linear target except that it exhibits
+unreliable behaviour periodically. It's been found useful in simulating
+failing devices for testing purposes.
Starting from the time the table is loaded, the device is available for
-<up interval> seconds, then returns errors for <down interval> seconds,
-and then this cycle repeats.
+<up interval> seconds, then exhibits unreliable behaviour for <down
+interval> seconds, and then this cycle repeats.
-Parameters: <dev path> <offset> <up interval> <down interval>
+Also, consider using this in combination with the dm-delay target too,
+which can delay reads and writes and/or send them to different
+underlying devices.
+
+Table parameters
+----------------
+ <dev path> <offset> <up interval> <down interval> \
+ [<num_features> [<feature arguments>]]
+
+Mandatory parameters:
<dev path>: Full pathname to the underlying block-device, or a
"major:minor" device-number.
<offset>: Starting sector within the device.
<up interval>: Number of seconds device is available.
<down interval>: Number of seconds device returns errors.
+
+Optional feature parameters:
+ If no feature parameters are present, during the periods of
+ unreliability, all I/O returns errors.
+
+ drop_writes:
+ All write I/O is silently ignored.
+ Read I/O is handled correctly.
+
+ corrupt_bio_byte <Nth_byte> <direction> <value> <flags>:
+ During <down interval>, replace <Nth_byte> of the data of
+ each matching bio with <value>.
+
+ <Nth_byte>: The offset of the byte to replace.
+ Counting starts at 1, to replace the first byte.
+ <direction>: Either 'r' to corrupt reads or 'w' to corrupt writes.
+ 'w' is incompatible with drop_writes.
+ <value>: The value (from 0-255) to write.
+ <flags>: Perform the replacement only if bio->bi_rw has all the
+ selected flags set.
+
+Examples:
+ corrupt_bio_byte 32 r 1 0
+ - replaces the 32nd byte of READ bios with the value 1
+
+ corrupt_bio_byte 224 w 0 32
+ - replaces the 224th byte of REQ_META (=32) bios with the value 0
diff --git a/Documentation/device-mapper/dm-raid.txt b/Documentation/device-mapper/dm-raid.txt
index 33b6b70..2a8c113 100644
--- a/Documentation/device-mapper/dm-raid.txt
+++ b/Documentation/device-mapper/dm-raid.txt
@@ -1,70 +1,108 @@
-Device-mapper RAID (dm-raid) is a bridge from DM to MD. It
-provides a way to use device-mapper interfaces to access the MD RAID
-drivers.
+dm-raid
+-------
-As with all device-mapper targets, the nominal public interfaces are the
-constructor (CTR) tables and the status outputs (both STATUSTYPE_INFO
-and STATUSTYPE_TABLE). The CTR table looks like the following:
+The device-mapper RAID (dm-raid) target provides a bridge from DM to MD.
+It allows the MD RAID drivers to be accessed using a device-mapper
+interface.
-1: <s> <l> raid \
-2: <raid_type> <#raid_params> <raid_params> \
-3: <#raid_devs> <meta_dev1> <dev1> .. <meta_devN> <devN>
+The target is named "raid" and it accepts the following parameters:
-Line 1 contains the standard first three arguments to any device-mapper
-target - the start, length, and target type fields. The target type in
-this case is "raid".
+ <raid_type> <#raid_params> <raid_params> \
+ <#raid_devs> <metadata_dev0> <dev0> [.. <metadata_devN> <devN>]
-Line 2 contains the arguments that define the particular raid
-type/personality/level, the required arguments for that raid type, and
-any optional arguments. Possible raid types include: raid4, raid5_la,
-raid5_ls, raid5_rs, raid6_zr, raid6_nr, and raid6_nc. (raid1 is
-planned for the future.) The list of required and optional parameters
-is the same for all the current raid types. The required parameters are
-positional, while the optional parameters are given as key/value pairs.
-The possible parameters are as follows:
- <chunk_size> Chunk size in sectors.
- [[no]sync] Force/Prevent RAID initialization
- [rebuild <idx>] Rebuild the drive indicated by the index
- [daemon_sleep <ms>] Time between bitmap daemon work to clear bits
- [min_recovery_rate <kB/sec/disk>] Throttle RAID initialization
- [max_recovery_rate <kB/sec/disk>] Throttle RAID initialization
- [max_write_behind <sectors>] See '-write-behind=' (man mdadm)
- [stripe_cache <sectors>] Stripe cache size for higher RAIDs
+<raid_type>:
+ raid1 RAID1 mirroring
+ raid4 RAID4 dedicated parity disk
+ raid5_la RAID5 left asymmetric
+ - rotating parity 0 with data continuation
+ raid5_ra RAID5 right asymmetric
+ - rotating parity N with data continuation
+ raid5_ls RAID5 left symmetric
+ - rotating parity 0 with data restart
+ raid5_rs RAID5 right symmetric
+ - rotating parity N with data restart
+ raid6_zr RAID6 zero restart
+ - rotating parity zero (left-to-right) with data restart
+ raid6_nr RAID6 N restart
+ - rotating parity N (right-to-left) with data restart
+ raid6_nc RAID6 N continue
+ - rotating parity N (right-to-left) with data continuation
-Line 3 contains the list of devices that compose the array in
-metadata/data device pairs. If the metadata is stored separately, a '-'
-is given for the metadata device position. If a drive has failed or is
-missing at creation time, a '-' can be given for both the metadata and
-data drives for a given position.
+ Refererence: Chapter 4 of
+ http://www.snia.org/sites/default/files/SNIA_DDF_Technical_Position_v2.0.pdf
-NB. Currently all metadata devices must be specified as '-'.
+<#raid_params>: The number of parameters that follow.
-Examples:
-# RAID4 - 4 data drives, 1 parity
+<raid_params> consists of
+ Mandatory parameters:
+ <chunk_size>: Chunk size in sectors. This parameter is often known as
+ "stripe size". It is the only mandatory parameter and
+ is placed first.
+
+ followed by optional parameters (in any order):
+ [sync|nosync] Force or prevent RAID initialization.
+
+ [rebuild <idx>] Rebuild drive number idx (first drive is 0).
+
+ [daemon_sleep <ms>]
+ Interval between runs of the bitmap daemon that
+ clear bits. A longer interval means less bitmap I/O but
+ resyncing after a failure is likely to take longer.
+
+ [min_recovery_rate <kB/sec/disk>] Throttle RAID initialization
+ [max_recovery_rate <kB/sec/disk>] Throttle RAID initialization
+ [write_mostly <idx>] Drive index is write-mostly
+ [max_write_behind <sectors>] See '-write-behind=' (man mdadm)
+ [stripe_cache <sectors>] Stripe cache size (higher RAIDs only)
+ [region_size <sectors>]
+ The region_size multiplied by the number of regions is the
+ logical size of the array. The bitmap records the device
+ synchronisation state for each region.
+
+<#raid_devs>: The number of devices composing the array.
+ Each device consists of two entries. The first is the device
+ containing the metadata (if any); the second is the one containing the
+ data.
+
+ If a drive has failed or is missing at creation time, a '-' can be
+ given for both the metadata and data drives for a given position.
+
+
+Example tables
+--------------
+# RAID4 - 4 data drives, 1 parity (no metadata devices)
# No metadata devices specified to hold superblock/bitmap info
# Chunk size of 1MiB
# (Lines separated for easy reading)
+
0 1960893648 raid \
raid4 1 2048 \
5 - 8:17 - 8:33 - 8:49 - 8:65 - 8:81
-# RAID4 - 4 data drives, 1 parity (no metadata devices)
+# RAID4 - 4 data drives, 1 parity (with metadata devices)
# Chunk size of 1MiB, force RAID initialization,
# min recovery rate at 20 kiB/sec/disk
+
0 1960893648 raid \
- raid4 4 2048 min_recovery_rate 20 sync\
- 5 - 8:17 - 8:33 - 8:49 - 8:65 - 8:81
+ raid4 4 2048 sync min_recovery_rate 20 \
+ 5 8:17 8:18 8:33 8:34 8:49 8:50 8:65 8:66 8:81 8:82
-Performing a 'dmsetup table' should display the CTR table used to
-construct the mapping (with possible reordering of optional
-parameters).
+'dmsetup table' displays the table used to construct the mapping.
+The optional parameters are always printed in the order listed
+above with "sync" or "nosync" always output ahead of the other
+arguments, regardless of the order used when originally loading the table.
+Arguments that can be repeated are ordered by value.
-Performing a 'dmsetup status' will yield information on the state and
-health of the array. The output is as follows:
+'dmsetup status' yields information on the state and health of the
+array.
+The output is as follows:
1: <s> <l> raid \
2: <raid_type> <#devices> <1 health char for each dev> <resync_ratio>
-Line 1 is standard DM output. Line 2 is best shown by example:
+Line 1 is the standard output produced by device-mapper.
+Line 2 is produced by the raid target, and best explained by example:
0 1960893648 raid raid4 5 AAAAA 2/490221568
Here we can see the RAID type is raid4, there are 5 devices - all of
which are 'A'live, and the array is 2/490221568 complete with recovery.
+Faulty or missing devices are marked 'D'. Devices that are out-of-sync
+are marked 'a'.
diff --git a/Documentation/devicetree/bindings/gpio/gpio_keys.txt b/Documentation/devicetree/bindings/gpio/gpio_keys.txt
index 7190c99..5c2c021 100644
--- a/Documentation/devicetree/bindings/gpio/gpio_keys.txt
+++ b/Documentation/devicetree/bindings/gpio/gpio_keys.txt
@@ -10,7 +10,7 @@
Each button (key) is represented as a sub-node of "gpio-keys":
Subnode properties:
- - gpios: OF devcie-tree gpio specificatin.
+ - gpios: OF device-tree gpio specification.
- label: Descriptive name of the key.
- linux,code: Keycode to emit.
diff --git a/Documentation/devicetree/bindings/input/fsl-mma8450.txt b/Documentation/devicetree/bindings/input/fsl-mma8450.txt
new file mode 100644
index 0000000..a00c94c
--- /dev/null
+++ b/Documentation/devicetree/bindings/input/fsl-mma8450.txt
@@ -0,0 +1,11 @@
+* Freescale MMA8450 3-Axis Accelerometer
+
+Required properties:
+- compatible : "fsl,mma8450".
+
+Example:
+
+accelerometer: mma8450@1c {
+ compatible = "fsl,mma8450";
+ reg = <0x1c>;
+};
diff --git a/Documentation/dmaengine.txt b/Documentation/dmaengine.txt
index 5a0cb1e..94b7e0f 100644
--- a/Documentation/dmaengine.txt
+++ b/Documentation/dmaengine.txt
@@ -10,87 +10,181 @@
Below is a guide to device driver writers on how to use the Slave-DMA API of the
DMA Engine. This is applicable only for slave DMA usage only.
-The slave DMA usage consists of following steps
+The slave DMA usage consists of following steps:
1. Allocate a DMA slave channel
2. Set slave and controller specific parameters
3. Get a descriptor for transaction
-4. Submit the transaction and wait for callback notification
+4. Submit the transaction
+5. Issue pending requests and wait for callback notification
1. Allocate a DMA slave channel
-Channel allocation is slightly different in the slave DMA context, client
-drivers typically need a channel from a particular DMA controller only and even
-in some cases a specific channel is desired. To request a channel
-dma_request_channel() API is used.
-Interface:
-struct dma_chan *dma_request_channel(dma_cap_mask_t mask,
- dma_filter_fn filter_fn,
- void *filter_param);
-where dma_filter_fn is defined as:
-typedef bool (*dma_filter_fn)(struct dma_chan *chan, void *filter_param);
+ Channel allocation is slightly different in the slave DMA context,
+ client drivers typically need a channel from a particular DMA
+ controller only and even in some cases a specific channel is desired.
+ To request a channel dma_request_channel() API is used.
-When the optional 'filter_fn' parameter is set to NULL dma_request_channel
-simply returns the first channel that satisfies the capability mask. Otherwise,
-when the mask parameter is insufficient for specifying the necessary channel,
-the filter_fn routine can be used to disposition the available channels in the
-system. The filter_fn routine is called once for each free channel in the
-system. Upon seeing a suitable channel filter_fn returns DMA_ACK which flags
-that channel to be the return value from dma_request_channel. A channel
-allocated via this interface is exclusive to the caller, until
-dma_release_channel() is called.
+ Interface:
+ struct dma_chan *dma_request_channel(dma_cap_mask_t mask,
+ dma_filter_fn filter_fn,
+ void *filter_param);
+ where dma_filter_fn is defined as:
+ typedef bool (*dma_filter_fn)(struct dma_chan *chan, void *filter_param);
+
+ The 'filter_fn' parameter is optional, but highly recommended for
+ slave and cyclic channels as they typically need to obtain a specific
+ DMA channel.
+
+ When the optional 'filter_fn' parameter is NULL, dma_request_channel()
+ simply returns the first channel that satisfies the capability mask.
+
+ Otherwise, the 'filter_fn' routine will be called once for each free
+ channel which has a capability in 'mask'. 'filter_fn' is expected to
+ return 'true' when the desired DMA channel is found.
+
+ A channel allocated via this interface is exclusive to the caller,
+ until dma_release_channel() is called.
2. Set slave and controller specific parameters
-Next step is always to pass some specific information to the DMA driver. Most of
-the generic information which a slave DMA can use is in struct dma_slave_config.
-It allows the clients to specify DMA direction, DMA addresses, bus widths, DMA
-burst lengths etc. If some DMA controllers have more parameters to be sent then
-they should try to embed struct dma_slave_config in their controller specific
-structure. That gives flexibility to client to pass more parameters, if
-required.
-Interface:
-int dmaengine_slave_config(struct dma_chan *chan,
- struct dma_slave_config *config)
+ Next step is always to pass some specific information to the DMA
+ driver. Most of the generic information which a slave DMA can use
+ is in struct dma_slave_config. This allows the clients to specify
+ DMA direction, DMA addresses, bus widths, DMA burst lengths etc
+ for the peripheral.
+
+ If some DMA controllers have more parameters to be sent then they
+ should try to embed struct dma_slave_config in their controller
+ specific structure. That gives flexibility to client to pass more
+ parameters, if required.
+
+ Interface:
+ int dmaengine_slave_config(struct dma_chan *chan,
+ struct dma_slave_config *config)
+
+ Please see the dma_slave_config structure definition in dmaengine.h
+ for a detailed explaination of the struct members. Please note
+ that the 'direction' member will be going away as it duplicates the
+ direction given in the prepare call.
3. Get a descriptor for transaction
-For slave usage the various modes of slave transfers supported by the
-DMA-engine are:
-slave_sg - DMA a list of scatter gather buffers from/to a peripheral
-dma_cyclic - Perform a cyclic DMA operation from/to a peripheral till the
+
+ For slave usage the various modes of slave transfers supported by the
+ DMA-engine are:
+
+ slave_sg - DMA a list of scatter gather buffers from/to a peripheral
+ dma_cyclic - Perform a cyclic DMA operation from/to a peripheral till the
operation is explicitly stopped.
-The non NULL return of this transfer API represents a "descriptor" for the given
-transaction.
-Interface:
-struct dma_async_tx_descriptor *(*chan->device->device_prep_dma_sg)(
- struct dma_chan *chan,
- struct scatterlist *dst_sg, unsigned int dst_nents,
- struct scatterlist *src_sg, unsigned int src_nents,
+ A non-NULL return of this transfer API represents a "descriptor" for
+ the given transaction.
+
+ Interface:
+ struct dma_async_tx_descriptor *(*chan->device->device_prep_slave_sg)(
+ struct dma_chan *chan, struct scatterlist *sgl,
+ unsigned int sg_len, enum dma_data_direction direction,
unsigned long flags);
-struct dma_async_tx_descriptor *(*chan->device->device_prep_dma_cyclic)(
+
+ struct dma_async_tx_descriptor *(*chan->device->device_prep_dma_cyclic)(
struct dma_chan *chan, dma_addr_t buf_addr, size_t buf_len,
size_t period_len, enum dma_data_direction direction);
-4. Submit the transaction and wait for callback notification
-To schedule the transaction to be scheduled by dma device, the "descriptor"
-returned in above (3) needs to be submitted.
-To tell the dma driver that a transaction is ready to be serviced, the
-descriptor->submit() callback needs to be invoked. This chains the descriptor to
-the pending queue.
-The transactions in the pending queue can be activated by calling the
-issue_pending API. If channel is idle then the first transaction in queue is
-started and subsequent ones queued up.
-On completion of the DMA operation the next in queue is submitted and a tasklet
-triggered. The tasklet would then call the client driver completion callback
-routine for notification, if set.
-Interface:
-void dma_async_issue_pending(struct dma_chan *chan);
+ The peripheral driver is expected to have mapped the scatterlist for
+ the DMA operation prior to calling device_prep_slave_sg, and must
+ keep the scatterlist mapped until the DMA operation has completed.
+ The scatterlist must be mapped using the DMA struct device. So,
+ normal setup should look like this:
-==============================================================================
+ nr_sg = dma_map_sg(chan->device->dev, sgl, sg_len);
+ if (nr_sg == 0)
+ /* error */
-Additional usage notes for dma driver writers
-1/ Although DMA engine specifies that completion callback routines cannot submit
-any new operations, but typically for slave DMA subsequent transaction may not
-be available for submit prior to callback routine being called. This requirement
-is not a requirement for DMA-slave devices. But they should take care to drop
-the spin-lock they might be holding before calling the callback routine
+ desc = chan->device->device_prep_slave_sg(chan, sgl, nr_sg,
+ direction, flags);
+
+ Once a descriptor has been obtained, the callback information can be
+ added and the descriptor must then be submitted. Some DMA engine
+ drivers may hold a spinlock between a successful preparation and
+ submission so it is important that these two operations are closely
+ paired.
+
+ Note:
+ Although the async_tx API specifies that completion callback
+ routines cannot submit any new operations, this is not the
+ case for slave/cyclic DMA.
+
+ For slave DMA, the subsequent transaction may not be available
+ for submission prior to callback function being invoked, so
+ slave DMA callbacks are permitted to prepare and submit a new
+ transaction.
+
+ For cyclic DMA, a callback function may wish to terminate the
+ DMA via dmaengine_terminate_all().
+
+ Therefore, it is important that DMA engine drivers drop any
+ locks before calling the callback function which may cause a
+ deadlock.
+
+ Note that callbacks will always be invoked from the DMA
+ engines tasklet, never from interrupt context.
+
+4. Submit the transaction
+
+ Once the descriptor has been prepared and the callback information
+ added, it must be placed on the DMA engine drivers pending queue.
+
+ Interface:
+ dma_cookie_t dmaengine_submit(struct dma_async_tx_descriptor *desc)
+
+ This returns a cookie can be used to check the progress of DMA engine
+ activity via other DMA engine calls not covered in this document.
+
+ dmaengine_submit() will not start the DMA operation, it merely adds
+ it to the pending queue. For this, see step 5, dma_async_issue_pending.
+
+5. Issue pending DMA requests and wait for callback notification
+
+ The transactions in the pending queue can be activated by calling the
+ issue_pending API. If channel is idle then the first transaction in
+ queue is started and subsequent ones queued up.
+
+ On completion of each DMA operation, the next in queue is started and
+ a tasklet triggered. The tasklet will then call the client driver
+ completion callback routine for notification, if set.
+
+ Interface:
+ void dma_async_issue_pending(struct dma_chan *chan);
+
+Further APIs:
+
+1. int dmaengine_terminate_all(struct dma_chan *chan)
+
+ This causes all activity for the DMA channel to be stopped, and may
+ discard data in the DMA FIFO which hasn't been fully transferred.
+ No callback functions will be called for any incomplete transfers.
+
+2. int dmaengine_pause(struct dma_chan *chan)
+
+ This pauses activity on the DMA channel without data loss.
+
+3. int dmaengine_resume(struct dma_chan *chan)
+
+ Resume a previously paused DMA channel. It is invalid to resume a
+ channel which is not currently paused.
+
+4. enum dma_status dma_async_is_tx_complete(struct dma_chan *chan,
+ dma_cookie_t cookie, dma_cookie_t *last, dma_cookie_t *used)
+
+ This can be used to check the status of the channel. Please see
+ the documentation in include/linux/dmaengine.h for a more complete
+ description of this API.
+
+ This can be used in conjunction with dma_async_is_complete() and
+ the cookie returned from 'descriptor->submit()' to check for
+ completion of a specific DMA transaction.
+
+ Note:
+ Not all DMA engine drivers can return reliable information for
+ a running DMA channel. It is recommended that DMA engine users
+ pause or stop (via dmaengine_terminate_all) the channel before
+ using this API.
diff --git a/Documentation/email-clients.txt b/Documentation/email-clients.txt
index a0b58e2..860c29a 100644
--- a/Documentation/email-clients.txt
+++ b/Documentation/email-clients.txt
@@ -199,18 +199,16 @@
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.)
+- Disable HTML Format: Set "mail.identity.id1.compose_html" to "false".
+
+- Enable "preformat" mode: Set "editor.quotesPreformatted" to "true".
+
+- Enable UTF8: Set "prefs.converted-to-utf8" to "true".
- Install the "toggle wordwrap" extension. Download the file from:
https://addons.mozilla.org/thunderbird/addon/2351/
diff --git a/Documentation/fault-injection/fault-injection.txt b/Documentation/fault-injection/fault-injection.txt
index 7be15e4..82a5d25 100644
--- a/Documentation/fault-injection/fault-injection.txt
+++ b/Documentation/fault-injection/fault-injection.txt
@@ -143,8 +143,7 @@
failslab, fail_page_alloc, and fail_make_request use this way.
Helper functions:
- init_fault_attr_dentries(entries, attr, name);
- void cleanup_fault_attr_dentries(entries);
+ fault_create_debugfs_attr(name, parent, attr);
- module parameters
diff --git a/Documentation/feature-removal-schedule.txt b/Documentation/feature-removal-schedule.txt
index ea0bace..c4a6e14 100644
--- a/Documentation/feature-removal-schedule.txt
+++ b/Documentation/feature-removal-schedule.txt
@@ -296,15 +296,6 @@
---------------------------
-What: CONFIG_THERMAL_HWMON
-When: January 2009
-Why: This option was introduced just to allow older lm-sensors userspace
- to keep working over the upgrade to 2.6.26. At the scheduled time of
- removal fixed lm-sensors (2.x or 3.x) should be readily available.
-Who: Rene Herman <rene.herman@gmail.com>
-
----------------------------
-
What: Code that is now under CONFIG_WIRELESS_EXT_SYSFS
(in net/core/net-sysfs.c)
When: After the only user (hal) has seen a release with the patches
@@ -590,3 +581,14 @@
Who: Alan Stern <stern@rowland.harvard.edu>
----------------------------
+
+What: threeg and interface sysfs files in /sys/devices/platform/acer-wmi
+When: 2012
+Why: In 3.0, we can now autodetect internal 3G device and already have
+ the threeg rfkill device. So, we plan to remove threeg sysfs support
+ for it's no longer necessary.
+
+ We also plan to remove interface sysfs file that exposed which ACPI-WMI
+ interface that was used by acer-wmi driver. It will replaced by
+ information log when acer-wmi initial.
+Who: Lee, Chun-Yi <jlee@novell.com>
diff --git a/Documentation/filesystems/befs.txt b/Documentation/filesystems/befs.txt
index 6e49c36..da45e6c 100644
--- a/Documentation/filesystems/befs.txt
+++ b/Documentation/filesystems/befs.txt
@@ -27,7 +27,7 @@
Does anyone know of a more current email address for Makoto? He doesn't
respond to the address given above...
-Current maintainer: Sergey S. Kostyliov <rathamahata@php4.ru>
+This filesystem doesn't have a maintainer.
WHAT IS THIS DRIVER?
==================
diff --git a/Documentation/frv/booting.txt b/Documentation/frv/booting.txt
index ace200b..37c4d84 100644
--- a/Documentation/frv/booting.txt
+++ b/Documentation/frv/booting.txt
@@ -106,13 +106,20 @@
To use the first on-chip serial port at baud rate 115200, no parity, 8
bits, and no flow control.
- (*) root=/dev/<xxxx>
+ (*) root=<xxxx>
- This specifies the device upon which the root filesystem resides. For
- example:
+ This specifies the device upon which the root filesystem resides. It
+ may be specified by major and minor number, device path, or even
+ partition uuid, if supported. For example:
/dev/nfs NFS root filesystem
/dev/mtdblock3 Fourth RedBoot partition on the System Flash
+ PARTUUID=00112233-4455-6677-8899-AABBCCDDEEFF/PARTNROFF=1
+ first partition after the partition with the given UUID
+ 253:0 Device with major 253 and minor 0
+
+ Authoritative information can be found in
+ "Documentation/kernel-parameters.txt".
(*) rw
diff --git a/Documentation/ioctl/ioctl-number.txt b/Documentation/ioctl/ioctl-number.txt
index 72ba8d5..845a191 100644
--- a/Documentation/ioctl/ioctl-number.txt
+++ b/Documentation/ioctl/ioctl-number.txt
@@ -292,6 +292,7 @@
<mailto:buk@buks.ipn.de>
0xA0 all linux/sdp/sdp.h Industrial Device Project
<mailto:kenji@bitgate.com>
+0xA2 00-0F arch/tile/include/asm/hardwall.h
0xA3 80-8F Port ACL in development:
<mailto:tlewis@mindspring.com>
0xA3 90-9F linux/dtlk.h
diff --git a/Documentation/kernel-docs.txt b/Documentation/kernel-docs.txt
index 9a86746..0e0734b 100644
--- a/Documentation/kernel-docs.txt
+++ b/Documentation/kernel-docs.txt
@@ -620,17 +620,6 @@
(including this document itself) have been moved there, and might
be more up to date than the web version.
- * Name: "Linux Source Driver"
- URL: http://lsd.linux.cz
- Keywords: Browsing source code.
- Description: "Linux Source Driver (LSD) is an application, which
- can make browsing source codes of Linux kernel easier than you can
- imagine. You can select between multiple versions of kernel (e.g.
- 0.01, 1.0.0, 2.0.33, 2.0.34pre13, 2.0.0, 2.1.101 etc.). With LSD
- you can search Linux kernel (fulltext, macros, types, functions
- and variables) and LSD can generate patches for you on the fly
- (files, directories or kernel)".
-
* Name: "Linux Kernel Source Reference"
Author: Thomas Graichen.
URL: http://marc.info/?l=linux-kernel&m=96446640102205&w=4
diff --git a/Documentation/kernel-parameters.txt b/Documentation/kernel-parameters.txt
index c328511..614d038 100644
--- a/Documentation/kernel-parameters.txt
+++ b/Documentation/kernel-parameters.txt
@@ -40,6 +40,7 @@
ALSA ALSA sound support is enabled.
APIC APIC support is enabled.
APM Advanced Power Management support is enabled.
+ ARM ARM architecture is enabled.
AVR32 AVR32 architecture is enabled.
AX25 Appropriate AX.25 support is enabled.
BLACKFIN Blackfin architecture is enabled.
@@ -49,6 +50,7 @@
EFI EFI Partitioning (GPT) is enabled
EIDE EIDE/ATAPI support is enabled.
FB The frame buffer device is enabled.
+ FTRACE Function tracing enabled.
GCOV GCOV profiling is enabled.
HW Appropriate hardware is enabled.
IA-64 IA-64 architecture is enabled.
@@ -69,6 +71,7 @@
Documentation/m68k/kernel-options.txt.
MCA MCA bus support is enabled.
MDA MDA console support is enabled.
+ MIPS MIPS architecture is enabled.
MOUSE Appropriate mouse support is enabled.
MSI Message Signaled Interrupts (PCI).
MTD MTD (Memory Technology Device) support is enabled.
@@ -100,7 +103,6 @@
SPARC Sparc architecture is enabled.
SWSUSP Software suspend (hibernation) is enabled.
SUSPEND System suspend states are enabled.
- FTRACE Function tracing enabled.
TPM TPM drivers are enabled.
TS Appropriate touchscreen support is enabled.
UMS USB Mass Storage support is enabled.
@@ -115,7 +117,7 @@
X86-64 X86-64 architecture is enabled.
More X86-64 boot options can be found in
Documentation/x86/x86_64/boot-options.txt .
- X86 Either 32bit or 64bit x86 (same as X86-32+X86-64)
+ X86 Either 32-bit or 64-bit x86 (same as X86-32+X86-64)
XEN Xen support is enabled
In addition, the following text indicates that the option:
@@ -163,6 +165,11 @@
See also Documentation/power/pm.txt, pci=noacpi
+ acpi_rsdp= [ACPI,EFI,KEXEC]
+ Pass the RSDP address to the kernel, mostly used
+ on machines running EFI runtime service to boot the
+ second kernel for kdump.
+
acpi_apic_instance= [ACPI, IOAPIC]
Format: <int>
2: use 2nd APIC table, if available
@@ -371,7 +378,7 @@
atkbd.softrepeat= [HW]
Use software keyboard repeat
- autotest [IA64]
+ autotest [IA-64]
baycom_epp= [HW,AX25]
Format: <io>,<mode>
@@ -546,6 +553,9 @@
/proc/<pid>/coredump_filter.
See also Documentation/filesystems/proc.txt.
+ cpuidle.off=1 [CPU_IDLE]
+ disable the cpuidle sub-system
+
cpcihp_generic= [HW,PCI] Generic port I/O CompactPCI driver
Format:
<first_slot>,<last_slot>,<port>,<enum_bit>[,<debug>]
@@ -673,8 +683,8 @@
uart[8250],mmio32,<addr>[,options]
Start an early, polled-mode console on the 8250/16550
UART at the specified I/O port or MMIO address.
- MMIO inter-register address stride is either 8bit (mmio)
- or 32bit (mmio32).
+ MMIO inter-register address stride is either 8-bit
+ (mmio) or 32-bit (mmio32).
The options are the same as for ttyS, above.
earlyprintk= [X86,SH,BLACKFIN]
@@ -717,7 +727,7 @@
See Documentation/block/as-iosched.txt and
Documentation/block/deadline-iosched.txt for details.
- elfcorehdr= [IA64,PPC,SH,X86]
+ elfcorehdr= [IA-64,PPC,SH,X86]
Specifies physical address of start of kernel core
image elf header. Generally kexec loader will
pass this option to capture kernel.
@@ -783,7 +793,7 @@
tracer at boot up. function-list is a comma separated
list of functions. This list can be changed at run
time by the set_ftrace_filter file in the debugfs
- tracing directory.
+ tracing directory.
ftrace_notrace=[function-list]
[FTRACE] Do not trace the functions specified in
@@ -821,7 +831,7 @@
hashdist= [KNL,NUMA] Large hashes allocated during boot
are distributed across NUMA nodes. Defaults on
- for 64bit NUMA, off otherwise.
+ for 64-bit NUMA, off otherwise.
Format: 0 | 1 (for off | on)
hcl= [IA-64] SGI's Hardware Graph compatibility layer
@@ -990,10 +1000,10 @@
DMA.
forcedac [x86_64]
With this option iommu will not optimize to look
- for io virtual address below 32 bit forcing dual
+ for io virtual address below 32-bit forcing dual
address cycle on pci bus for cards supporting greater
- than 32 bit addressing. The default is to look
- for translation below 32 bit and if not available
+ than 32-bit addressing. The default is to look
+ for translation below 32-bit and if not available
then look in the higher range.
strict [Default Off]
With this option on every unmap_single operation will
@@ -1009,7 +1019,7 @@
off disable Interrupt Remapping
nosid disable Source ID checking
- inttest= [IA64]
+ inttest= [IA-64]
iomem= Disable strict checking of access to MMIO memory
strict regions from userspace.
@@ -1026,7 +1036,7 @@
nomerge
forcesac
soft
- pt [x86, IA64]
+ pt [x86, IA-64]
io7= [HW] IO7 for Marvel based alpha systems
See comment before marvel_specify_io7 in
@@ -1157,7 +1167,7 @@
kvm-amd.npt= [KVM,AMD] Disable nested paging (virtualized MMU)
for all guests.
- Default is 1 (enabled) if in 64bit or 32bit-PAE mode
+ Default is 1 (enabled) if in 64-bit or 32-bit PAE mode.
kvm-intel.ept= [KVM,Intel] Disable extended page tables
(virtualized MMU) support on capable Intel chips.
@@ -1194,10 +1204,10 @@
libata.dma=0 Disable all PATA and SATA DMA
libata.dma=1 PATA and SATA Disk DMA only
libata.dma=2 ATAPI (CDROM) DMA only
- libata.dma=4 Compact Flash DMA only
+ libata.dma=4 Compact Flash DMA only
Combinations also work, so libata.dma=3 enables DMA
for disks and CDROMs, but not CFs.
-
+
libata.ignore_hpa= [LIBATA] Ignore HPA limit
libata.ignore_hpa=0 keep BIOS limits (default)
libata.ignore_hpa=1 ignore limits, using full disk
@@ -1323,7 +1333,7 @@
ltpc= [NET]
Format: <io>,<irq>,<dma>
- machvec= [IA64] Force the use of a particular machine-vector
+ machvec= [IA-64] Force the use of a particular machine-vector
(machvec) in a generic kernel.
Example: machvec=hpzx1_swiotlb
@@ -1729,7 +1739,7 @@
nointroute [IA-64]
- nojitter [IA64] Disables jitter checking for ITC timers.
+ nojitter [IA-64] Disables jitter checking for ITC timers.
no-kvmclock [X86,KVM] Disable paravirtualized KVM clock driver
@@ -1795,7 +1805,7 @@
nox2apic [X86-64,APIC] Do not enable x2APIC mode.
- nptcg= [IA64] Override max number of concurrent global TLB
+ nptcg= [IA-64] Override max number of concurrent global TLB
purges which is reported from either PAL_VM_SUMMARY or
SAL PALO.
@@ -2072,7 +2082,7 @@
Format: { parport<nr> | timid | 0 }
See also Documentation/parport.txt.
- pmtmr= [X86] Manual setup of pmtmr I/O Port.
+ pmtmr= [X86] Manual setup of pmtmr I/O Port.
Override pmtimer IOPort with a hex value.
e.g. pmtmr=0x508
@@ -2156,6 +2166,8 @@
[HW,MOUSE] Controls Logitech smartscroll autorepeat.
0 = disabled, 1 = enabled (default).
+ pstore.backend= Specify the name of the pstore backend to use
+
pt. [PARIDE]
See Documentation/blockdev/paride.txt.
@@ -2241,6 +2253,7 @@
ro [KNL] Mount root device read-only on boot
root= [KNL] Root filesystem
+ See name_to_dev_t comment in init/do_mounts.c.
rootdelay= [KNL] Delay (in seconds) to pause before attempting to
mount the root filesystem
@@ -2627,6 +2640,16 @@
medium is write-protected).
Example: quirks=0419:aaf5:rl,0421:0433:rc
+ user_debug= [KNL,ARM]
+ Format: <int>
+ See arch/arm/Kconfig.debug help text.
+ 1 - undefined instruction events
+ 2 - system calls
+ 4 - invalid data aborts
+ 8 - SIGSEGV faults
+ 16 - SIGBUS faults
+ Example: user_debug=31
+
userpte=
[X86] Flags controlling user PTE allocations.
@@ -2672,6 +2695,27 @@
vmpoff= [KNL,S390] Perform z/VM CP command after power off.
Format: <command>
+ vsyscall= [X86-64]
+ Controls the behavior of vsyscalls (i.e. calls to
+ fixed addresses of 0xffffffffff600x00 from legacy
+ code). Most statically-linked binaries and older
+ versions of glibc use these calls. Because these
+ functions are at fixed addresses, they make nice
+ targets for exploits that can control RIP.
+
+ emulate [default] Vsyscalls turn into traps and are
+ emulated reasonably safely.
+
+ native Vsyscalls are native syscall instructions.
+ This is a little bit faster than trapping
+ and makes a few dynamic recompilers work
+ better than they would in emulation mode.
+ It also makes exploits much easier to write.
+
+ none Vsyscalls don't work at all. This makes
+ them quite hard to use for exploits but
+ might break your system.
+
vt.cur_default= [VT] Default cursor shape.
Format: 0xCCBBAA, where AA, BB, and CC are the same as
the parameters of the <Esc>[?A;B;Cc escape sequence;
diff --git a/Documentation/m68k/kernel-options.txt b/Documentation/m68k/kernel-options.txt
index c93bed6..97d45f2 100644
--- a/Documentation/m68k/kernel-options.txt
+++ b/Documentation/m68k/kernel-options.txt
@@ -129,6 +129,20 @@
the first of these. You can find out all valid major numbers by
looking into include/linux/major.h.
+In addition to major and minor numbers, if the device containing your
+root partition uses a partition table format with unique partition
+identifiers, then you may use them. For instance,
+"root=PARTUUID=00112233-4455-6677-8899-AABBCCDDEEFF". It is also
+possible to reference another partition on the same device using a
+known partition UUID as the starting point. For example,
+if partition 5 of the device has the UUID of
+00112233-4455-6677-8899-AABBCCDDEEFF then partition 3 may be found as
+follows:
+ PARTUUID=00112233-4455-6677-8899-AABBCCDDEEFF/PARTNROFF=-2
+
+Authoritative information can be found in
+"Documentation/kernel-parameters.txt".
+
2.2) ro, rw
-----------
diff --git a/Documentation/networking/bonding.txt b/Documentation/networking/bonding.txt
index 675612f..91df678 100644
--- a/Documentation/networking/bonding.txt
+++ b/Documentation/networking/bonding.txt
@@ -238,6 +238,18 @@
This option was added in bonding version 3.4.0.
+all_slaves_active
+
+ Specifies that duplicate frames (received on inactive ports) should be
+ dropped (0) or delivered (1).
+
+ Normally, bonding will drop duplicate frames (received on inactive
+ ports), which is desirable for most users. But there are some times
+ it is nice to allow duplicate frames to be delivered.
+
+ The default value is 0 (drop duplicate frames received on inactive
+ ports).
+
arp_interval
Specifies the ARP link monitoring frequency in milliseconds.
@@ -433,6 +445,23 @@
determined. See the High Availability section for additional
information. The default value is 0.
+min_links
+
+ Specifies the minimum number of links that must be active before
+ asserting carrier. It is similar to the Cisco EtherChannel min-links
+ feature. This allows setting the minimum number of member ports that
+ must be up (link-up state) before marking the bond device as up
+ (carrier on). This is useful for situations where higher level services
+ such as clustering want to ensure a minimum number of low bandwidth
+ links are active before switchover. This option only affect 802.3ad
+ mode.
+
+ The default value is 0. This will cause carrier to be asserted (for
+ 802.3ad mode) whenever there is an active aggregator, regardless of the
+ number of available links in that aggregator. Note that, because an
+ aggregator cannot be active without at least one available link,
+ setting this option to 0 or to 1 has the exact same effect.
+
mode
Specifies one of the bonding policies. The default is
@@ -599,7 +628,7 @@
affect only the active-backup mode. These options were added for
bonding versions 3.3.0 and 3.4.0 respectively.
- From Linux 2.6.40 and bonding version 3.7.1, these notifications
+ From Linux 3.0 and bonding version 3.7.1, these notifications
are generated by the ipv4 and ipv6 code and the numbers of
repetitions cannot be set independently.
diff --git a/Documentation/networking/scaling.txt b/Documentation/networking/scaling.txt
new file mode 100644
index 0000000..7254b4b
--- /dev/null
+++ b/Documentation/networking/scaling.txt
@@ -0,0 +1,371 @@
+Scaling in the Linux Networking Stack
+
+
+Introduction
+============
+
+This document describes a set of complementary techniques in the Linux
+networking stack to increase parallelism and improve performance for
+multi-processor systems.
+
+The following technologies are described:
+
+ RSS: Receive Side Scaling
+ RPS: Receive Packet Steering
+ RFS: Receive Flow Steering
+ Accelerated Receive Flow Steering
+ XPS: Transmit Packet Steering
+
+
+RSS: Receive Side Scaling
+=========================
+
+Contemporary NICs support multiple receive and transmit descriptor queues
+(multi-queue). On reception, a NIC can send different packets to different
+queues to distribute processing among CPUs. The NIC distributes packets by
+applying a filter to each packet that assigns it to one of a small number
+of logical flows. Packets for each flow are steered to a separate receive
+queue, which in turn can be processed by separate CPUs. This mechanism is
+generally known as “Receive-side Scaling” (RSS). The goal of RSS and
+the other scaling techniques to increase performance uniformly.
+Multi-queue distribution can also be used for traffic prioritization, but
+that is not the focus of these techniques.
+
+The filter used in RSS is typically a hash function over the network
+and/or transport layer headers-- for example, a 4-tuple hash over
+IP addresses and TCP ports of a packet. The most common hardware
+implementation of RSS uses a 128-entry indirection table where each entry
+stores a queue number. The receive queue for a packet is determined
+by masking out the low order seven bits of the computed hash for the
+packet (usually a Toeplitz hash), taking this number as a key into the
+indirection table and reading the corresponding value.
+
+Some advanced NICs allow steering packets to queues based on
+programmable filters. For example, webserver bound TCP port 80 packets
+can be directed to their own receive queue. Such “n-tuple” filters can
+be configured from ethtool (--config-ntuple).
+
+==== RSS Configuration
+
+The driver for a multi-queue capable NIC typically provides a kernel
+module parameter for specifying the number of hardware queues to
+configure. In the bnx2x driver, for instance, this parameter is called
+num_queues. A typical RSS configuration would be to have one receive queue
+for each CPU if the device supports enough queues, or otherwise at least
+one for each cache domain at a particular cache level (L1, L2, etc.).
+
+The indirection table of an RSS device, which resolves a queue by masked
+hash, is usually programmed by the driver at initialization. The
+default mapping is to distribute the queues evenly in the table, but the
+indirection table can be retrieved and modified at runtime using ethtool
+commands (--show-rxfh-indir and --set-rxfh-indir). Modifying the
+indirection table could be done to give different queues different
+relative weights.
+
+== RSS IRQ Configuration
+
+Each receive queue has a separate IRQ associated with it. The NIC triggers
+this to notify a CPU when new packets arrive on the given queue. The
+signaling path for PCIe devices uses message signaled interrupts (MSI-X),
+that can route each interrupt to a particular CPU. The active mapping
+of queues to IRQs can be determined from /proc/interrupts. By default,
+an IRQ may be handled on any CPU. Because a non-negligible part of packet
+processing takes place in receive interrupt handling, it is advantageous
+to spread receive interrupts between CPUs. To manually adjust the IRQ
+affinity of each interrupt see Documentation/IRQ-affinity. Some systems
+will be running irqbalance, a daemon that dynamically optimizes IRQ
+assignments and as a result may override any manual settings.
+
+== Suggested Configuration
+
+RSS should be enabled when latency is a concern or whenever receive
+interrupt processing forms a bottleneck. Spreading load between CPUs
+decreases queue length. For low latency networking, the optimal setting
+is to allocate as many queues as there are CPUs in the system (or the
+NIC maximum, if lower). Because the aggregate number of interrupts grows
+with each additional queue, the most efficient high-rate configuration
+is likely the one with the smallest number of receive queues where no
+CPU that processes receive interrupts reaches 100% utilization. Per-cpu
+load can be observed using the mpstat utility.
+
+
+RPS: Receive Packet Steering
+============================
+
+Receive Packet Steering (RPS) is logically a software implementation of
+RSS. Being in software, it is necessarily called later in the datapath.
+Whereas RSS selects the queue and hence CPU that will run the hardware
+interrupt handler, RPS selects the CPU to perform protocol processing
+above the interrupt handler. This is accomplished by placing the packet
+on the desired CPU’s backlog queue and waking up the CPU for processing.
+RPS has some advantages over RSS: 1) it can be used with any NIC,
+2) software filters can easily be added to hash over new protocols,
+3) it does not increase hardware device interrupt rate (although it does
+introduce inter-processor interrupts (IPIs)).
+
+RPS is called during bottom half of the receive interrupt handler, when
+a driver sends a packet up the network stack with netif_rx() or
+netif_receive_skb(). These call the get_rps_cpu() function, which
+selects the queue that should process a packet.
+
+The first step in determining the target CPU for RPS is to calculate a
+flow hash over the packet’s addresses or ports (2-tuple or 4-tuple hash
+depending on the protocol). This serves as a consistent hash of the
+associated flow of the packet. The hash is either provided by hardware
+or will be computed in the stack. Capable hardware can pass the hash in
+the receive descriptor for the packet; this would usually be the same
+hash used for RSS (e.g. computed Toeplitz hash). The hash is saved in
+skb->rx_hash and can be used elsewhere in the stack as a hash of the
+packet’s flow.
+
+Each receive hardware queue has an associated list of CPUs to which
+RPS may enqueue packets for processing. For each received packet,
+an index into the list is computed from the flow hash modulo the size
+of the list. The indexed CPU is the target for processing the packet,
+and the packet is queued to the tail of that CPU’s backlog queue. At
+the end of the bottom half routine, IPIs are sent to any CPUs for which
+packets have been queued to their backlog queue. The IPI wakes backlog
+processing on the remote CPU, and any queued packets are then processed
+up the networking stack.
+
+==== RPS Configuration
+
+RPS requires a kernel compiled with the CONFIG_RPS kconfig symbol (on
+by default for SMP). Even when compiled in, RPS remains disabled until
+explicitly configured. The list of CPUs to which RPS may forward traffic
+can be configured for each receive queue using a sysfs file entry:
+
+ /sys/class/net/<dev>/queues/rx-<n>/rps_cpus
+
+This file implements a bitmap of CPUs. RPS is disabled when it is zero
+(the default), in which case packets are processed on the interrupting
+CPU. Documentation/IRQ-affinity.txt explains how CPUs are assigned to
+the bitmap.
+
+== Suggested Configuration
+
+For a single queue device, a typical RPS configuration would be to set
+the rps_cpus to the CPUs in the same cache domain of the interrupting
+CPU. If NUMA locality is not an issue, this could also be all CPUs in
+the system. At high interrupt rate, it might be wise to exclude the
+interrupting CPU from the map since that already performs much work.
+
+For a multi-queue system, if RSS is configured so that a hardware
+receive queue is mapped to each CPU, then RPS is probably redundant
+and unnecessary. If there are fewer hardware queues than CPUs, then
+RPS might be beneficial if the rps_cpus for each queue are the ones that
+share the same cache domain as the interrupting CPU for that queue.
+
+
+RFS: Receive Flow Steering
+==========================
+
+While RPS steers packets solely based on hash, and thus generally
+provides good load distribution, it does not take into account
+application locality. This is accomplished by Receive Flow Steering
+(RFS). The goal of RFS is to increase datacache hitrate by steering
+kernel processing of packets to the CPU where the application thread
+consuming the packet is running. RFS relies on the same RPS mechanisms
+to enqueue packets onto the backlog of another CPU and to wake up that
+CPU.
+
+In RFS, packets are not forwarded directly by the value of their hash,
+but the hash is used as index into a flow lookup table. This table maps
+flows to the CPUs where those flows are being processed. The flow hash
+(see RPS section above) is used to calculate the index into this table.
+The CPU recorded in each entry is the one which last processed the flow.
+If an entry does not hold a valid CPU, then packets mapped to that entry
+are steered using plain RPS. Multiple table entries may point to the
+same CPU. Indeed, with many flows and few CPUs, it is very likely that
+a single application thread handles flows with many different flow hashes.
+
+rps_sock_table is a global flow table that contains the *desired* CPU for
+flows: the CPU that is currently processing the flow in userspace. Each
+table value is a CPU index that is updated during calls to recvmsg and
+sendmsg (specifically, inet_recvmsg(), inet_sendmsg(), inet_sendpage()
+and tcp_splice_read()).
+
+When the scheduler moves a thread to a new CPU while it has outstanding
+receive packets on the old CPU, packets may arrive out of order. To
+avoid this, RFS uses a second flow table to track outstanding packets
+for each flow: rps_dev_flow_table is a table specific to each hardware
+receive queue of each device. Each table value stores a CPU index and a
+counter. The CPU index represents the *current* CPU onto which packets
+for this flow are enqueued for further kernel processing. Ideally, kernel
+and userspace processing occur on the same CPU, and hence the CPU index
+in both tables is identical. This is likely false if the scheduler has
+recently migrated a userspace thread while the kernel still has packets
+enqueued for kernel processing on the old CPU.
+
+The counter in rps_dev_flow_table values records the length of the current
+CPU's backlog when a packet in this flow was last enqueued. Each backlog
+queue has a head counter that is incremented on dequeue. A tail counter
+is computed as head counter + queue length. In other words, the counter
+in rps_dev_flow_table[i] records the last element in flow i that has
+been enqueued onto the currently designated CPU for flow i (of course,
+entry i is actually selected by hash and multiple flows may hash to the
+same entry i).
+
+And now the trick for avoiding out of order packets: when selecting the
+CPU for packet processing (from get_rps_cpu()) the rps_sock_flow table
+and the rps_dev_flow table of the queue that the packet was received on
+are compared. If the desired CPU for the flow (found in the
+rps_sock_flow table) matches the current CPU (found in the rps_dev_flow
+table), the packet is enqueued onto that CPU’s backlog. If they differ,
+the current CPU is updated to match the desired CPU if one of the
+following is true:
+
+- The current CPU's queue head counter >= the recorded tail counter
+ value in rps_dev_flow[i]
+- The current CPU is unset (equal to NR_CPUS)
+- The current CPU is offline
+
+After this check, the packet is sent to the (possibly updated) current
+CPU. These rules aim to ensure that a flow only moves to a new CPU when
+there are no packets outstanding on the old CPU, as the outstanding
+packets could arrive later than those about to be processed on the new
+CPU.
+
+==== RFS Configuration
+
+RFS is only available if the kconfig symbol CONFIG_RFS is enabled (on
+by default for SMP). The functionality remains disabled until explicitly
+configured. The number of entries in the global flow table is set through:
+
+ /proc/sys/net/core/rps_sock_flow_entries
+
+The number of entries in the per-queue flow table are set through:
+
+ /sys/class/net/<dev>/queues/tx-<n>/rps_flow_cnt
+
+== Suggested Configuration
+
+Both of these need to be set before RFS is enabled for a receive queue.
+Values for both are rounded up to the nearest power of two. The
+suggested flow count depends on the expected number of active connections
+at any given time, which may be significantly less than the number of open
+connections. We have found that a value of 32768 for rps_sock_flow_entries
+works fairly well on a moderately loaded server.
+
+For a single queue device, the rps_flow_cnt value for the single queue
+would normally be configured to the same value as rps_sock_flow_entries.
+For a multi-queue device, the rps_flow_cnt for each queue might be
+configured as rps_sock_flow_entries / N, where N is the number of
+queues. So for instance, if rps_flow_entries is set to 32768 and there
+are 16 configured receive queues, rps_flow_cnt for each queue might be
+configured as 2048.
+
+
+Accelerated RFS
+===============
+
+Accelerated RFS is to RFS what RSS is to RPS: a hardware-accelerated load
+balancing mechanism that uses soft state to steer flows based on where
+the application thread consuming the packets of each flow is running.
+Accelerated RFS should perform better than RFS since packets are sent
+directly to a CPU local to the thread consuming the data. The target CPU
+will either be the same CPU where the application runs, or at least a CPU
+which is local to the application thread’s CPU in the cache hierarchy.
+
+To enable accelerated RFS, the networking stack calls the
+ndo_rx_flow_steer driver function to communicate the desired hardware
+queue for packets matching a particular flow. The network stack
+automatically calls this function every time a flow entry in
+rps_dev_flow_table is updated. The driver in turn uses a device specific
+method to program the NIC to steer the packets.
+
+The hardware queue for a flow is derived from the CPU recorded in
+rps_dev_flow_table. The stack consults a CPU to hardware queue map which
+is maintained by the NIC driver. This is an auto-generated reverse map of
+the IRQ affinity table shown by /proc/interrupts. Drivers can use
+functions in the cpu_rmap (“CPU affinity reverse map”) kernel library
+to populate the map. For each CPU, the corresponding queue in the map is
+set to be one whose processing CPU is closest in cache locality.
+
+==== Accelerated RFS Configuration
+
+Accelerated RFS is only available if the kernel is compiled with
+CONFIG_RFS_ACCEL and support is provided by the NIC device and driver.
+It also requires that ntuple filtering is enabled via ethtool. The map
+of CPU to queues is automatically deduced from the IRQ affinities
+configured for each receive queue by the driver, so no additional
+configuration should be necessary.
+
+== Suggested Configuration
+
+This technique should be enabled whenever one wants to use RFS and the
+NIC supports hardware acceleration.
+
+XPS: Transmit Packet Steering
+=============================
+
+Transmit Packet Steering is a mechanism for intelligently selecting
+which transmit queue to use when transmitting a packet on a multi-queue
+device. To accomplish this, a mapping from CPU to hardware queue(s) is
+recorded. The goal of this mapping is usually to assign queues
+exclusively to a subset of CPUs, where the transmit completions for
+these queues are processed on a CPU within this set. This choice
+provides two benefits. First, contention on the device queue lock is
+significantly reduced since fewer CPUs contend for the same queue
+(contention can be eliminated completely if each CPU has its own
+transmit queue). Secondly, cache miss rate on transmit completion is
+reduced, in particular for data cache lines that hold the sk_buff
+structures.
+
+XPS is configured per transmit queue by setting a bitmap of CPUs that
+may use that queue to transmit. The reverse mapping, from CPUs to
+transmit queues, is computed and maintained for each network device.
+When transmitting the first packet in a flow, the function
+get_xps_queue() is called to select a queue. This function uses the ID
+of the running CPU as a key into the CPU-to-queue lookup table. If the
+ID matches a single queue, that is used for transmission. If multiple
+queues match, one is selected by using the flow hash to compute an index
+into the set.
+
+The queue chosen for transmitting a particular flow is saved in the
+corresponding socket structure for the flow (e.g. a TCP connection).
+This transmit queue is used for subsequent packets sent on the flow to
+prevent out of order (ooo) packets. The choice also amortizes the cost
+of calling get_xps_queues() over all packets in the connection. To avoid
+ooo packets, the queue for a flow can subsequently only be changed if
+skb->ooo_okay is set for a packet in the flow. This flag indicates that
+there are no outstanding packets in the flow, so the transmit queue can
+change without the risk of generating out of order packets. The
+transport layer is responsible for setting ooo_okay appropriately. TCP,
+for instance, sets the flag when all data for a connection has been
+acknowledged.
+
+==== XPS Configuration
+
+XPS is only available if the kconfig symbol CONFIG_XPS is enabled (on by
+default for SMP). The functionality remains disabled until explicitly
+configured. To enable XPS, the bitmap of CPUs that may use a transmit
+queue is configured using the sysfs file entry:
+
+/sys/class/net/<dev>/queues/tx-<n>/xps_cpus
+
+== Suggested Configuration
+
+For a network device with a single transmission queue, XPS configuration
+has no effect, since there is no choice in this case. In a multi-queue
+system, XPS is preferably configured so that each CPU maps onto one queue.
+If there are as many queues as there are CPUs in the system, then each
+queue can also map onto one CPU, resulting in exclusive pairings that
+experience no contention. If there are fewer queues than CPUs, then the
+best CPUs to share a given queue are probably those that share the cache
+with the CPU that processes transmit completions for that queue
+(transmit interrupts).
+
+
+Further Information
+===================
+RPS and RFS were introduced in kernel 2.6.35. XPS was incorporated into
+2.6.38. Original patches were submitted by Tom Herbert
+(therbert@google.com)
+
+Accelerated RFS was introduced in 2.6.35. Original patches were
+submitted by Ben Hutchings (bhutchings@solarflare.com)
+
+Authors:
+Tom Herbert (therbert@google.com)
+Willem de Bruijn (willemb@google.com)
diff --git a/Documentation/power/runtime_pm.txt b/Documentation/power/runtime_pm.txt
index 14dd3c6..4ce5450 100644
--- a/Documentation/power/runtime_pm.txt
+++ b/Documentation/power/runtime_pm.txt
@@ -54,11 +54,10 @@
By default, the callbacks are always invoked in process context with interrupts
enabled. However, subsystems can use the pm_runtime_irq_safe() helper function
to tell the PM core that a device's ->runtime_suspend() and ->runtime_resume()
-callbacks should be invoked in atomic context with interrupts disabled
-(->runtime_idle() is still invoked the default way). This implies that these
-callback routines must not block or sleep, but it also means that the
-synchronous helper functions listed at the end of Section 4 can be used within
-an interrupt handler or in an atomic context.
+callbacks should be invoked in atomic context with interrupts disabled.
+This implies that these callback routines must not block or sleep, but it also
+means that the synchronous helper functions listed at the end of Section 4 can
+be used within an interrupt handler or in an atomic context.
The subsystem-level suspend callback is _entirely_ _responsible_ for handling
the suspend of the device as appropriate, which may, but need not include
@@ -483,6 +482,7 @@
pm_runtime_autosuspend()
pm_runtime_resume()
pm_runtime_get_sync()
+pm_runtime_put_sync()
pm_runtime_put_sync_suspend()
5. Runtime PM Initialization, Device Probing and Removal
diff --git a/Documentation/ramoops.txt b/Documentation/ramoops.txt
new file mode 100644
index 0000000..8fb1ba7
--- /dev/null
+++ b/Documentation/ramoops.txt
@@ -0,0 +1,76 @@
+Ramoops oops/panic logger
+=========================
+
+Sergiu Iordache <sergiu@chromium.org>
+
+Updated: 8 August 2011
+
+0. Introduction
+
+Ramoops is an oops/panic logger that writes its logs to RAM before the system
+crashes. It works by logging oopses and panics in a circular buffer. Ramoops
+needs a system with persistent RAM so that the content of that area can
+survive after a restart.
+
+1. Ramoops concepts
+
+Ramoops uses a predefined memory area to store the dump. The start and size of
+the memory area are set using two variables:
+ * "mem_address" for the start
+ * "mem_size" for the size. The memory size will be rounded down to a
+ power of two.
+
+The memory area is divided into "record_size" chunks (also rounded down to
+power of two) and each oops/panic writes a "record_size" chunk of
+information.
+
+Dumping both oopses and panics can be done by setting 1 in the "dump_oops"
+variable while setting 0 in that variable dumps only the panics.
+
+The module uses a counter to record multiple dumps but the counter gets reset
+on restart (i.e. new dumps after the restart will overwrite old ones).
+
+2. Setting the parameters
+
+Setting the ramoops parameters can be done in 2 different manners:
+ 1. Use the module parameters (which have the names of the variables described
+ as before).
+ 2. Use a platform device and set the platform data. The parameters can then
+ be set through that platform data. An example of doing that is:
+
+#include <linux/ramoops.h>
+[...]
+
+static struct ramoops_platform_data ramoops_data = {
+ .mem_size = <...>,
+ .mem_address = <...>,
+ .record_size = <...>,
+ .dump_oops = <...>,
+};
+
+static struct platform_device ramoops_dev = {
+ .name = "ramoops",
+ .dev = {
+ .platform_data = &ramoops_data,
+ },
+};
+
+[... inside a function ...]
+int ret;
+
+ret = platform_device_register(&ramoops_dev);
+if (ret) {
+ printk(KERN_ERR "unable to register platform device\n");
+ return ret;
+}
+
+3. Dump format
+
+The data dump begins with a header, currently defined as "====" followed by a
+timestamp and a new line. The dump then continues with the actual data.
+
+4. Reading the data
+
+The dump data can be read from memory (through /dev/mem or other means).
+Getting the module parameters, which are needed in order to parse the data, can
+be done through /sys/module/ramoops/parameters/* .
diff --git a/Documentation/virtual/00-INDEX b/Documentation/virtual/00-INDEX
index fe0251c..8e60199 100644
--- a/Documentation/virtual/00-INDEX
+++ b/Documentation/virtual/00-INDEX
@@ -8,3 +8,6 @@
- Extremely simple hypervisor for experimental/educational use.
uml/
- User Mode Linux, builds/runs Linux kernel as a userspace program.
+virtio.txt
+ - Text version of draft virtio spec.
+ See http://ozlabs.org/~rusty/virtio-spec
diff --git a/Documentation/virtual/lguest/lguest.c b/Documentation/virtual/lguest/lguest.c
index 043bd7d..d928c13 100644
--- a/Documentation/virtual/lguest/lguest.c
+++ b/Documentation/virtual/lguest/lguest.c
@@ -1996,6 +1996,9 @@
/* We use a simple helper to copy the arguments separated by spaces. */
concat((char *)(boot + 1), argv+optind+2);
+ /* Set kernel alignment to 16M (CONFIG_PHYSICAL_ALIGN) */
+ boot->hdr.kernel_alignment = 0x1000000;
+
/* Boot protocol version: 2.07 supports the fields for lguest. */
boot->hdr.version = 0x207;
diff --git a/Documentation/virtual/virtio-spec.txt b/Documentation/virtual/virtio-spec.txt
new file mode 100644
index 0000000..a350ae1
--- /dev/null
+++ b/Documentation/virtual/virtio-spec.txt
@@ -0,0 +1,2200 @@
+[Generated file: see http://ozlabs.org/~rusty/virtio-spec/]
+Virtio PCI Card Specification
+v0.9.1 DRAFT
+-
+
+Rusty Russell <rusty@rustcorp.com.au>IBM Corporation (Editor)
+
+2011 August 1.
+
+Purpose and Description
+
+This document describes the specifications of the “virtio” family
+of PCI[LaTeX Command: nomenclature] devices. These are devices
+are found in virtual environments[LaTeX Command: nomenclature],
+yet by design they are not all that different from physical PCI
+devices, and this document treats them as such. This allows the
+guest to use standard PCI drivers and discovery mechanisms.
+
+The purpose of virtio and this specification is that virtual
+environments and guests should have a straightforward, efficient,
+standard and extensible mechanism for virtual devices, rather
+than boutique per-environment or per-OS mechanisms.
+
+ Straightforward: Virtio PCI devices use normal PCI mechanisms
+ of interrupts and DMA which should be familiar to any device
+ driver author. There is no exotic page-flipping or COW
+ mechanism: it's just a PCI device.[footnote:
+This lack of page-sharing implies that the implementation of the
+device (e.g. the hypervisor or host) needs full access to the
+guest memory. Communication with untrusted parties (i.e.
+inter-guest communication) requires copying.
+]
+
+ Efficient: Virtio PCI devices consist of rings of descriptors
+ for input and output, which are neatly separated to avoid cache
+ effects from both guest and device writing to the same cache
+ lines.
+
+ Standard: Virtio PCI makes no assumptions about the environment
+ in which it operates, beyond supporting PCI. In fact the virtio
+ devices specified in the appendices do not require PCI at all:
+ they have been implemented on non-PCI buses.[footnote:
+The Linux implementation further separates the PCI virtio code
+from the specific virtio drivers: these drivers are shared with
+the non-PCI implementations (currently lguest and S/390).
+]
+
+ Extensible: Virtio PCI devices contain feature bits which are
+ acknowledged by the guest operating system during device setup.
+ This allows forwards and backwards compatibility: the device
+ offers all the features it knows about, and the driver
+ acknowledges those it understands and wishes to use.
+
+ Virtqueues
+
+The mechanism for bulk data transport on virtio PCI devices is
+pretentiously called a virtqueue. Each device can have zero or
+more virtqueues: for example, the network device has one for
+transmit and one for receive.
+
+Each virtqueue occupies two or more physically-contiguous pages
+(defined, for the purposes of this specification, as 4096 bytes),
+and consists of three parts:
+
+
++-------------------+-----------------------------------+-----------+
+| Descriptor Table | Available Ring (padding) | Used Ring |
++-------------------+-----------------------------------+-----------+
+
+
+When the driver wants to send buffers to the device, it puts them
+in one or more slots in the descriptor table, and writes the
+descriptor indices into the available ring. It then notifies the
+device. When the device has finished with the buffers, it writes
+the descriptors into the used ring, and sends an interrupt.
+
+Specification
+
+ PCI Discovery
+
+Any PCI device with Vendor ID 0x1AF4, and Device ID 0x1000
+through 0x103F inclusive is a virtio device[footnote:
+The actual value within this range is ignored
+]. The device must also have a Revision ID of 0 to match this
+specification.
+
+The Subsystem Device ID indicates which virtio device is
+supported by the device. The Subsystem Vendor ID should reflect
+the PCI Vendor ID of the environment (it's currently only used
+for informational purposes by the guest).
+
+
++----------------------+--------------------+---------------+
+| Subsystem Device ID | Virtio Device | Specification |
++----------------------+--------------------+---------------+
++----------------------+--------------------+---------------+
+| 1 | network card | Appendix C |
++----------------------+--------------------+---------------+
+| 2 | block device | Appendix D |
++----------------------+--------------------+---------------+
+| 3 | console | Appendix E |
++----------------------+--------------------+---------------+
+| 4 | entropy source | Appendix F |
++----------------------+--------------------+---------------+
+| 5 | memory ballooning | Appendix G |
++----------------------+--------------------+---------------+
+| 6 | ioMemory | - |
++----------------------+--------------------+---------------+
+| 9 | 9P transport | - |
++----------------------+--------------------+---------------+
+
+
+ Device Configuration
+
+To configure the device, we use the first I/O region of the PCI
+device. This contains a virtio header followed by a
+device-specific region.
+
+There may be different widths of accesses to the I/O region; the “
+natural” access method for each field in the virtio header must
+be used (i.e. 32-bit accesses for 32-bit fields, etc), but the
+device-specific region can be accessed using any width accesses,
+and should obtain the same results.
+
+Note that this is possible because while the virtio header is PCI
+(i.e. little) endian, the device-specific region is encoded in
+the native endian of the guest (where such distinction is
+applicable).
+
+ Device Initialization Sequence
+
+We start with an overview of device initialization, then expand
+on the details of the device and how each step is preformed.
+
+ Reset the device. This is not required on initial start up.
+
+ The ACKNOWLEDGE status bit is set: we have noticed the device.
+
+ The DRIVER status bit is set: we know how to drive the device.
+
+ Device-specific setup, including reading the Device Feature
+ Bits, discovery of virtqueues for the device, optional MSI-X
+ setup, and reading and possibly writing the virtio
+ configuration space.
+
+ The subset of Device Feature Bits understood by the driver is
+ written to the device.
+
+ The DRIVER_OK status bit is set.
+
+ The device can now be used (ie. buffers added to the
+ virtqueues)[footnote:
+Historically, drivers have used the device before steps 5 and 6.
+This is only allowed if the driver does not use any features
+which would alter this early use of the device.
+]
+
+If any of these steps go irrecoverably wrong, the guest should
+set the FAILED status bit to indicate that it has given up on the
+device (it can reset the device later to restart if desired).
+
+We now cover the fields required for general setup in detail.
+
+ Virtio Header
+
+The virtio header looks as follows:
+
+
++------------++---------------------+---------------------+----------+--------+---------+---------+---------+--------+
+| Bits || 32 | 32 | 32 | 16 | 16 | 16 | 8 | 8 |
++------------++---------------------+---------------------+----------+--------+---------+---------+---------+--------+
+| Read/Write || R | R+W | R+W | R | R+W | R+W | R+W | R |
++------------++---------------------+---------------------+----------+--------+---------+---------+---------+--------+
+| Purpose || Device | Guest | Queue | Queue | Queue | Queue | Device | ISR |
+| || Features bits 0:31 | Features bits 0:31 | Address | Size | Select | Notify | Status | Status |
++------------++---------------------+---------------------+----------+--------+---------+---------+---------+--------+
+
+
+If MSI-X is enabled for the device, two additional fields
+immediately follow this header:
+
+
++------------++----------------+--------+
+| Bits || 16 | 16 |
+ +----------------+--------+
++------------++----------------+--------+
+| Read/Write || R+W | R+W |
++------------++----------------+--------+
+| Purpose || Configuration | Queue |
+| (MSI-X) || Vector | Vector |
++------------++----------------+--------+
+
+
+Finally, if feature bits (VIRTIO_F_FEATURES_HI) this is
+immediately followed by two additional fields:
+
+
++------------++----------------------+----------------------
+| Bits || 32 | 32
++------------++----------------------+----------------------
+| Read/Write || R | R+W
++------------++----------------------+----------------------
+| Purpose || Device | Guest
+| || Features bits 32:63 | Features bits 32:63
++------------++----------------------+----------------------
+
+
+Immediately following these general headers, there may be
+device-specific headers:
+
+
++------------++--------------------+
+| Bits || Device Specific |
+ +--------------------+
++------------++--------------------+
+| Read/Write || Device Specific |
++------------++--------------------+
+| Purpose || Device Specific... |
+| || |
++------------++--------------------+
+
+
+ Device Status
+
+The Device Status field is updated by the guest to indicate its
+progress. This provides a simple low-level diagnostic: it's most
+useful to imagine them hooked up to traffic lights on the console
+indicating the status of each device.
+
+The device can be reset by writing a 0 to this field, otherwise
+at least one bit should be set:
+
+ ACKNOWLEDGE (1) Indicates that the guest OS has found the
+ device and recognized it as a valid virtio device.
+
+ DRIVER (2) Indicates that the guest OS knows how to drive the
+ device. Under Linux, drivers can be loadable modules so there
+ may be a significant (or infinite) delay before setting this
+ bit.
+
+ DRIVER_OK (3) Indicates that the driver is set up and ready to
+ drive the device.
+
+ FAILED (8) Indicates that something went wrong in the guest,
+ and it has given up on the device. This could be an internal
+ error, or the driver didn't like the device for some reason, or
+ even a fatal error during device operation. The device must be
+ reset before attempting to re-initialize.
+
+ Feature Bits
+
+The least significant 31 bits of the first configuration field
+indicates the features that the device supports (the high bit is
+reserved, and will be used to indicate the presence of future
+feature bits elsewhere). If more than 31 feature bits are
+supported, the device indicates so by setting feature bit 31 (see
+[cha:Reserved-Feature-Bits]). The bits are allocated as follows:
+
+ 0 to 23 Feature bits for the specific device type
+
+ 24 to 40 Feature bits reserved for extensions to the queue and
+ feature negotiation mechanisms
+
+ 41 to 63 Feature bits reserved for future extensions
+
+For example, feature bit 0 for a network device (i.e. Subsystem
+Device ID 1) indicates that the device supports checksumming of
+packets.
+
+The feature bits are negotiated: the device lists all the
+features it understands in the Device Features field, and the
+guest writes the subset that it understands into the Guest
+Features field. The only way to renegotiate is to reset the
+device.
+
+In particular, new fields in the device configuration header are
+indicated by offering a feature bit, so the guest can check
+before accessing that part of the configuration space.
+
+This allows for forwards and backwards compatibility: if the
+device is enhanced with a new feature bit, older guests will not
+write that feature bit back to the Guest Features field and it
+can go into backwards compatibility mode. Similarly, if a guest
+is enhanced with a feature that the device doesn't support, it
+will not see that feature bit in the Device Features field and
+can go into backwards compatibility mode (or, for poor
+implementations, set the FAILED Device Status bit).
+
+Access to feature bits 32 to 63 is enabled by Guest by setting
+feature bit 31. If this bit is unset, Device must assume that all
+feature bits > 31 are unset.
+
+ Configuration/Queue Vectors
+
+When MSI-X capability is present and enabled in the device
+(through standard PCI configuration space) 4 bytes at byte offset
+20 are used to map configuration change and queue interrupts to
+MSI-X vectors. In this case, the ISR Status field is unused, and
+device specific configuration starts at byte offset 24 in virtio
+header structure. When MSI-X capability is not enabled, device
+specific configuration starts at byte offset 20 in virtio header.
+
+Writing a valid MSI-X Table entry number, 0 to 0x7FF, to one of
+Configuration/Queue Vector registers, maps interrupts triggered
+by the configuration change/selected queue events respectively to
+the corresponding MSI-X vector. To disable interrupts for a
+specific event type, unmap it by writing a special NO_VECTOR
+value:
+
+/* Vector value used to disable MSI for queue */
+
+#define VIRTIO_MSI_NO_VECTOR 0xffff
+
+Reading these registers returns vector mapped to a given event,
+or NO_VECTOR if unmapped. All queue and configuration change
+events are unmapped by default.
+
+Note that mapping an event to vector might require allocating
+internal device resources, and might fail. Devices report such
+failures by returning the NO_VECTOR value when the relevant
+Vector field is read. After mapping an event to vector, the
+driver must verify success by reading the Vector field value: on
+success, the previously written value is returned, and on
+failure, NO_VECTOR is returned. If a mapping failure is detected,
+the driver can retry mapping with fewervectors, or disable MSI-X.
+
+ Virtqueue Configuration
+
+As a device can have zero or more virtqueues for bulk data
+transport (for example, the network driver has two), the driver
+needs to configure them as part of the device-specific
+configuration.
+
+This is done as follows, for each virtqueue a device has:
+
+ Write the virtqueue index (first queue is 0) to the Queue
+ Select field.
+
+ Read the virtqueue size from the Queue Size field, which is
+ always a power of 2. This controls how big the virtqueue is
+ (see below). If this field is 0, the virtqueue does not exist.
+
+ Allocate and zero virtqueue in contiguous physical memory, on a
+ 4096 byte alignment. Write the physical address, divided by
+ 4096 to the Queue Address field.[footnote:
+The 4096 is based on the x86 page size, but it's also large
+enough to ensure that the separate parts of the virtqueue are on
+separate cache lines.
+]
+
+ Optionally, if MSI-X capability is present and enabled on the
+ device, select a vector to use to request interrupts triggered
+ by virtqueue events. Write the MSI-X Table entry number
+ corresponding to this vector in Queue Vector field. Read the
+ Queue Vector field: on success, previously written value is
+ returned; on failure, NO_VECTOR value is returned.
+
+The Queue Size field controls the total number of bytes required
+for the virtqueue according to the following formula:
+
+#define ALIGN(x) (((x) + 4095) & ~4095)
+
+static inline unsigned vring_size(unsigned int qsz)
+
+{
+
+ return ALIGN(sizeof(struct vring_desc)*qsz + sizeof(u16)*(2
++ qsz))
+
+ + ALIGN(sizeof(struct vring_used_elem)*qsz);
+
+}
+
+This currently wastes some space with padding, but also allows
+future extensions. The virtqueue layout structure looks like this
+(qsz is the Queue Size field, which is a variable, so this code
+won't compile):
+
+struct vring {
+
+ /* The actual descriptors (16 bytes each) */
+
+ struct vring_desc desc[qsz];
+
+
+
+ /* A ring of available descriptor heads with free-running
+index. */
+
+ struct vring_avail avail;
+
+
+
+ // Padding to the next 4096 boundary.
+
+ char pad[];
+
+
+
+ // A ring of used descriptor heads with free-running index.
+
+ struct vring_used used;
+
+};
+
+ A Note on Virtqueue Endianness
+
+Note that the endian of these fields and everything else in the
+virtqueue is the native endian of the guest, not little-endian as
+PCI normally is. This makes for simpler guest code, and it is
+assumed that the host already has to be deeply aware of the guest
+endian so such an “endian-aware” device is not a significant
+issue.
+
+ Descriptor Table
+
+The descriptor table refers to the buffers the guest is using for
+the device. The addresses are physical addresses, and the buffers
+can be chained via the next field. Each descriptor describes a
+buffer which is read-only or write-only, but a chain of
+descriptors can contain both read-only and write-only buffers.
+
+No descriptor chain may be more than 2^32 bytes long in total.struct vring_desc {
+
+ /* Address (guest-physical). */
+
+ u64 addr;
+
+ /* Length. */
+
+ u32 len;
+
+/* This marks a buffer as continuing via the next field. */
+
+#define VRING_DESC_F_NEXT 1
+
+/* This marks a buffer as write-only (otherwise read-only). */
+
+#define VRING_DESC_F_WRITE 2
+
+/* This means the buffer contains a list of buffer descriptors.
+*/
+
+#define VRING_DESC_F_INDIRECT 4
+
+ /* The flags as indicated above. */
+
+ u16 flags;
+
+ /* Next field if flags & NEXT */
+
+ u16 next;
+
+};
+
+The number of descriptors in the table is specified by the Queue
+Size field for this virtqueue.
+
+ <sub:Indirect-Descriptors>Indirect Descriptors
+
+Some devices benefit by concurrently dispatching a large number
+of large requests. The VIRTIO_RING_F_INDIRECT_DESC feature can be
+used to allow this (see [cha:Reserved-Feature-Bits]). To increase
+ring capacity it is possible to store a table of indirect
+descriptors anywhere in memory, and insert a descriptor in main
+virtqueue (with flags&INDIRECT on) that refers to memory buffer
+containing this indirect descriptor table; fields addr and len
+refer to the indirect table address and length in bytes,
+respectively. The indirect table layout structure looks like this
+(len is the length of the descriptor that refers to this table,
+which is a variable, so this code won't compile):
+
+struct indirect_descriptor_table {
+
+ /* The actual descriptors (16 bytes each) */
+
+ struct vring_desc desc[len / 16];
+
+};
+
+The first indirect descriptor is located at start of the indirect
+descriptor table (index 0), additional indirect descriptors are
+chained by next field. An indirect descriptor without next field
+(with flags&NEXT off) signals the end of the indirect descriptor
+table, and transfers control back to the main virtqueue. An
+indirect descriptor can not refer to another indirect descriptor
+table (flags&INDIRECT must be off). A single indirect descriptor
+table can include both read-only and write-only descriptors;
+write-only flag (flags&WRITE) in the descriptor that refers to it
+is ignored.
+
+ Available Ring
+
+The available ring refers to what descriptors we are offering the
+device: it refers to the head of a descriptor chain. The “flags”
+field is currently 0 or 1: 1 indicating that we do not need an
+interrupt when the device consumes a descriptor from the
+available ring. Alternatively, the guest can ask the device to
+delay interrupts until an entry with an index specified by the “
+used_event” field is written in the used ring (equivalently,
+until the idx field in the used ring will reach the value
+used_event + 1). The method employed by the device is controlled
+by the VIRTIO_RING_F_EVENT_IDX feature bit (see [cha:Reserved-Feature-Bits]
+). This interrupt suppression is merely an optimization; it may
+not suppress interrupts entirely.
+
+The “idx” field indicates where we would put the next descriptor
+entry (modulo the ring size). This starts at 0, and increases.
+
+struct vring_avail {
+
+#define VRING_AVAIL_F_NO_INTERRUPT 1
+
+ u16 flags;
+
+ u16 idx;
+
+ u16 ring[qsz]; /* qsz is the Queue Size field read from device
+*/
+
+ u16 used_event;
+
+};
+
+ Used Ring
+
+The used ring is where the device returns buffers once it is done
+with them. The flags field can be used by the device to hint that
+no notification is necessary when the guest adds to the available
+ring. Alternatively, the “avail_event” field can be used by the
+device to hint that no notification is necessary until an entry
+with an index specified by the “avail_event” is written in the
+available ring (equivalently, until the idx field in the
+available ring will reach the value avail_event + 1). The method
+employed by the device is controlled by the guest through the
+VIRTIO_RING_F_EVENT_IDX feature bit (see [cha:Reserved-Feature-Bits]
+). [footnote:
+These fields are kept here because this is the only part of the
+virtqueue written by the device
+].
+
+Each entry in the ring is a pair: the head entry of the
+descriptor chain describing the buffer (this matches an entry
+placed in the available ring by the guest earlier), and the total
+of bytes written into the buffer. The latter is extremely useful
+for guests using untrusted buffers: if you do not know exactly
+how much has been written by the device, you usually have to zero
+the buffer to ensure no data leakage occurs.
+
+/* u32 is used here for ids for padding reasons. */
+
+struct vring_used_elem {
+
+ /* Index of start of used descriptor chain. */
+
+ u32 id;
+
+ /* Total length of the descriptor chain which was used
+(written to) */
+
+ u32 len;
+
+};
+
+
+
+struct vring_used {
+
+#define VRING_USED_F_NO_NOTIFY 1
+
+ u16 flags;
+
+ u16 idx;
+
+ struct vring_used_elem ring[qsz];
+
+ u16 avail_event;
+
+};
+
+ Helpers for Managing Virtqueues
+
+The Linux Kernel Source code contains the definitions above and
+helper routines in a more usable form, in
+include/linux/virtio_ring.h. This was explicitly licensed by IBM
+and Red Hat under the (3-clause) BSD license so that it can be
+freely used by all other projects, and is reproduced (with slight
+variation to remove Linux assumptions) in Appendix A.
+
+ Device Operation
+
+There are two parts to device operation: supplying new buffers to
+the device, and processing used buffers from the device. As an
+example, the virtio network device has two virtqueues: the
+transmit virtqueue and the receive virtqueue. The driver adds
+outgoing (read-only) packets to the transmit virtqueue, and then
+frees them after they are used. Similarly, incoming (write-only)
+buffers are added to the receive virtqueue, and processed after
+they are used.
+
+ Supplying Buffers to The Device
+
+Actual transfer of buffers from the guest OS to the device
+operates as follows:
+
+ Place the buffer(s) into free descriptor(s).
+
+ If there are no free descriptors, the guest may choose to
+ notify the device even if notifications are suppressed (to
+ reduce latency).[footnote:
+The Linux drivers do this only for read-only buffers: for
+write-only buffers, it is assumed that the driver is merely
+trying to keep the receive buffer ring full, and no notification
+of this expected condition is necessary.
+]
+
+ Place the id of the buffer in the next ring entry of the
+ available ring.
+
+ The steps (1) and (2) may be performed repeatedly if batching
+ is possible.
+
+ A memory barrier should be executed to ensure the device sees
+ the updated descriptor table and available ring before the next
+ step.
+
+ The available “idx” field should be increased by the number of
+ entries added to the available ring.
+
+ A memory barrier should be executed to ensure that we update
+ the idx field before checking for notification suppression.
+
+ If notifications are not suppressed, the device should be
+ notified of the new buffers.
+
+Note that the above code does not take precautions against the
+available ring buffer wrapping around: this is not possible since
+the ring buffer is the same size as the descriptor table, so step
+(1) will prevent such a condition.
+
+In addition, the maximum queue size is 32768 (it must be a power
+of 2 which fits in 16 bits), so the 16-bit “idx” value can always
+distinguish between a full and empty buffer.
+
+Here is a description of each stage in more detail.
+
+ Placing Buffers Into The Descriptor Table
+
+A buffer consists of zero or more read-only physically-contiguous
+elements followed by zero or more physically-contiguous
+write-only elements (it must have at least one element). This
+algorithm maps it into the descriptor table:
+
+ for each buffer element, b:
+
+ Get the next free descriptor table entry, d
+
+ Set d.addr to the physical address of the start of b
+
+ Set d.len to the length of b.
+
+ If b is write-only, set d.flags to VRING_DESC_F_WRITE,
+ otherwise 0.
+
+ If there is a buffer element after this:
+
+ Set d.next to the index of the next free descriptor element.
+
+ Set the VRING_DESC_F_NEXT bit in d.flags.
+
+In practice, the d.next fields are usually used to chain free
+descriptors, and a separate count kept to check there are enough
+free descriptors before beginning the mappings.
+
+ Updating The Available Ring
+
+The head of the buffer we mapped is the first d in the algorithm
+above. A naive implementation would do the following:
+
+avail->ring[avail->idx % qsz] = head;
+
+However, in general we can add many descriptors before we update
+the “idx” field (at which point they become visible to the
+device), so we keep a counter of how many we've added:
+
+avail->ring[(avail->idx + added++) % qsz] = head;
+
+ Updating The Index Field
+
+Once the idx field of the virtqueue is updated, the device will
+be able to access the descriptor entries we've created and the
+memory they refer to. This is why a memory barrier is generally
+used before the idx update, to ensure it sees the most up-to-date
+copy.
+
+The idx field always increments, and we let it wrap naturally at
+65536:
+
+avail->idx += added;
+
+ <sub:Notifying-The-Device>Notifying The Device
+
+Device notification occurs by writing the 16-bit virtqueue index
+of this virtqueue to the Queue Notify field of the virtio header
+in the first I/O region of the PCI device. This can be expensive,
+however, so the device can suppress such notifications if it
+doesn't need them. We have to be careful to expose the new idx
+value before checking the suppression flag: it's OK to notify
+gratuitously, but not to omit a required notification. So again,
+we use a memory barrier here before reading the flags or the
+avail_event field.
+
+If the VIRTIO_F_RING_EVENT_IDX feature is not negotiated, and if
+the VRING_USED_F_NOTIFY flag is not set, we go ahead and write to
+the PCI configuration space.
+
+If the VIRTIO_F_RING_EVENT_IDX feature is negotiated, we read the
+avail_event field in the available ring structure. If the
+available index crossed_the avail_event field value since the
+last notification, we go ahead and write to the PCI configuration
+space. The avail_event field wraps naturally at 65536 as well:
+
+(u16)(new_idx - avail_event - 1) < (u16)(new_idx - old_idx)
+
+ <sub:Receiving-Used-Buffers>Receiving Used Buffers From The
+ Device
+
+Once the device has used a buffer (read from or written to it, or
+parts of both, depending on the nature of the virtqueue and the
+device), it sends an interrupt, following an algorithm very
+similar to the algorithm used for the driver to send the device a
+buffer:
+
+ Write the head descriptor number to the next field in the used
+ ring.
+
+ Update the used ring idx.
+
+ Determine whether an interrupt is necessary:
+
+ If the VIRTIO_F_RING_EVENT_IDX feature is not negotiated: check
+ if f the VRING_AVAIL_F_NO_INTERRUPT flag is not set in avail-
+ >flags
+
+ If the VIRTIO_F_RING_EVENT_IDX feature is negotiated: check
+ whether the used index crossed the used_event field value
+ since the last update. The used_event field wraps naturally
+ at 65536 as well:(u16)(new_idx - used_event - 1) < (u16)(new_idx - old_idx)
+
+ If an interrupt is necessary:
+
+ If MSI-X capability is disabled:
+
+ Set the lower bit of the ISR Status field for the device.
+
+ Send the appropriate PCI interrupt for the device.
+
+ If MSI-X capability is enabled:
+
+ Request the appropriate MSI-X interrupt message for the
+ device, Queue Vector field sets the MSI-X Table entry
+ number.
+
+ If Queue Vector field value is NO_VECTOR, no interrupt
+ message is requested for this event.
+
+The guest interrupt handler should:
+
+ If MSI-X capability is disabled: read the ISR Status field,
+ which will reset it to zero. If the lower bit is zero, the
+ interrupt was not for this device. Otherwise, the guest driver
+ should look through the used rings of each virtqueue for the
+ device, to see if any progress has been made by the device
+ which requires servicing.
+
+ If MSI-X capability is enabled: look through the used rings of
+ each virtqueue mapped to the specific MSI-X vector for the
+ device, to see if any progress has been made by the device
+ which requires servicing.
+
+For each ring, guest should then disable interrupts by writing
+VRING_AVAIL_F_NO_INTERRUPT flag in avail structure, if required.
+It can then process used ring entries finally enabling interrupts
+by clearing the VRING_AVAIL_F_NO_INTERRUPT flag or updating the
+EVENT_IDX field in the available structure, Guest should then
+execute a memory barrier, and then recheck the ring empty
+condition. This is necessary to handle the case where, after the
+last check and before enabling interrupts, an interrupt has been
+suppressed by the device:
+
+vring_disable_interrupts(vq);
+
+for (;;) {
+
+ if (vq->last_seen_used != vring->used.idx) {
+
+ vring_enable_interrupts(vq);
+
+ mb();
+
+ if (vq->last_seen_used != vring->used.idx)
+
+ break;
+
+ }
+
+ struct vring_used_elem *e =
+vring.used->ring[vq->last_seen_used%vsz];
+
+ process_buffer(e);
+
+ vq->last_seen_used++;
+
+}
+
+ Dealing With Configuration Changes
+
+Some virtio PCI devices can change the device configuration
+state, as reflected in the virtio header in the PCI configuration
+space. In this case:
+
+ If MSI-X capability is disabled: an interrupt is delivered and
+ the second highest bit is set in the ISR Status field to
+ indicate that the driver should re-examine the configuration
+ space.Note that a single interrupt can indicate both that one
+ or more virtqueue has been used and that the configuration
+ space has changed: even if the config bit is set, virtqueues
+ must be scanned.
+
+ If MSI-X capability is enabled: an interrupt message is
+ requested. The Configuration Vector field sets the MSI-X Table
+ entry number to use. If Configuration Vector field value is
+ NO_VECTOR, no interrupt message is requested for this event.
+
+Creating New Device Types
+
+Various considerations are necessary when creating a new device
+type:
+
+ How Many Virtqueues?
+
+It is possible that a very simple device will operate entirely
+through its configuration space, but most will need at least one
+virtqueue in which it will place requests. A device with both
+input and output (eg. console and network devices described here)
+need two queues: one which the driver fills with buffers to
+receive input, and one which the driver places buffers to
+transmit output.
+
+ What Configuration Space Layout?
+
+Configuration space is generally used for rarely-changing or
+initialization-time parameters. But it is a limited resource, so
+it might be better to use a virtqueue to update configuration
+information (the network device does this for filtering,
+otherwise the table in the config space could potentially be very
+large).
+
+Note that this space is generally the guest's native endian,
+rather than PCI's little-endian.
+
+ What Device Number?
+
+Currently device numbers are assigned quite freely: a simple
+request mail to the author of this document or the Linux
+virtualization mailing list[footnote:
+
+https://lists.linux-foundation.org/mailman/listinfo/virtualization
+] will be sufficient to secure a unique one.
+
+Meanwhile for experimental drivers, use 65535 and work backwards.
+
+ How many MSI-X vectors?
+
+Using the optional MSI-X capability devices can speed up
+interrupt processing by removing the need to read ISR Status
+register by guest driver (which might be an expensive operation),
+reducing interrupt sharing between devices and queues within the
+device, and handling interrupts from multiple CPUs. However, some
+systems impose a limit (which might be as low as 256) on the
+total number of MSI-X vectors that can be allocated to all
+devices. Devices and/or device drivers should take this into
+account, limiting the number of vectors used unless the device is
+expected to cause a high volume of interrupts. Devices can
+control the number of vectors used by limiting the MSI-X Table
+Size or not presenting MSI-X capability in PCI configuration
+space. Drivers can control this by mapping events to as small
+number of vectors as possible, or disabling MSI-X capability
+altogether.
+
+ Message Framing
+
+The descriptors used for a buffer should not effect the semantics
+of the message, except for the total length of the buffer. For
+example, a network buffer consists of a 10 byte header followed
+by the network packet. Whether this is presented in the ring
+descriptor chain as (say) a 10 byte buffer and a 1514 byte
+buffer, or a single 1524 byte buffer, or even three buffers,
+should have no effect.
+
+In particular, no implementation should use the descriptor
+boundaries to determine the size of any header in a request.[footnote:
+The current qemu device implementations mistakenly insist that
+the first descriptor cover the header in these cases exactly, so
+a cautious driver should arrange it so.
+]
+
+ Device Improvements
+
+Any change to configuration space, or new virtqueues, or
+behavioural changes, should be indicated by negotiation of a new
+feature bit. This establishes clarity[footnote:
+Even if it does mean documenting design or implementation
+mistakes!
+] and avoids future expansion problems.
+
+Clusters of functionality which are always implemented together
+can use a single bit, but if one feature makes sense without the
+others they should not be gratuitously grouped together to
+conserve feature bits. We can always extend the spec when the
+first person needs more than 24 feature bits for their device.
+
+[LaTeX Command: printnomenclature]
+
+Appendix A: virtio_ring.h
+
+#ifndef VIRTIO_RING_H
+
+#define VIRTIO_RING_H
+
+/* An interface for efficient virtio implementation.
+
+ *
+
+ * This header is BSD licensed so anyone can use the definitions
+
+ * to implement compatible drivers/servers.
+
+ *
+
+ * Copyright 2007, 2009, IBM Corporation
+
+ * Copyright 2011, Red Hat, Inc
+
+ * All rights reserved.
+
+ *
+
+ * Redistribution and use in source and binary forms, with or
+without
+
+ * modification, are permitted provided that the following
+conditions
+
+ * are met:
+
+ * 1. Redistributions of source code must retain the above
+copyright
+
+ * notice, this list of conditions and the following
+disclaimer.
+
+ * 2. Redistributions in binary form must reproduce the above
+copyright
+
+ * notice, this list of conditions and the following
+disclaimer in the
+
+ * documentation and/or other materials provided with the
+distribution.
+
+ * 3. Neither the name of IBM nor the names of its contributors
+
+ * may be used to endorse or promote products derived from
+this software
+
+ * without specific prior written permission.
+
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+CONTRIBUTORS ``AS IS'' AND
+
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+TO, THE
+
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+PARTICULAR PURPOSE
+
+ * ARE DISCLAIMED. IN NO EVENT SHALL IBM OR CONTRIBUTORS BE
+LIABLE
+
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+CONSEQUENTIAL
+
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+SUBSTITUTE GOODS
+
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+INTERRUPTION)
+
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+CONTRACT, STRICT
+
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+IN ANY WAY
+
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+POSSIBILITY OF
+
+ * SUCH DAMAGE.
+
+ */
+
+
+
+/* This marks a buffer as continuing via the next field. */
+
+#define VRING_DESC_F_NEXT 1
+
+/* This marks a buffer as write-only (otherwise read-only). */
+
+#define VRING_DESC_F_WRITE 2
+
+
+
+/* The Host uses this in used->flags to advise the Guest: don't
+kick me
+
+ * when you add a buffer. It's unreliable, so it's simply an
+
+ * optimization. Guest will still kick if it's out of buffers.
+*/
+
+#define VRING_USED_F_NO_NOTIFY 1
+
+/* The Guest uses this in avail->flags to advise the Host: don't
+
+ * interrupt me when you consume a buffer. It's unreliable, so
+it's
+
+ * simply an optimization. */
+
+#define VRING_AVAIL_F_NO_INTERRUPT 1
+
+
+
+/* Virtio ring descriptors: 16 bytes.
+
+ * These can chain together via "next". */
+
+struct vring_desc {
+
+ /* Address (guest-physical). */
+
+ uint64_t addr;
+
+ /* Length. */
+
+ uint32_t len;
+
+ /* The flags as indicated above. */
+
+ uint16_t flags;
+
+ /* We chain unused descriptors via this, too */
+
+ uint16_t next;
+
+};
+
+
+
+struct vring_avail {
+
+ uint16_t flags;
+
+ uint16_t idx;
+
+ uint16_t ring[];
+
+ uint16_t used_event;
+
+};
+
+
+
+/* u32 is used here for ids for padding reasons. */
+
+struct vring_used_elem {
+
+ /* Index of start of used descriptor chain. */
+
+ uint32_t id;
+
+ /* Total length of the descriptor chain which was written
+to. */
+
+ uint32_t len;
+
+};
+
+
+
+struct vring_used {
+
+ uint16_t flags;
+
+ uint16_t idx;
+
+ struct vring_used_elem ring[];
+
+ uint16_t avail_event;
+
+};
+
+
+
+struct vring {
+
+ unsigned int num;
+
+
+
+ struct vring_desc *desc;
+
+ struct vring_avail *avail;
+
+ struct vring_used *used;
+
+};
+
+
+
+/* The standard layout for the ring is a continuous chunk of
+memory which
+
+ * looks like this. We assume num is a power of 2.
+
+ *
+
+ * struct vring {
+
+ * // The actual descriptors (16 bytes each)
+
+ * struct vring_desc desc[num];
+
+ *
+
+ * // A ring of available descriptor heads with free-running
+index.
+
+ * __u16 avail_flags;
+
+ * __u16 avail_idx;
+
+ * __u16 available[num];
+
+ *
+
+ * // Padding to the next align boundary.
+
+ * char pad[];
+
+ *
+
+ * // A ring of used descriptor heads with free-running
+index.
+
+ * __u16 used_flags;
+
+ * __u16 EVENT_IDX;
+
+ * struct vring_used_elem used[num];
+
+ * };
+
+ * Note: for virtio PCI, align is 4096.
+
+ */
+
+static inline void vring_init(struct vring *vr, unsigned int num,
+void *p,
+
+ unsigned long align)
+
+{
+
+ vr->num = num;
+
+ vr->desc = p;
+
+ vr->avail = p + num*sizeof(struct vring_desc);
+
+ vr->used = (void *)(((unsigned long)&vr->avail->ring[num]
+
+ + align-1)
+
+ & ~(align - 1));
+
+}
+
+
+
+static inline unsigned vring_size(unsigned int num, unsigned long
+align)
+
+{
+
+ return ((sizeof(struct vring_desc)*num +
+sizeof(uint16_t)*(2+num)
+
+ + align - 1) & ~(align - 1))
+
+ + sizeof(uint16_t)*3 + sizeof(struct
+vring_used_elem)*num;
+
+}
+
+
+
+static inline int vring_need_event(uint16_t event_idx, uint16_t
+new_idx, uint16_t old_idx)
+
+{
+
+ return (uint16_t)(new_idx - event_idx - 1) <
+(uint16_t)(new_idx - old_idx);
+
+}
+
+#endif /* VIRTIO_RING_H */
+
+<cha:Reserved-Feature-Bits>Appendix B: Reserved Feature Bits
+
+Currently there are five device-independent feature bits defined:
+
+ VIRTIO_F_NOTIFY_ON_EMPTY (24) Negotiating this feature
+ indicates that the driver wants an interrupt if the device runs
+ out of available descriptors on a virtqueue, even though
+ interrupts are suppressed using the VRING_AVAIL_F_NO_INTERRUPT
+ flag or the used_event field. An example of this is the
+ networking driver: it doesn't need to know every time a packet
+ is transmitted, but it does need to free the transmitted
+ packets a finite time after they are transmitted. It can avoid
+ using a timer if the device interrupts it when all the packets
+ are transmitted.
+
+ VIRTIO_F_RING_INDIRECT_DESC (28) Negotiating this feature
+ indicates that the driver can use descriptors with the
+ VRING_DESC_F_INDIRECT flag set, as described in [sub:Indirect-Descriptors]
+ .
+
+ VIRTIO_F_RING_EVENT_IDX(29) This feature enables the used_event
+ and the avail_event fields. If set, it indicates that the
+ device should ignore the flags field in the available ring
+ structure. Instead, the used_event field in this structure is
+ used by guest to suppress device interrupts. Further, the
+ driver should ignore the flags field in the used ring
+ structure. Instead, the avail_event field in this structure is
+ used by the device to suppress notifications. If unset, the
+ driver should ignore the used_event field; the device should
+ ignore the avail_event field; the flags field is used
+
+ VIRTIO_F_BAD_FEATURE(30) This feature should never be
+ negotiated by the guest; doing so is an indication that the
+ guest is faulty[footnote:
+An experimental virtio PCI driver contained in Linux version
+2.6.25 had this problem, and this feature bit can be used to
+detect it.
+]
+
+ VIRTIO_F_FEATURES_HIGH(31) This feature indicates that the
+ device supports feature bits 32:63. If unset, feature bits
+ 32:63 are unset.
+
+Appendix C: Network Device
+
+The virtio network device is a virtual ethernet card, and is the
+most complex of the devices supported so far by virtio. It has
+enhanced rapidly and demonstrates clearly how support for new
+features should be added to an existing device. Empty buffers are
+placed in one virtqueue for receiving packets, and outgoing
+packets are enqueued into another for transmission in that order.
+A third command queue is used to control advanced filtering
+features.
+
+ Configuration
+
+ Subsystem Device ID 1
+
+ Virtqueues 0:receiveq. 1:transmitq. 2:controlq[footnote:
+Only if VIRTIO_NET_F_CTRL_VQ set
+]
+
+ Feature bits
+
+ VIRTIO_NET_F_CSUM (0) Device handles packets with partial
+ checksum
+
+ VIRTIO_NET_F_GUEST_CSUM (1) Guest handles packets with partial
+ checksum
+
+ VIRTIO_NET_F_MAC (5) Device has given MAC address.
+
+ VIRTIO_NET_F_GSO (6) (Deprecated) device handles packets with
+ any GSO type.[footnote:
+It was supposed to indicate segmentation offload support, but
+upon further investigation it became clear that multiple bits
+were required.
+]
+
+ VIRTIO_NET_F_GUEST_TSO4 (7) Guest can receive TSOv4.
+
+ VIRTIO_NET_F_GUEST_TSO6 (8) Guest can receive TSOv6.
+
+ VIRTIO_NET_F_GUEST_ECN (9) Guest can receive TSO with ECN.
+
+ VIRTIO_NET_F_GUEST_UFO (10) Guest can receive UFO.
+
+ VIRTIO_NET_F_HOST_TSO4 (11) Device can receive TSOv4.
+
+ VIRTIO_NET_F_HOST_TSO6 (12) Device can receive TSOv6.
+
+ VIRTIO_NET_F_HOST_ECN (13) Device can receive TSO with ECN.
+
+ VIRTIO_NET_F_HOST_UFO (14) Device can receive UFO.
+
+ VIRTIO_NET_F_MRG_RXBUF (15) Guest can merge receive buffers.
+
+ VIRTIO_NET_F_STATUS (16) Configuration status field is
+ available.
+
+ VIRTIO_NET_F_CTRL_VQ (17) Control channel is available.
+
+ VIRTIO_NET_F_CTRL_RX (18) Control channel RX mode support.
+
+ VIRTIO_NET_F_CTRL_VLAN (19) Control channel VLAN filtering.
+
+ Device configuration layout Two configuration fields are
+ currently defined. The mac address field always exists (though
+ is only valid if VIRTIO_NET_F_MAC is set), and the status field
+ only exists if VIRTIO_NET_F_STATUS is set. Only one bit is
+ currently defined for the status field: VIRTIO_NET_S_LINK_UP. #define VIRTIO_NET_S_LINK_UP 1
+
+
+
+struct virtio_net_config {
+
+ u8 mac[6];
+
+ u16 status;
+
+};
+
+ Device Initialization
+
+ The initialization routine should identify the receive and
+ transmission virtqueues.
+
+ If the VIRTIO_NET_F_MAC feature bit is set, the configuration
+ space “mac” entry indicates the “physical” address of the the
+ network card, otherwise a private MAC address should be
+ assigned. All guests are expected to negotiate this feature if
+ it is set.
+
+ If the VIRTIO_NET_F_CTRL_VQ feature bit is negotiated, identify
+ the control virtqueue.
+
+ If the VIRTIO_NET_F_STATUS feature bit is negotiated, the link
+ status can be read from the bottom bit of the “status” config
+ field. Otherwise, the link should be assumed active.
+
+ The receive virtqueue should be filled with receive buffers.
+ This is described in detail below in “Setting Up Receive
+ Buffers”.
+
+ A driver can indicate that it will generate checksumless
+ packets by negotating the VIRTIO_NET_F_CSUM feature. This “
+ checksum offload” is a common feature on modern network cards.
+
+ If that feature is negotiated, a driver can use TCP or UDP
+ segmentation offload by negotiating the VIRTIO_NET_F_HOST_TSO4
+ (IPv4 TCP), VIRTIO_NET_F_HOST_TSO6 (IPv6 TCP) and
+ VIRTIO_NET_F_HOST_UFO (UDP fragmentation) features. It should
+ not send TCP packets requiring segmentation offload which have
+ the Explicit Congestion Notification bit set, unless the
+ VIRTIO_NET_F_HOST_ECN feature is negotiated.[footnote:
+This is a common restriction in real, older network cards.
+]
+
+ The converse features are also available: a driver can save the
+ virtual device some work by negotiating these features.[footnote:
+For example, a network packet transported between two guests on
+the same system may not require checksumming at all, nor
+segmentation, if both guests are amenable.
+] The VIRTIO_NET_F_GUEST_CSUM feature indicates that partially
+ checksummed packets can be received, and if it can do that then
+ the VIRTIO_NET_F_GUEST_TSO4, VIRTIO_NET_F_GUEST_TSO6,
+ VIRTIO_NET_F_GUEST_UFO and VIRTIO_NET_F_GUEST_ECN are the input
+ equivalents of the features described above. See “Receiving
+ Packets” below.
+
+ Device Operation
+
+Packets are transmitted by placing them in the transmitq, and
+buffers for incoming packets are placed in the receiveq. In each
+case, the packet itself is preceeded by a header:
+
+struct virtio_net_hdr {
+
+#define VIRTIO_NET_HDR_F_NEEDS_CSUM 1
+
+ u8 flags;
+
+#define VIRTIO_NET_HDR_GSO_NONE 0
+
+#define VIRTIO_NET_HDR_GSO_TCPV4 1
+
+#define VIRTIO_NET_HDR_GSO_UDP 3
+
+#define VIRTIO_NET_HDR_GSO_TCPV6 4
+
+#define VIRTIO_NET_HDR_GSO_ECN 0x80
+
+ u8 gso_type;
+
+ u16 hdr_len;
+
+ u16 gso_size;
+
+ u16 csum_start;
+
+ u16 csum_offset;
+
+/* Only if VIRTIO_NET_F_MRG_RXBUF: */
+
+ u16 num_buffers
+
+};
+
+The controlq is used to control device features such as
+filtering.
+
+ Packet Transmission
+
+Transmitting a single packet is simple, but varies depending on
+the different features the driver negotiated.
+
+ If the driver negotiated VIRTIO_NET_F_CSUM, and the packet has
+ not been fully checksummed, then the virtio_net_hdr's fields
+ are set as follows. Otherwise, the packet must be fully
+ checksummed, and flags is zero.
+
+ flags has the VIRTIO_NET_HDR_F_NEEDS_CSUM set,
+
+ <ite:csum_start-is-set>csum_start is set to the offset within
+ the packet to begin checksumming, and
+
+ csum_offset indicates how many bytes after the csum_start the
+ new (16 bit ones' complement) checksum should be placed.[footnote:
+For example, consider a partially checksummed TCP (IPv4) packet.
+It will have a 14 byte ethernet header and 20 byte IP header
+followed by the TCP header (with the TCP checksum field 16 bytes
+into that header). csum_start will be 14+20 = 34 (the TCP
+checksum includes the header), and csum_offset will be 16. The
+value in the TCP checksum field will be the sum of the TCP pseudo
+header, so that replacing it by the ones' complement checksum of
+the TCP header and body will give the correct result.
+]
+
+ <enu:If-the-driver>If the driver negotiated
+ VIRTIO_NET_F_HOST_TSO4, TSO6 or UFO, and the packet requires
+ TCP segmentation or UDP fragmentation, then the “gso_type”
+ field is set to VIRTIO_NET_HDR_GSO_TCPV4, TCPV6 or UDP.
+ (Otherwise, it is set to VIRTIO_NET_HDR_GSO_NONE). In this
+ case, packets larger than 1514 bytes can be transmitted: the
+ metadata indicates how to replicate the packet header to cut it
+ into smaller packets. The other gso fields are set:
+
+ hdr_len is a hint to the device as to how much of the header
+ needs to be kept to copy into each packet, usually set to the
+ length of the headers, including the transport header.[footnote:
+Due to various bugs in implementations, this field is not useful
+as a guarantee of the transport header size.
+]
+
+ gso_size is the size of the packet beyond that header (ie.
+ MSS).
+
+ If the driver negotiated the VIRTIO_NET_F_HOST_ECN feature, the
+ VIRTIO_NET_HDR_GSO_ECN bit may be set in “gso_type” as well,
+ indicating that the TCP packet has the ECN bit set.[footnote:
+This case is not handled by some older hardware, so is called out
+specifically in the protocol.
+]
+
+ If the driver negotiated the VIRTIO_NET_F_MRG_RXBUF feature,
+ the num_buffers field is set to zero.
+
+ The header and packet are added as one output buffer to the
+ transmitq, and the device is notified of the new entry (see [sub:Notifying-The-Device]
+ ).[footnote:
+Note that the header will be two bytes longer for the
+VIRTIO_NET_F_MRG_RXBUF case.
+]
+
+ Packet Transmission Interrupt
+
+Often a driver will suppress transmission interrupts using the
+VRING_AVAIL_F_NO_INTERRUPT flag (see [sub:Receiving-Used-Buffers]
+) and check for used packets in the transmit path of following
+packets. However, it will still receive interrupts if the
+VIRTIO_F_NOTIFY_ON_EMPTY feature is negotiated, indicating that
+the transmission queue is completely emptied.
+
+The normal behavior in this interrupt handler is to retrieve and
+new descriptors from the used ring and free the corresponding
+headers and packets.
+
+ Setting Up Receive Buffers
+
+It is generally a good idea to keep the receive virtqueue as
+fully populated as possible: if it runs out, network performance
+will suffer.
+
+If the VIRTIO_NET_F_GUEST_TSO4, VIRTIO_NET_F_GUEST_TSO6 or
+VIRTIO_NET_F_GUEST_UFO features are used, the Guest will need to
+accept packets of up to 65550 bytes long (the maximum size of a
+TCP or UDP packet, plus the 14 byte ethernet header), otherwise
+1514 bytes. So unless VIRTIO_NET_F_MRG_RXBUF is negotiated, every
+buffer in the receive queue needs to be at least this length [footnote:
+Obviously each one can be split across multiple descriptor
+elements.
+].
+
+If VIRTIO_NET_F_MRG_RXBUF is negotiated, each buffer must be at
+least the size of the struct virtio_net_hdr.
+
+ Packet Receive Interrupt
+
+When a packet is copied into a buffer in the receiveq, the
+optimal path is to disable further interrupts for the receiveq
+(see [sub:Receiving-Used-Buffers]) and process packets until no
+more are found, then re-enable them.
+
+Processing packet involves:
+
+ If the driver negotiated the VIRTIO_NET_F_MRG_RXBUF feature,
+ then the “num_buffers” field indicates how many descriptors
+ this packet is spread over (including this one). This allows
+ receipt of large packets without having to allocate large
+ buffers. In this case, there will be at least “num_buffers” in
+ the used ring, and they should be chained together to form a
+ single packet. The other buffers will not begin with a struct
+ virtio_net_hdr.
+
+ If the VIRTIO_NET_F_MRG_RXBUF feature was not negotiated, or
+ the “num_buffers” field is one, then the entire packet will be
+ contained within this buffer, immediately following the struct
+ virtio_net_hdr.
+
+ If the VIRTIO_NET_F_GUEST_CSUM feature was negotiated, the
+ VIRTIO_NET_HDR_F_NEEDS_CSUM bit in the “flags” field may be
+ set: if so, the checksum on the packet is incomplete and the “
+ csum_start” and “csum_offset” fields indicate how to calculate
+ it (see [ite:csum_start-is-set]).
+
+ If the VIRTIO_NET_F_GUEST_TSO4, TSO6 or UFO options were
+ negotiated, then the “gso_type” may be something other than
+ VIRTIO_NET_HDR_GSO_NONE, and the “gso_size” field indicates the
+ desired MSS (see [enu:If-the-driver]).Control Virtqueue
+
+The driver uses the control virtqueue (if VIRTIO_NET_F_VTRL_VQ is
+negotiated) to send commands to manipulate various features of
+the device which would not easily map into the configuration
+space.
+
+All commands are of the following form:
+
+struct virtio_net_ctrl {
+
+ u8 class;
+
+ u8 command;
+
+ u8 command-specific-data[];
+
+ u8 ack;
+
+};
+
+
+
+/* ack values */
+
+#define VIRTIO_NET_OK 0
+
+#define VIRTIO_NET_ERR 1
+
+The class, command and command-specific-data are set by the
+driver, and the device sets the ack byte. There is little it can
+do except issue a diagnostic if the ack byte is not
+VIRTIO_NET_OK.
+
+ Packet Receive Filtering
+
+If the VIRTIO_NET_F_CTRL_RX feature is negotiated, the driver can
+send control commands for promiscuous mode, multicast receiving,
+and filtering of MAC addresses.
+
+Note that in general, these commands are best-effort: unwanted
+packets may still arrive.
+
+ Setting Promiscuous Mode
+
+#define VIRTIO_NET_CTRL_RX 0
+
+ #define VIRTIO_NET_CTRL_RX_PROMISC 0
+
+ #define VIRTIO_NET_CTRL_RX_ALLMULTI 1
+
+The class VIRTIO_NET_CTRL_RX has two commands:
+VIRTIO_NET_CTRL_RX_PROMISC turns promiscuous mode on and off, and
+VIRTIO_NET_CTRL_RX_ALLMULTI turns all-multicast receive on and
+off. The command-specific-data is one byte containing 0 (off) or
+1 (on).
+
+ Setting MAC Address Filtering
+
+struct virtio_net_ctrl_mac {
+
+ u32 entries;
+
+ u8 macs[entries][ETH_ALEN];
+
+};
+
+
+
+#define VIRTIO_NET_CTRL_MAC 1
+
+ #define VIRTIO_NET_CTRL_MAC_TABLE_SET 0
+
+The device can filter incoming packets by any number of
+destination MAC addresses.[footnote:
+Since there are no guarentees, it can use a hash filter
+orsilently switch to allmulti or promiscuous mode if it is given
+too many addresses.
+] This table is set using the class VIRTIO_NET_CTRL_MAC and the
+command VIRTIO_NET_CTRL_MAC_TABLE_SET. The command-specific-data
+is two variable length tables of 6-byte MAC addresses. The first
+table contains unicast addresses, and the second contains
+multicast addresses.
+
+ VLAN Filtering
+
+If the driver negotiates the VIRTION_NET_F_CTRL_VLAN feature, it
+can control a VLAN filter table in the device.
+
+#define VIRTIO_NET_CTRL_VLAN 2
+
+ #define VIRTIO_NET_CTRL_VLAN_ADD 0
+
+ #define VIRTIO_NET_CTRL_VLAN_DEL 1
+
+Both the VIRTIO_NET_CTRL_VLAN_ADD and VIRTIO_NET_CTRL_VLAN_DEL
+command take a 16-bit VLAN id as the command-specific-data.
+
+Appendix D: Block Device
+
+The virtio block device is a simple virtual block device (ie.
+disk). Read and write requests (and other exotic requests) are
+placed in the queue, and serviced (probably out of order) by the
+device except where noted.
+
+ Configuration
+
+ Subsystem Device ID 2
+
+ Virtqueues 0:requestq.
+
+ Feature bits
+
+ VIRTIO_BLK_F_BARRIER (0) Host supports request barriers.
+
+ VIRTIO_BLK_F_SIZE_MAX (1) Maximum size of any single segment is
+ in “size_max”.
+
+ VIRTIO_BLK_F_SEG_MAX (2) Maximum number of segments in a
+ request is in “seg_max”.
+
+ VIRTIO_BLK_F_GEOMETRY (4) Disk-style geometry specified in “
+ geometry”.
+
+ VIRTIO_BLK_F_RO (5) Device is read-only.
+
+ VIRTIO_BLK_F_BLK_SIZE (6) Block size of disk is in “blk_size”.
+
+ VIRTIO_BLK_F_SCSI (7) Device supports scsi packet commands.
+
+ VIRTIO_BLK_F_FLUSH (9) Cache flush command support.
+
+
+
+ Device configuration layout The capacity of the device
+ (expressed in 512-byte sectors) is always present. The
+ availability of the others all depend on various feature bits
+ as indicated above. struct virtio_blk_config {
+
+ u64 capacity;
+
+ u32 size_max;
+
+ u32 seg_max;
+
+ struct virtio_blk_geometry {
+
+ u16 cylinders;
+
+ u8 heads;
+
+ u8 sectors;
+
+ } geometry;
+
+ u32 blk_size;
+
+
+
+};
+
+ Device Initialization
+
+ The device size should be read from the “capacity”
+ configuration field. No requests should be submitted which goes
+ beyond this limit.
+
+ If the VIRTIO_BLK_F_BLK_SIZE feature is negotiated, the
+ blk_size field can be read to determine the optimal sector size
+ for the driver to use. This does not effect the units used in
+ the protocol (always 512 bytes), but awareness of the correct
+ value can effect performance.
+
+ If the VIRTIO_BLK_F_RO feature is set by the device, any write
+ requests will fail.
+
+
+
+ Device Operation
+
+The driver queues requests to the virtqueue, and they are used by
+the device (not necessarily in order). Each request is of form:
+
+struct virtio_blk_req {
+
+
+
+ u32 type;
+
+ u32 ioprio;
+
+ u64 sector;
+
+ char data[][512];
+
+ u8 status;
+
+};
+
+If the device has VIRTIO_BLK_F_SCSI feature, it can also support
+scsi packet command requests, each of these requests is of form:struct virtio_scsi_pc_req {
+
+ u32 type;
+
+ u32 ioprio;
+
+ u64 sector;
+
+ char cmd[];
+
+ char data[][512];
+
+#define SCSI_SENSE_BUFFERSIZE 96
+
+ u8 sense[SCSI_SENSE_BUFFERSIZE];
+
+ u32 errors;
+
+ u32 data_len;
+
+ u32 sense_len;
+
+ u32 residual;
+
+ u8 status;
+
+};
+
+The type of the request is either a read (VIRTIO_BLK_T_IN), a
+write (VIRTIO_BLK_T_OUT), a scsi packet command
+(VIRTIO_BLK_T_SCSI_CMD or VIRTIO_BLK_T_SCSI_CMD_OUT[footnote:
+the SCSI_CMD and SCSI_CMD_OUT types are equivalent, the device
+does not distinguish between them
+]) or a flush (VIRTIO_BLK_T_FLUSH or VIRTIO_BLK_T_FLUSH_OUT[footnote:
+the FLUSH and FLUSH_OUT types are equivalent, the device does not
+distinguish between them
+]). If the device has VIRTIO_BLK_F_BARRIER feature the high bit
+(VIRTIO_BLK_T_BARRIER) indicates that this request acts as a
+barrier and that all preceeding requests must be complete before
+this one, and all following requests must not be started until
+this is complete. Note that a barrier does not flush caches in
+the underlying backend device in host, and thus does not serve as
+data consistency guarantee. Driver must use FLUSH request to
+flush the host cache.
+
+#define VIRTIO_BLK_T_IN 0
+
+#define VIRTIO_BLK_T_OUT 1
+
+#define VIRTIO_BLK_T_SCSI_CMD 2
+
+#define VIRTIO_BLK_T_SCSI_CMD_OUT 3
+
+#define VIRTIO_BLK_T_FLUSH 4
+
+#define VIRTIO_BLK_T_FLUSH_OUT 5
+
+#define VIRTIO_BLK_T_BARRIER 0x80000000
+
+The ioprio field is a hint about the relative priorities of
+requests to the device: higher numbers indicate more important
+requests.
+
+The sector number indicates the offset (multiplied by 512) where
+the read or write is to occur. This field is unused and set to 0
+for scsi packet commands and for flush commands.
+
+The cmd field is only present for scsi packet command requests,
+and indicates the command to perform. This field must reside in a
+single, separate read-only buffer; command length can be derived
+from the length of this buffer.
+
+Note that these first three (four for scsi packet commands)
+fields are always read-only: the data field is either read-only
+or write-only, depending on the request. The size of the read or
+write can be derived from the total size of the request buffers.
+
+The sense field is only present for scsi packet command requests,
+and indicates the buffer for scsi sense data.
+
+The data_len field is only present for scsi packet command
+requests, this field is deprecated, and should be ignored by the
+driver. Historically, devices copied data length there.
+
+The sense_len field is only present for scsi packet command
+requests and indicates the number of bytes actually written to
+the sense buffer.
+
+The residual field is only present for scsi packet command
+requests and indicates the residual size, calculated as data
+length - number of bytes actually transferred.
+
+The final status byte is written by the device: either
+VIRTIO_BLK_S_OK for success, VIRTIO_BLK_S_IOERR for host or guest
+error or VIRTIO_BLK_S_UNSUPP for a request unsupported by host:#define VIRTIO_BLK_S_OK 0
+
+#define VIRTIO_BLK_S_IOERR 1
+
+#define VIRTIO_BLK_S_UNSUPP 2
+
+Historically, devices assumed that the fields type, ioprio and
+sector reside in a single, separate read-only buffer; the fields
+errors, data_len, sense_len and residual reside in a single,
+separate write-only buffer; the sense field in a separate
+write-only buffer of size 96 bytes, by itself; the fields errors,
+data_len, sense_len and residual in a single write-only buffer;
+and the status field is a separate read-only buffer of size 1
+byte, by itself.
+
+Appendix E: Console Device
+
+The virtio console device is a simple device for data input and
+output. A device may have one or more ports. Each port has a pair
+of input and output virtqueues. Moreover, a device has a pair of
+control IO virtqueues. The control virtqueues are used to
+communicate information between the device and the driver about
+ports being opened and closed on either side of the connection,
+indication from the host about whether a particular port is a
+console port, adding new ports, port hot-plug/unplug, etc., and
+indication from the guest about whether a port or a device was
+successfully added, port open/close, etc.. For data IO, one or
+more empty buffers are placed in the receive queue for incoming
+data and outgoing characters are placed in the transmit queue.
+
+ Configuration
+
+ Subsystem Device ID 3
+
+ Virtqueues 0:receiveq(port0). 1:transmitq(port0), 2:control
+ receiveq[footnote:
+Ports 2 onwards only if VIRTIO_CONSOLE_F_MULTIPORT is set
+], 3:control transmitq, 4:receiveq(port1), 5:transmitq(port1),
+ ...
+
+ Feature bits
+
+ VIRTIO_CONSOLE_F_SIZE (0) Configuration cols and rows fields
+ are valid.
+
+ VIRTIO_CONSOLE_F_MULTIPORT(1) Device has support for multiple
+ ports; configuration fields nr_ports and max_nr_ports are
+ valid and control virtqueues will be used.
+
+ Device configuration layout The size of the console is supplied
+ in the configuration space if the VIRTIO_CONSOLE_F_SIZE feature
+ is set. Furthermore, if the VIRTIO_CONSOLE_F_MULTIPORT feature
+ is set, the maximum number of ports supported by the device can
+ be fetched.struct virtio_console_config {
+
+ u16 cols;
+
+ u16 rows;
+
+
+
+ u32 max_nr_ports;
+
+};
+
+ Device Initialization
+
+ If the VIRTIO_CONSOLE_F_SIZE feature is negotiated, the driver
+ can read the console dimensions from the configuration fields.
+
+ If the VIRTIO_CONSOLE_F_MULTIPORT feature is negotiated, the
+ driver can spawn multiple ports, not all of which may be
+ attached to a console. Some could be generic ports. In this
+ case, the control virtqueues are enabled and according to the
+ max_nr_ports configuration-space value, the appropriate number
+ of virtqueues are created. A control message indicating the
+ driver is ready is sent to the host. The host can then send
+ control messages for adding new ports to the device. After
+ creating and initializing each port, a
+ VIRTIO_CONSOLE_PORT_READY control message is sent to the host
+ for that port so the host can let us know of any additional
+ configuration options set for that port.
+
+ The receiveq for each port is populated with one or more
+ receive buffers.
+
+ Device Operation
+
+ For output, a buffer containing the characters is placed in the
+ port's transmitq.[footnote:
+Because this is high importance and low bandwidth, the current
+Linux implementation polls for the buffer to be used, rather than
+waiting for an interrupt, simplifying the implementation
+significantly. However, for generic serial ports with the
+O_NONBLOCK flag set, the polling limitation is relaxed and the
+consumed buffers are freed upon the next write or poll call or
+when a port is closed or hot-unplugged.
+]
+
+ When a buffer is used in the receiveq (signalled by an
+ interrupt), the contents is the input to the port associated
+ with the virtqueue for which the notification was received.
+
+ If the driver negotiated the VIRTIO_CONSOLE_F_SIZE feature, a
+ configuration change interrupt may occur. The updated size can
+ be read from the configuration fields.
+
+ If the driver negotiated the VIRTIO_CONSOLE_F_MULTIPORT
+ feature, active ports are announced by the host using the
+ VIRTIO_CONSOLE_PORT_ADD control message. The same message is
+ used for port hot-plug as well.
+
+ If the host specified a port `name', a sysfs attribute is
+ created with the name filled in, so that udev rules can be
+ written that can create a symlink from the port's name to the
+ char device for port discovery by applications in the guest.
+
+ Changes to ports' state are effected by control messages.
+ Appropriate action is taken on the port indicated in the
+ control message. The layout of the structure of the control
+ buffer and the events associated are:struct virtio_console_control {
+
+ uint32_t id; /* Port number */
+
+ uint16_t event; /* The kind of control event */
+
+ uint16_t value; /* Extra information for the event */
+
+};
+
+
+
+/* Some events for the internal messages (control packets) */
+
+
+
+#define VIRTIO_CONSOLE_DEVICE_READY 0
+
+#define VIRTIO_CONSOLE_PORT_ADD 1
+
+#define VIRTIO_CONSOLE_PORT_REMOVE 2
+
+#define VIRTIO_CONSOLE_PORT_READY 3
+
+#define VIRTIO_CONSOLE_CONSOLE_PORT 4
+
+#define VIRTIO_CONSOLE_RESIZE 5
+
+#define VIRTIO_CONSOLE_PORT_OPEN 6
+
+#define VIRTIO_CONSOLE_PORT_NAME 7
+
+Appendix F: Entropy Device
+
+The virtio entropy device supplies high-quality randomness for
+guest use.
+
+ Configuration
+
+ Subsystem Device ID 4
+
+ Virtqueues 0:requestq.
+
+ Feature bits None currently defined
+
+ Device configuration layout None currently defined.
+
+ Device Initialization
+
+ The virtqueue is initialized
+
+ Device Operation
+
+When the driver requires random bytes, it places the descriptor
+of one or more buffers in the queue. It will be completely filled
+by random data by the device.
+
+Appendix G: Memory Balloon Device
+
+The virtio memory balloon device is a primitive device for
+managing guest memory: the device asks for a certain amount of
+memory, and the guest supplies it (or withdraws it, if the device
+has more than it asks for). This allows the guest to adapt to
+changes in allowance of underlying physical memory. If the
+feature is negotiated, the device can also be used to communicate
+guest memory statistics to the host.
+
+ Configuration
+
+ Subsystem Device ID 5
+
+ Virtqueues 0:inflateq. 1:deflateq. 2:statsq.[footnote:
+Only if VIRTIO_BALLON_F_STATS_VQ set
+]
+
+ Feature bits
+
+ VIRTIO_BALLOON_F_MUST_TELL_HOST (0) Host must be told before
+ pages from the balloon are used.
+
+ VIRTIO_BALLOON_F_STATS_VQ (1) A virtqueue for reporting guest
+ memory statistics is present.
+
+ Device configuration layout Both fields of this configuration
+ are always available. Note that they are little endian, despite
+ convention that device fields are guest endian:struct virtio_balloon_config {
+
+ u32 num_pages;
+
+ u32 actual;
+
+};
+
+ Device Initialization
+
+ The inflate and deflate virtqueues are identified.
+
+ If the VIRTIO_BALLOON_F_STATS_VQ feature bit is negotiated:
+
+ Identify the stats virtqueue.
+
+ Add one empty buffer to the stats virtqueue and notify the
+ host.
+
+Device operation begins immediately.
+
+ Device Operation
+
+ Memory Ballooning The device is driven by the receipt of a
+ configuration change interrupt.
+
+ The “num_pages” configuration field is examined. If this is
+ greater than the “actual” number of pages, memory must be given
+ to the balloon. If it is less than the “actual” number of
+ pages, memory may be taken back from the balloon for general
+ use.
+
+ To supply memory to the balloon (aka. inflate):
+
+ The driver constructs an array of addresses of unused memory
+ pages. These addresses are divided by 4096[footnote:
+This is historical, and independent of the guest page size
+] and the descriptor describing the resulting 32-bit array is
+ added to the inflateq.
+
+ To remove memory from the balloon (aka. deflate):
+
+ The driver constructs an array of addresses of memory pages it
+ has previously given to the balloon, as described above. This
+ descriptor is added to the deflateq.
+
+ If the VIRTIO_BALLOON_F_MUST_TELL_HOST feature is set, the
+ guest may not use these requested pages until that descriptor
+ in the deflateq has been used by the device.
+
+ Otherwise, the guest may begin to re-use pages previously given
+ to the balloon before the device has acknowledged their
+ withdrawl. [footnote:
+In this case, deflation advice is merely a courtesy
+]
+
+ In either case, once the device has completed the inflation or
+ deflation, the “actual” field of the configuration should be
+ updated to reflect the new number of pages in the balloon.[footnote:
+As updates to configuration space are not atomic, this field
+isn't particularly reliable, but can be used to diagnose buggy
+guests.
+]
+
+ Memory Statistics
+
+The stats virtqueue is atypical because communication is driven
+by the device (not the driver). The channel becomes active at
+driver initialization time when the driver adds an empty buffer
+and notifies the device. A request for memory statistics proceeds
+as follows:
+
+ The device pushes the buffer onto the used ring and sends an
+ interrupt.
+
+ The driver pops the used buffer and discards it.
+
+ The driver collects memory statistics and writes them into a
+ new buffer.
+
+ The driver adds the buffer to the virtqueue and notifies the
+ device.
+
+ The device pops the buffer (retaining it to initiate a
+ subsequent request) and consumes the statistics.
+
+ Memory Statistics Format Each statistic consists of a 16 bit
+ tag and a 64 bit value. Both quantities are represented in the
+ native endian of the guest. All statistics are optional and the
+ driver may choose which ones to supply. To guarantee backwards
+ compatibility, unsupported statistics should be omitted.
+
+ struct virtio_balloon_stat {
+
+#define VIRTIO_BALLOON_S_SWAP_IN 0
+
+#define VIRTIO_BALLOON_S_SWAP_OUT 1
+
+#define VIRTIO_BALLOON_S_MAJFLT 2
+
+#define VIRTIO_BALLOON_S_MINFLT 3
+
+#define VIRTIO_BALLOON_S_MEMFREE 4
+
+#define VIRTIO_BALLOON_S_MEMTOT 5
+
+ u16 tag;
+
+ u64 val;
+
+} __attribute__((packed));
+
+ Tags
+
+ VIRTIO_BALLOON_S_SWAP_IN The amount of memory that has been
+ swapped in (in bytes).
+
+ VIRTIO_BALLOON_S_SWAP_OUT The amount of memory that has been
+ swapped out to disk (in bytes).
+
+ VIRTIO_BALLOON_S_MAJFLT The number of major page faults that
+ have occurred.
+
+ VIRTIO_BALLOON_S_MINFLT The number of minor page faults that
+ have occurred.
+
+ VIRTIO_BALLOON_S_MEMFREE The amount of memory not being used
+ for any purpose (in bytes).
+
+ VIRTIO_BALLOON_S_MEMTOT The total amount of memory available
+ (in bytes).
+
diff --git a/MAINTAINERS b/MAINTAINERS
index c9c6324..1d445f5 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -2643,9 +2643,8 @@
F: arch/x86/math-emu/
FRAME RELAY DLCI/FRAD (Sangoma drivers too)
-M: Mike McLagan <mike.mclagan@linux.org>
L: netdev@vger.kernel.org
-S: Maintained
+S: Orphan
F: drivers/net/wan/dlci.c
F: drivers/net/wan/sdla.c
@@ -3367,6 +3366,12 @@
F: drivers/net/ixgbe/
F: drivers/net/ixgbevf/
+INTEL MRST PMU DRIVER
+M: Len Brown <len.brown@intel.com>
+L: linux-pm@lists.linux-foundation.org
+S: Supported
+F: arch/x86/platform/mrst/pmu.*
+
INTEL PRO/WIRELESS 2100 NETWORK CONNECTION SUPPORT
L: linux-wireless@vger.kernel.org
S: Orphan
@@ -3900,9 +3905,9 @@
F: drivers/macintosh/
LINUX FOR POWERPC EMBEDDED MPC5XXX
-M: Grant Likely <grant.likely@secretlab.ca>
+M: Anatolij Gustschin <agust@denx.de>
L: linuxppc-dev@lists.ozlabs.org
-T: git git://git.secretlab.ca/git/linux-2.6.git
+T: git git://git.denx.de/linux-2.6-agust.git
S: Maintained
F: arch/powerpc/platforms/512x/
F: arch/powerpc/platforms/52xx/
@@ -4409,10 +4414,10 @@
F: net/netfilter/
NETLABEL
-M: Paul Moore <paul.moore@hp.com>
+M: Paul Moore <paul@paul-moore.com>
W: http://netlabel.sf.net
L: netdev@vger.kernel.org
-S: Supported
+S: Maintained
F: Documentation/netlabel/
F: include/net/netlabel.h
F: net/netlabel/
@@ -4457,7 +4462,6 @@
NETWORKING [IPv4/IPv6]
M: "David S. Miller" <davem@davemloft.net>
M: Alexey Kuznetsov <kuznet@ms2.inr.ac.ru>
-M: "Pekka Savola (ipv6)" <pekkas@netcore.fi>
M: James Morris <jmorris@namei.org>
M: Hideaki YOSHIFUJI <yoshfuji@linux-ipv6.org>
M: Patrick McHardy <kaber@trash.net>
@@ -4470,7 +4474,7 @@
F: arch/x86/net/*
NETWORKING [LABELED] (NetLabel, CIPSO, Labeled IPsec, SECMARK)
-M: Paul Moore <paul.moore@hp.com>
+M: Paul Moore <paul@paul-moore.com>
L: netdev@vger.kernel.org
S: Maintained
@@ -4600,7 +4604,7 @@
F: arch/arm/mach-omap2/clockdomain44xx.c
OMAP AUDIO SUPPORT
-M: Jarkko Nikula <jhnikula@gmail.com>
+M: Jarkko Nikula <jarkko.nikula@bitmer.com>
L: alsa-devel@alsa-project.org (subscribers-only)
L: linux-omap@vger.kernel.org
S: Maintained
@@ -4722,6 +4726,7 @@
F: drivers/of
F: include/linux/of*.h
K: of_get_property
+K: of_match_table
OPENRISC ARCHITECTURE
M: Jonas Bonn <jonas@southpole.se>
@@ -6318,6 +6323,7 @@
TARGET SUBSYSTEM
M: Nicholas A. Bellinger <nab@linux-iscsi.org>
L: linux-scsi@vger.kernel.org
+L: target-devel@vger.kernel.org
L: http://groups.google.com/group/linux-iscsi-target-dev
W: http://www.linux-iscsi.org
T: git git://git.kernel.org/pub/scm/linux/kernel/git/nab/lio-core-2.6.git master
@@ -7351,7 +7357,7 @@
M: Linus Torvalds <torvalds@linux-foundation.org>
L: linux-kernel@vger.kernel.org
Q: http://patchwork.kernel.org/project/LKML/list/
-T: git git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux-2.6.git
+T: git git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
S: Buried alive in reporters
F: *
F: */
diff --git a/Makefile b/Makefile
index f676d15..3241d41 100644
--- a/Makefile
+++ b/Makefile
@@ -1,8 +1,8 @@
VERSION = 3
-PATCHLEVEL = 0
+PATCHLEVEL = 1
SUBLEVEL = 0
-EXTRAVERSION =
-NAME = Sneaky Weasel
+EXTRAVERSION = -rc2
+NAME = Wet Seal
# *DOCUMENTATION*
# To see a list of typical targets execute "make help"
diff --git a/arch/Kconfig b/arch/Kconfig
index 26b0e23..4b0669c 100644
--- a/arch/Kconfig
+++ b/arch/Kconfig
@@ -178,4 +178,7 @@
config HAVE_RCU_TABLE_FREE
bool
+config ARCH_HAVE_NMI_SAFE_CMPXCHG
+ bool
+
source "kernel/gcov/Kconfig"
diff --git a/arch/alpha/Kconfig b/arch/alpha/Kconfig
index ca2da8d..60cde53 100644
--- a/arch/alpha/Kconfig
+++ b/arch/alpha/Kconfig
@@ -14,6 +14,7 @@
select AUTO_IRQ_AFFINITY if SMP
select GENERIC_IRQ_SHOW
select ARCH_WANT_OPTIONAL_GPIOLIB
+ select ARCH_HAVE_NMI_SAFE_CMPXCHG
help
The Alpha is a 64-bit general-purpose processor designed and
marketed by the Digital Equipment Corporation of blessed memory,
diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig
index 2c71a8f..5ebc5d9 100644
--- a/arch/arm/Kconfig
+++ b/arch/arm/Kconfig
@@ -195,8 +195,7 @@
The base address of exception vectors.
config ARM_PATCH_PHYS_VIRT
- bool "Patch physical to virtual translations at runtime (EXPERIMENTAL)"
- depends on EXPERIMENTAL
+ bool "Patch physical to virtual translations at runtime"
depends on !XIP_KERNEL && MMU
depends on !ARCH_REALVIEW || !SPARSEMEM
help
diff --git a/arch/arm/kernel/armksyms.c b/arch/arm/kernel/armksyms.c
index acca35a..aeef960 100644
--- a/arch/arm/kernel/armksyms.c
+++ b/arch/arm/kernel/armksyms.c
@@ -112,9 +112,6 @@
EXPORT_SYMBOL(__put_user_8);
#endif
- /* crypto hash */
-EXPORT_SYMBOL(sha_transform);
-
/* gcc lib functions */
EXPORT_SYMBOL(__ashldi3);
EXPORT_SYMBOL(__ashrdi3);
diff --git a/arch/arm/kernel/iwmmxt.S b/arch/arm/kernel/iwmmxt.S
index 7fa3bb0..a087838 100644
--- a/arch/arm/kernel/iwmmxt.S
+++ b/arch/arm/kernel/iwmmxt.S
@@ -195,10 +195,10 @@
@ enable access to CP0 and CP1
XSC(mrc p15, 0, r4, c15, c1, 0)
- XSC(orr r4, r4, #0xf)
+ XSC(orr r4, r4, #0x3)
XSC(mcr p15, 0, r4, c15, c1, 0)
PJ4(mrc p15, 0, r4, c1, c0, 2)
- PJ4(orr r4, r4, #0x3)
+ PJ4(orr r4, r4, #0xf)
PJ4(mcr p15, 0, r4, c1, c0, 2)
mov r0, #0 @ nothing to load
@@ -313,7 +313,7 @@
teq r2, r3 @ next task owns it?
movne pc, lr @ no: leave Concan disabled
-1: @ flip Conan access
+1: @ flip Concan access
XSC(eor r1, r1, #0x3)
XSC(mcr p15, 0, r1, c15, c1, 0)
PJ4(eor r1, r1, #0xf)
diff --git a/arch/arm/kernel/module.c b/arch/arm/kernel/module.c
index 05b3776..cc2020c 100644
--- a/arch/arm/kernel/module.c
+++ b/arch/arm/kernel/module.c
@@ -323,7 +323,11 @@
#endif
s = find_mod_section(hdr, sechdrs, ".alt.smp.init");
if (s && !is_smp())
+#ifdef CONFIG_SMP_ON_UP
fixup_smp((void *)s->sh_addr, s->sh_size);
+#else
+ return -EINVAL;
+#endif
return 0;
}
diff --git a/arch/arm/kernel/process.c b/arch/arm/kernel/process.c
index 5e1e541..1a347f4 100644
--- a/arch/arm/kernel/process.c
+++ b/arch/arm/kernel/process.c
@@ -30,6 +30,7 @@
#include <linux/uaccess.h>
#include <linux/random.h>
#include <linux/hw_breakpoint.h>
+#include <linux/cpuidle.h>
#include <asm/cacheflush.h>
#include <asm/leds.h>
@@ -196,7 +197,8 @@
cpu_relax();
} else {
stop_critical_timings();
- pm_idle();
+ if (cpuidle_idle_call())
+ pm_idle();
start_critical_timings();
/*
* This will eventually be removed - pm_idle
diff --git a/arch/arm/lib/Makefile b/arch/arm/lib/Makefile
index 59ff42d..cf73a7f 100644
--- a/arch/arm/lib/Makefile
+++ b/arch/arm/lib/Makefile
@@ -12,7 +12,7 @@
strchr.o strrchr.o \
testchangebit.o testclearbit.o testsetbit.o \
ashldi3.o ashrdi3.o lshrdi3.o muldi3.o \
- ucmpdi2.o lib1funcs.o div64.o sha1.o \
+ ucmpdi2.o lib1funcs.o div64.o \
io-readsb.o io-writesb.o io-readsl.o io-writesl.o
mmu-y := clear_user.o copy_page.o getuser.o putuser.o
diff --git a/arch/arm/lib/sha1.S b/arch/arm/lib/sha1.S
deleted file mode 100644
index eb0edb8..0000000
--- a/arch/arm/lib/sha1.S
+++ /dev/null
@@ -1,211 +0,0 @@
-/*
- * linux/arch/arm/lib/sha1.S
- *
- * SHA transform optimized for ARM
- *
- * Copyright: (C) 2005 by Nicolas Pitre <nico@fluxnic.net>
- * Created: September 17, 2005
- *
- * 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.
- *
- * The reference implementation for this code is linux/lib/sha1.c
- */
-
-#include <linux/linkage.h>
-
- .text
-
-
-/*
- * void sha_transform(__u32 *digest, const char *in, __u32 *W)
- *
- * Note: the "in" ptr may be unaligned.
- */
-
-ENTRY(sha_transform)
-
- stmfd sp!, {r4 - r8, lr}
-
- @ for (i = 0; i < 16; i++)
- @ W[i] = be32_to_cpu(in[i]);
-
-#ifdef __ARMEB__
- mov r4, r0
- mov r0, r2
- mov r2, #64
- bl memcpy
- mov r2, r0
- mov r0, r4
-#else
- mov r3, r2
- mov lr, #16
-1: ldrb r4, [r1], #1
- ldrb r5, [r1], #1
- ldrb r6, [r1], #1
- ldrb r7, [r1], #1
- subs lr, lr, #1
- orr r5, r5, r4, lsl #8
- orr r6, r6, r5, lsl #8
- orr r7, r7, r6, lsl #8
- str r7, [r3], #4
- bne 1b
-#endif
-
- @ for (i = 0; i < 64; i++)
- @ W[i+16] = ror(W[i+13] ^ W[i+8] ^ W[i+2] ^ W[i], 31);
-
- sub r3, r2, #4
- mov lr, #64
-2: ldr r4, [r3, #4]!
- subs lr, lr, #1
- ldr r5, [r3, #8]
- ldr r6, [r3, #32]
- ldr r7, [r3, #52]
- eor r4, r4, r5
- eor r4, r4, r6
- eor r4, r4, r7
- mov r4, r4, ror #31
- str r4, [r3, #64]
- bne 2b
-
- /*
- * The SHA functions are:
- *
- * f1(B,C,D) = (D ^ (B & (C ^ D)))
- * f2(B,C,D) = (B ^ C ^ D)
- * f3(B,C,D) = ((B & C) | (D & (B | C)))
- *
- * Then the sub-blocks are processed as follows:
- *
- * A' = ror(A, 27) + f(B,C,D) + E + K + *W++
- * B' = A
- * C' = ror(B, 2)
- * D' = C
- * E' = D
- *
- * We therefore unroll each loop 5 times to avoid register shuffling.
- * Also the ror for C (and also D and E which are successivelyderived
- * from it) is applied in place to cut on an additional mov insn for
- * each round.
- */
-
- .macro sha_f1, A, B, C, D, E
- ldr r3, [r2], #4
- eor ip, \C, \D
- add \E, r1, \E, ror #2
- and ip, \B, ip, ror #2
- add \E, \E, \A, ror #27
- eor ip, ip, \D, ror #2
- add \E, \E, r3
- add \E, \E, ip
- .endm
-
- .macro sha_f2, A, B, C, D, E
- ldr r3, [r2], #4
- add \E, r1, \E, ror #2
- eor ip, \B, \C, ror #2
- add \E, \E, \A, ror #27
- eor ip, ip, \D, ror #2
- add \E, \E, r3
- add \E, \E, ip
- .endm
-
- .macro sha_f3, A, B, C, D, E
- ldr r3, [r2], #4
- add \E, r1, \E, ror #2
- orr ip, \B, \C, ror #2
- add \E, \E, \A, ror #27
- and ip, ip, \D, ror #2
- add \E, \E, r3
- and r3, \B, \C, ror #2
- orr ip, ip, r3
- add \E, \E, ip
- .endm
-
- ldmia r0, {r4 - r8}
-
- mov lr, #4
- ldr r1, .L_sha_K + 0
-
- /* adjust initial values */
- mov r6, r6, ror #30
- mov r7, r7, ror #30
- mov r8, r8, ror #30
-
-3: subs lr, lr, #1
- sha_f1 r4, r5, r6, r7, r8
- sha_f1 r8, r4, r5, r6, r7
- sha_f1 r7, r8, r4, r5, r6
- sha_f1 r6, r7, r8, r4, r5
- sha_f1 r5, r6, r7, r8, r4
- bne 3b
-
- ldr r1, .L_sha_K + 4
- mov lr, #4
-
-4: subs lr, lr, #1
- sha_f2 r4, r5, r6, r7, r8
- sha_f2 r8, r4, r5, r6, r7
- sha_f2 r7, r8, r4, r5, r6
- sha_f2 r6, r7, r8, r4, r5
- sha_f2 r5, r6, r7, r8, r4
- bne 4b
-
- ldr r1, .L_sha_K + 8
- mov lr, #4
-
-5: subs lr, lr, #1
- sha_f3 r4, r5, r6, r7, r8
- sha_f3 r8, r4, r5, r6, r7
- sha_f3 r7, r8, r4, r5, r6
- sha_f3 r6, r7, r8, r4, r5
- sha_f3 r5, r6, r7, r8, r4
- bne 5b
-
- ldr r1, .L_sha_K + 12
- mov lr, #4
-
-6: subs lr, lr, #1
- sha_f2 r4, r5, r6, r7, r8
- sha_f2 r8, r4, r5, r6, r7
- sha_f2 r7, r8, r4, r5, r6
- sha_f2 r6, r7, r8, r4, r5
- sha_f2 r5, r6, r7, r8, r4
- bne 6b
-
- ldmia r0, {r1, r2, r3, ip, lr}
- add r4, r1, r4
- add r5, r2, r5
- add r6, r3, r6, ror #2
- add r7, ip, r7, ror #2
- add r8, lr, r8, ror #2
- stmia r0, {r4 - r8}
-
- ldmfd sp!, {r4 - r8, pc}
-
-ENDPROC(sha_transform)
-
- .align 2
-.L_sha_K:
- .word 0x5a827999, 0x6ed9eba1, 0x8f1bbcdc, 0xca62c1d6
-
-
-/*
- * void sha_init(__u32 *buf)
- */
-
- .align 2
-.L_sha_initial_digest:
- .word 0x67452301, 0xefcdab89, 0x98badcfe, 0x10325476, 0xc3d2e1f0
-
-ENTRY(sha_init)
-
- str lr, [sp, #-4]!
- adr r1, .L_sha_initial_digest
- ldmia r1, {r1, r2, r3, ip, lr}
- stmia r0, {r1, r2, r3, ip, lr}
- ldr pc, [sp], #4
-
-ENDPROC(sha_init)
diff --git a/arch/arm/mach-imx/clock-imx25.c b/arch/arm/mach-imx/clock-imx25.c
index 0fc7ba5..e63e235 100644
--- a/arch/arm/mach-imx/clock-imx25.c
+++ b/arch/arm/mach-imx/clock-imx25.c
@@ -331,6 +331,9 @@
__raw_writel(__raw_readl(CRM_BASE+0x64) | (1 << 7) | (1 << 0),
CRM_BASE + 0x64);
+ /* Clock source for gpt is ahb_div */
+ __raw_writel(__raw_readl(CRM_BASE+0x64) & ~(1 << 5), CRM_BASE + 0x64);
+
mxc_timer_init(&gpt_clk, MX25_IO_ADDRESS(MX25_GPT1_BASE_ADDR), 54);
return 0;
diff --git a/arch/arm/mach-imx/mach-imx27_visstrim_m10.c b/arch/arm/mach-imx/mach-imx27_visstrim_m10.c
index 6707de0a..6778f81 100644
--- a/arch/arm/mach-imx/mach-imx27_visstrim_m10.c
+++ b/arch/arm/mach-imx/mach-imx27_visstrim_m10.c
@@ -30,6 +30,7 @@
#include <linux/input.h>
#include <linux/gpio.h>
#include <linux/delay.h>
+#include <sound/tlv320aic32x4.h>
#include <asm/mach-types.h>
#include <asm/mach/arch.h>
#include <asm/mach/time.h>
@@ -196,6 +197,17 @@
.invert = 0,
};
+static struct aic32x4_pdata visstrim_m10_aic32x4_pdata = {
+ .power_cfg = AIC32X4_PWR_MICBIAS_2075_LDOIN |
+ AIC32X4_PWR_AVDD_DVDD_WEAK_DISABLE |
+ AIC32X4_PWR_AIC32X4_LDO_ENABLE |
+ AIC32X4_PWR_CMMODE_LDOIN_RANGE_18_36 |
+ AIC32X4_PWR_CMMODE_HP_LDOIN_POWERED,
+ .micpga_routing = AIC32X4_MICPGA_ROUTE_LMIC_IN2R_10K |
+ AIC32X4_MICPGA_ROUTE_RMIC_IN1L_10K,
+ .swapdacs = false,
+};
+
static struct i2c_board_info visstrim_m10_i2c_devices[] = {
{
I2C_BOARD_INFO("pca9555", 0x20),
@@ -203,6 +215,7 @@
},
{
I2C_BOARD_INFO("tlv320aic32x4", 0x18),
+ .platform_data = &visstrim_m10_aic32x4_pdata,
}
};
diff --git a/arch/arm/mach-imx/mach-mx31ads.c b/arch/arm/mach-imx/mach-mx31ads.c
index 0ce4947..29ca890 100644
--- a/arch/arm/mach-imx/mach-mx31ads.c
+++ b/arch/arm/mach-imx/mach-mx31ads.c
@@ -468,7 +468,7 @@
#endif
};
-static void mxc_init_i2c(void)
+static void __init mxc_init_i2c(void)
{
i2c_register_board_info(1, mx31ads_i2c1_devices,
ARRAY_SIZE(mx31ads_i2c1_devices));
@@ -486,7 +486,7 @@
MX31_PIN_STXD5__STXD5,
};
-static void mxc_init_audio(void)
+static void __init mxc_init_audio(void)
{
imx31_add_imx_ssi(0, NULL);
mxc_iomux_setup_multiple_pins(ssi_pins, ARRAY_SIZE(ssi_pins), "ssi");
diff --git a/arch/arm/mach-imx/mach-mx31lilly.c b/arch/arm/mach-imx/mach-mx31lilly.c
index 750368d..126913a 100644
--- a/arch/arm/mach-imx/mach-mx31lilly.c
+++ b/arch/arm/mach-imx/mach-mx31lilly.c
@@ -192,7 +192,7 @@
.portsc = MXC_EHCI_MODE_ULPI | MXC_EHCI_UTMI_8BIT,
};
-static void lilly1131_usb_init(void)
+static void __init lilly1131_usb_init(void)
{
imx31_add_mxc_ehci_hs(1, &usbh1_pdata);
diff --git a/arch/arm/mach-mmp/gplugd.c b/arch/arm/mach-mmp/gplugd.c
index c070c24..98e25d9 100644
--- a/arch/arm/mach-mmp/gplugd.c
+++ b/arch/arm/mach-mmp/gplugd.c
@@ -16,16 +16,18 @@
#include <mach/gpio.h>
#include <mach/pxa168.h>
#include <mach/mfp-pxa168.h>
-#include <mach/mfp-gplugd.h>
#include "common.h"
static unsigned long gplugd_pin_config[] __initdata = {
/* UART3 */
- GPIO8_UART3_SOUT,
- GPIO9_UART3_SIN,
- GPI1O_UART3_CTS,
- GPI11_UART3_RTS,
+ GPIO8_UART3_TXD,
+ GPIO9_UART3_RXD,
+ GPIO1O_UART3_CTS,
+ GPIO11_UART3_RTS,
+
+ /* USB OTG PEN */
+ GPIO18_GPIO,
/* MMC2 */
GPIO28_MMC2_CMD,
@@ -109,6 +111,12 @@
GPIO105_CI2C_SDA,
GPIO106_CI2C_SCL,
+ /* SPI NOR Flash on SSP2 */
+ GPIO107_SSP2_RXD,
+ GPIO108_SSP2_TXD,
+ GPIO110_GPIO, /* SPI_CSn */
+ GPIO111_SSP2_CLK,
+
/* Select JTAG */
GPIO109_GPIO,
@@ -154,7 +162,7 @@
"frequency\n");
} else {
gpio_direction_output(35, 1);
- gpio_free(104);
+ gpio_free(35);
}
if (unlikely(gpio_request(85, "DISP_FREQ_SEL_2"))) {
@@ -162,7 +170,7 @@
"frequency\n");
} else {
gpio_direction_output(85, 0);
- gpio_free(104);
+ gpio_free(85);
}
}
diff --git a/arch/arm/mach-mmp/include/mach/mfp-gplugd.h b/arch/arm/mach-mmp/include/mach/mfp-gplugd.h
deleted file mode 100644
index b8cf38d..0000000
--- a/arch/arm/mach-mmp/include/mach/mfp-gplugd.h
+++ /dev/null
@@ -1,52 +0,0 @@
-/*
- * linux/arch/arm/mach-mmp/include/mach/mfp-gplugd.h
- *
- * MFP definitions used in gplugD
- *
- * 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 __MACH_MFP_GPLUGD_H
-#define __MACH_MFP_GPLUGD_H
-
-#include <plat/mfp.h>
-#include <mach/mfp.h>
-
-/* UART3 */
-#define GPIO8_UART3_SOUT MFP_CFG(GPIO8, AF2)
-#define GPIO9_UART3_SIN MFP_CFG(GPIO9, AF2)
-#define GPI1O_UART3_CTS MFP_CFG(GPIO10, AF2)
-#define GPI11_UART3_RTS MFP_CFG(GPIO11, AF2)
-
-/* MMC2 */
-#define GPIO28_MMC2_CMD MFP_CFG_DRV(GPIO28, AF6, FAST)
-#define GPIO29_MMC2_CLK MFP_CFG_DRV(GPIO29, AF6, FAST)
-#define GPIO30_MMC2_DAT0 MFP_CFG_DRV(GPIO30, AF6, FAST)
-#define GPIO31_MMC2_DAT1 MFP_CFG_DRV(GPIO31, AF6, FAST)
-#define GPIO32_MMC2_DAT2 MFP_CFG_DRV(GPIO32, AF6, FAST)
-#define GPIO33_MMC2_DAT3 MFP_CFG_DRV(GPIO33, AF6, FAST)
-
-/* I2S */
-#undef GPIO114_I2S_FRM
-#undef GPIO115_I2S_BCLK
-
-#define GPIO114_I2S_FRM MFP_CFG_DRV(GPIO114, AF1, FAST)
-#define GPIO115_I2S_BCLK MFP_CFG_DRV(GPIO115, AF1, FAST)
-#define GPIO116_I2S_TXD MFP_CFG_DRV(GPIO116, AF1, FAST)
-
-/* MMC4 */
-#define GPIO125_MMC4_DAT3 MFP_CFG_DRV(GPIO125, AF7, FAST)
-#define GPIO126_MMC4_DAT2 MFP_CFG_DRV(GPIO126, AF7, FAST)
-#define GPIO127_MMC4_DAT1 MFP_CFG_DRV(GPIO127, AF7, FAST)
-#define GPIO0_2_MMC4_DAT0 MFP_CFG_DRV(GPIO0_2, AF7, FAST)
-#define GPIO1_2_MMC4_CMD MFP_CFG_DRV(GPIO1_2, AF7, FAST)
-#define GPIO2_2_MMC4_CLK MFP_CFG_DRV(GPIO2_2, AF7, FAST)
-
-/* OTG GPIO */
-#define GPIO_USB_OTG_PEN 18
-#define GPIO_USB_OIDIR 20
-
-/* Other GPIOs are 35, 84, 85 */
-#endif /* __MACH_MFP_GPLUGD_H */
diff --git a/arch/arm/mach-mmp/include/mach/mfp-pxa168.h b/arch/arm/mach-mmp/include/mach/mfp-pxa168.h
index 8c78232..92aaa3c 100644
--- a/arch/arm/mach-mmp/include/mach/mfp-pxa168.h
+++ b/arch/arm/mach-mmp/include/mach/mfp-pxa168.h
@@ -203,6 +203,10 @@
#define GPIO33_CF_nCD2 MFP_CFG(GPIO33, AF3)
/* UART */
+#define GPIO8_UART3_TXD MFP_CFG(GPIO8, AF2)
+#define GPIO9_UART3_RXD MFP_CFG(GPIO9, AF2)
+#define GPIO1O_UART3_CTS MFP_CFG(GPIO10, AF2)
+#define GPIO11_UART3_RTS MFP_CFG(GPIO11, AF2)
#define GPIO88_UART2_TXD MFP_CFG(GPIO88, AF2)
#define GPIO89_UART2_RXD MFP_CFG(GPIO89, AF2)
#define GPIO107_UART1_TXD MFP_CFG_DRV(GPIO107, AF1, FAST)
@@ -232,6 +236,22 @@
#define GPIO53_MMC1_CD MFP_CFG(GPIO53, AF1)
#define GPIO46_MMC1_WP MFP_CFG(GPIO46, AF1)
+/* MMC2 */
+#define GPIO28_MMC2_CMD MFP_CFG_DRV(GPIO28, AF6, FAST)
+#define GPIO29_MMC2_CLK MFP_CFG_DRV(GPIO29, AF6, FAST)
+#define GPIO30_MMC2_DAT0 MFP_CFG_DRV(GPIO30, AF6, FAST)
+#define GPIO31_MMC2_DAT1 MFP_CFG_DRV(GPIO31, AF6, FAST)
+#define GPIO32_MMC2_DAT2 MFP_CFG_DRV(GPIO32, AF6, FAST)
+#define GPIO33_MMC2_DAT3 MFP_CFG_DRV(GPIO33, AF6, FAST)
+
+/* MMC4 */
+#define GPIO125_MMC4_DAT3 MFP_CFG_DRV(GPIO125, AF7, FAST)
+#define GPIO126_MMC4_DAT2 MFP_CFG_DRV(GPIO126, AF7, FAST)
+#define GPIO127_MMC4_DAT1 MFP_CFG_DRV(GPIO127, AF7, FAST)
+#define GPIO0_2_MMC4_DAT0 MFP_CFG_DRV(GPIO0_2, AF7, FAST)
+#define GPIO1_2_MMC4_CMD MFP_CFG_DRV(GPIO1_2, AF7, FAST)
+#define GPIO2_2_MMC4_CLK MFP_CFG_DRV(GPIO2_2, AF7, FAST)
+
/* LCD */
#define GPIO84_LCD_CS MFP_CFG(GPIO84, AF1)
#define GPIO60_LCD_DD0 MFP_CFG(GPIO60, AF1)
@@ -269,11 +289,12 @@
#define GPIO106_CI2C_SCL MFP_CFG(GPIO106, AF1)
/* I2S */
-#define GPIO113_I2S_MCLK MFP_CFG(GPIO113,AF6)
-#define GPIO114_I2S_FRM MFP_CFG(GPIO114,AF1)
-#define GPIO115_I2S_BCLK MFP_CFG(GPIO115,AF1)
-#define GPIO116_I2S_RXD MFP_CFG(GPIO116,AF2)
-#define GPIO117_I2S_TXD MFP_CFG(GPIO117,AF2)
+#define GPIO113_I2S_MCLK MFP_CFG(GPIO113, AF6)
+#define GPIO114_I2S_FRM MFP_CFG(GPIO114, AF1)
+#define GPIO115_I2S_BCLK MFP_CFG(GPIO115, AF1)
+#define GPIO116_I2S_RXD MFP_CFG(GPIO116, AF2)
+#define GPIO116_I2S_TXD MFP_CFG(GPIO116, AF1)
+#define GPIO117_I2S_TXD MFP_CFG(GPIO117, AF2)
/* PWM */
#define GPIO96_PWM3_OUT MFP_CFG(GPIO96, AF1)
@@ -324,4 +345,10 @@
#define GPIO101_MII_MDIO MFP_CFG(GPIO101, AF5)
#define GPIO103_RX_DV MFP_CFG(GPIO103, AF5)
+/* SSP2 */
+#define GPIO107_SSP2_RXD MFP_CFG(GPIO107, AF4)
+#define GPIO108_SSP2_TXD MFP_CFG(GPIO108, AF4)
+#define GPIO111_SSP2_CLK MFP_CFG(GPIO111, AF4)
+#define GPIO112_SSP2_FRM MFP_CFG(GPIO112, AF4)
+
#endif /* __ASM_MACH_MFP_PXA168_H */
diff --git a/arch/arm/mach-mmp/time.c b/arch/arm/mach-mmp/time.c
index 99833b9..4e91ee6 100644
--- a/arch/arm/mach-mmp/time.c
+++ b/arch/arm/mach-mmp/time.c
@@ -51,12 +51,12 @@
{
int delay = 100;
- __raw_writel(1, TIMERS_VIRT_BASE + TMR_CVWR(0));
+ __raw_writel(1, TIMERS_VIRT_BASE + TMR_CVWR(1));
while (delay--)
cpu_relax();
- return __raw_readl(TIMERS_VIRT_BASE + TMR_CVWR(0));
+ return __raw_readl(TIMERS_VIRT_BASE + TMR_CVWR(1));
}
unsigned long long notrace sched_clock(void)
@@ -75,28 +75,51 @@
{
struct clock_event_device *c = dev_id;
- /* disable and clear pending interrupt status */
- __raw_writel(0x0, TIMERS_VIRT_BASE + TMR_IER(0));
- __raw_writel(0x1, TIMERS_VIRT_BASE + TMR_ICR(0));
+ /*
+ * Clear pending interrupt status.
+ */
+ __raw_writel(0x01, TIMERS_VIRT_BASE + TMR_ICR(0));
+
+ /*
+ * Disable timer 0.
+ */
+ __raw_writel(0x02, TIMERS_VIRT_BASE + TMR_CER);
+
c->event_handler(c);
+
return IRQ_HANDLED;
}
static int timer_set_next_event(unsigned long delta,
struct clock_event_device *dev)
{
- unsigned long flags, next;
+ unsigned long flags;
local_irq_save(flags);
- /* clear pending interrupt status and enable */
+ /*
+ * Disable timer 0.
+ */
+ __raw_writel(0x02, TIMERS_VIRT_BASE + TMR_CER);
+
+ /*
+ * Clear and enable timer match 0 interrupt.
+ */
__raw_writel(0x01, TIMERS_VIRT_BASE + TMR_ICR(0));
__raw_writel(0x01, TIMERS_VIRT_BASE + TMR_IER(0));
- next = timer_read() + delta;
- __raw_writel(next, TIMERS_VIRT_BASE + TMR_TN_MM(0, 0));
+ /*
+ * Setup new clockevent timer value.
+ */
+ __raw_writel(delta - 1, TIMERS_VIRT_BASE + TMR_TN_MM(0, 0));
+
+ /*
+ * Enable timer 0.
+ */
+ __raw_writel(0x03, TIMERS_VIRT_BASE + TMR_CER);
local_irq_restore(flags);
+
return 0;
}
@@ -145,23 +168,26 @@
static void __init timer_config(void)
{
uint32_t ccr = __raw_readl(TIMERS_VIRT_BASE + TMR_CCR);
- uint32_t cer = __raw_readl(TIMERS_VIRT_BASE + TMR_CER);
- uint32_t cmr = __raw_readl(TIMERS_VIRT_BASE + TMR_CMR);
- __raw_writel(cer & ~0x1, TIMERS_VIRT_BASE + TMR_CER); /* disable */
+ __raw_writel(0x0, TIMERS_VIRT_BASE + TMR_CER); /* disable */
- ccr &= (cpu_is_mmp2()) ? TMR_CCR_CS_0(0) : TMR_CCR_CS_0(3);
+ ccr &= (cpu_is_mmp2()) ? (TMR_CCR_CS_0(0) | TMR_CCR_CS_1(0)) :
+ (TMR_CCR_CS_0(3) | TMR_CCR_CS_1(3));
__raw_writel(ccr, TIMERS_VIRT_BASE + TMR_CCR);
- /* free-running mode */
- __raw_writel(cmr | 0x01, TIMERS_VIRT_BASE + TMR_CMR);
+ /* set timer 0 to periodic mode, and timer 1 to free-running mode */
+ __raw_writel(0x2, TIMERS_VIRT_BASE + TMR_CMR);
- __raw_writel(0x0, TIMERS_VIRT_BASE + TMR_PLCR(0)); /* free-running */
+ __raw_writel(0x1, TIMERS_VIRT_BASE + TMR_PLCR(0)); /* periodic */
__raw_writel(0x7, TIMERS_VIRT_BASE + TMR_ICR(0)); /* clear status */
__raw_writel(0x0, TIMERS_VIRT_BASE + TMR_IER(0));
- /* enable timer counter */
- __raw_writel(cer | 0x01, TIMERS_VIRT_BASE + TMR_CER);
+ __raw_writel(0x0, TIMERS_VIRT_BASE + TMR_PLCR(1)); /* free-running */
+ __raw_writel(0x7, TIMERS_VIRT_BASE + TMR_ICR(1)); /* clear status */
+ __raw_writel(0x0, TIMERS_VIRT_BASE + TMR_IER(1));
+
+ /* enable timer 1 counter */
+ __raw_writel(0x2, TIMERS_VIRT_BASE + TMR_CER);
}
static struct irqaction timer_irq = {
diff --git a/arch/arm/mach-msm/Kconfig b/arch/arm/mach-msm/Kconfig
index 888e925..ebde97f 100644
--- a/arch/arm/mach-msm/Kconfig
+++ b/arch/arm/mach-msm/Kconfig
@@ -11,6 +11,7 @@
select MSM_SMD
select MSM_SMD_PKG3
select CPU_V6
+ select GPIO_MSM_V1
select MSM_PROC_COMM
select HAS_MSM_DEBUG_UART_PHYS
@@ -22,6 +23,7 @@
select MSM_VIC
select CPU_V7
select MSM_GPIOMUX
+ select GPIO_MSM_V1
select MSM_PROC_COMM
select HAS_MSM_DEBUG_UART_PHYS
@@ -33,6 +35,7 @@
select MSM_VIC
select CPU_V7
select MSM_GPIOMUX
+ select GPIO_MSM_V1
select MSM_PROC_COMM
select HAS_MSM_DEBUG_UART_PHYS
@@ -44,6 +47,7 @@
select ARM_GIC
select CPU_V7
select MSM_V2_TLMM
+ select GPIO_MSM_V2
select MSM_GPIOMUX
select MSM_SCM if SMP
diff --git a/arch/arm/mach-msm/Makefile b/arch/arm/mach-msm/Makefile
index b70658c..4285dfd 100644
--- a/arch/arm/mach-msm/Makefile
+++ b/arch/arm/mach-msm/Makefile
@@ -29,11 +29,3 @@
obj-$(CONFIG_ARCH_MSM7X30) += gpiomux-v1.o gpiomux.o
obj-$(CONFIG_ARCH_QSD8X50) += gpiomux-8x50.o gpiomux-v1.o gpiomux.o
obj-$(CONFIG_ARCH_MSM8X60) += gpiomux-8x60.o gpiomux-v2.o gpiomux.o
-ifdef CONFIG_MSM_V2_TLMM
-ifndef CONFIG_ARCH_MSM8960
-# TODO: TLMM Mapping issues need to be resolved
-obj-y += gpio-v2.o
-endif
-else
-obj-y += gpio.o
-endif
diff --git a/arch/arm/mach-msm/gpio.c b/arch/arm/mach-msm/gpio.c
deleted file mode 100644
index 5ea273b..0000000
--- a/arch/arm/mach-msm/gpio.c
+++ /dev/null
@@ -1,376 +0,0 @@
-/* linux/arch/arm/mach-msm/gpio.c
- *
- * Copyright (C) 2007 Google, Inc.
- * Copyright (c) 2009-2010, Code Aurora Forum. All rights reserved.
- *
- * This software is licensed under the terms of the GNU General Public
- * License version 2, as published by the Free Software Foundation, and
- * may be copied, distributed, and modified under those terms.
- *
- * 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.
- *
- */
-
-#include <linux/bitops.h>
-#include <linux/gpio.h>
-#include <linux/interrupt.h>
-#include <linux/io.h>
-#include <linux/irq.h>
-#include <linux/module.h>
-#include "gpio_hw.h"
-#include "gpiomux.h"
-
-#define FIRST_GPIO_IRQ MSM_GPIO_TO_INT(0)
-
-#define MSM_GPIO_BANK(bank, first, last) \
- { \
- .regs = { \
- .out = MSM_GPIO_OUT_##bank, \
- .in = MSM_GPIO_IN_##bank, \
- .int_status = MSM_GPIO_INT_STATUS_##bank, \
- .int_clear = MSM_GPIO_INT_CLEAR_##bank, \
- .int_en = MSM_GPIO_INT_EN_##bank, \
- .int_edge = MSM_GPIO_INT_EDGE_##bank, \
- .int_pos = MSM_GPIO_INT_POS_##bank, \
- .oe = MSM_GPIO_OE_##bank, \
- }, \
- .chip = { \
- .base = (first), \
- .ngpio = (last) - (first) + 1, \
- .get = msm_gpio_get, \
- .set = msm_gpio_set, \
- .direction_input = msm_gpio_direction_input, \
- .direction_output = msm_gpio_direction_output, \
- .to_irq = msm_gpio_to_irq, \
- .request = msm_gpio_request, \
- .free = msm_gpio_free, \
- } \
- }
-
-#define MSM_GPIO_BROKEN_INT_CLEAR 1
-
-struct msm_gpio_regs {
- void __iomem *out;
- void __iomem *in;
- void __iomem *int_status;
- void __iomem *int_clear;
- void __iomem *int_en;
- void __iomem *int_edge;
- void __iomem *int_pos;
- void __iomem *oe;
-};
-
-struct msm_gpio_chip {
- spinlock_t lock;
- struct gpio_chip chip;
- struct msm_gpio_regs regs;
-#if MSM_GPIO_BROKEN_INT_CLEAR
- unsigned int_status_copy;
-#endif
- unsigned int both_edge_detect;
- unsigned int int_enable[2]; /* 0: awake, 1: sleep */
-};
-
-static int msm_gpio_write(struct msm_gpio_chip *msm_chip,
- unsigned offset, unsigned on)
-{
- unsigned mask = BIT(offset);
- unsigned val;
-
- val = readl(msm_chip->regs.out);
- if (on)
- writel(val | mask, msm_chip->regs.out);
- else
- writel(val & ~mask, msm_chip->regs.out);
- return 0;
-}
-
-static void msm_gpio_update_both_edge_detect(struct msm_gpio_chip *msm_chip)
-{
- int loop_limit = 100;
- unsigned pol, val, val2, intstat;
- do {
- val = readl(msm_chip->regs.in);
- pol = readl(msm_chip->regs.int_pos);
- pol = (pol & ~msm_chip->both_edge_detect) |
- (~val & msm_chip->both_edge_detect);
- writel(pol, msm_chip->regs.int_pos);
- intstat = readl(msm_chip->regs.int_status);
- val2 = readl(msm_chip->regs.in);
- if (((val ^ val2) & msm_chip->both_edge_detect & ~intstat) == 0)
- return;
- } while (loop_limit-- > 0);
- printk(KERN_ERR "msm_gpio_update_both_edge_detect, "
- "failed to reach stable state %x != %x\n", val, val2);
-}
-
-static int msm_gpio_clear_detect_status(struct msm_gpio_chip *msm_chip,
- unsigned offset)
-{
- unsigned bit = BIT(offset);
-
-#if MSM_GPIO_BROKEN_INT_CLEAR
- /* Save interrupts that already triggered before we loose them. */
- /* Any interrupt that triggers between the read of int_status */
- /* and the write to int_clear will still be lost though. */
- msm_chip->int_status_copy |= readl(msm_chip->regs.int_status);
- msm_chip->int_status_copy &= ~bit;
-#endif
- writel(bit, msm_chip->regs.int_clear);
- msm_gpio_update_both_edge_detect(msm_chip);
- return 0;
-}
-
-static int msm_gpio_direction_input(struct gpio_chip *chip, unsigned offset)
-{
- struct msm_gpio_chip *msm_chip;
- unsigned long irq_flags;
-
- msm_chip = container_of(chip, struct msm_gpio_chip, chip);
- spin_lock_irqsave(&msm_chip->lock, irq_flags);
- writel(readl(msm_chip->regs.oe) & ~BIT(offset), msm_chip->regs.oe);
- spin_unlock_irqrestore(&msm_chip->lock, irq_flags);
- return 0;
-}
-
-static int
-msm_gpio_direction_output(struct gpio_chip *chip, unsigned offset, int value)
-{
- struct msm_gpio_chip *msm_chip;
- unsigned long irq_flags;
-
- msm_chip = container_of(chip, struct msm_gpio_chip, chip);
- spin_lock_irqsave(&msm_chip->lock, irq_flags);
- msm_gpio_write(msm_chip, offset, value);
- writel(readl(msm_chip->regs.oe) | BIT(offset), msm_chip->regs.oe);
- spin_unlock_irqrestore(&msm_chip->lock, irq_flags);
- return 0;
-}
-
-static int msm_gpio_get(struct gpio_chip *chip, unsigned offset)
-{
- struct msm_gpio_chip *msm_chip;
-
- msm_chip = container_of(chip, struct msm_gpio_chip, chip);
- return (readl(msm_chip->regs.in) & (1U << offset)) ? 1 : 0;
-}
-
-static void msm_gpio_set(struct gpio_chip *chip, unsigned offset, int value)
-{
- struct msm_gpio_chip *msm_chip;
- unsigned long irq_flags;
-
- msm_chip = container_of(chip, struct msm_gpio_chip, chip);
- spin_lock_irqsave(&msm_chip->lock, irq_flags);
- msm_gpio_write(msm_chip, offset, value);
- spin_unlock_irqrestore(&msm_chip->lock, irq_flags);
-}
-
-static int msm_gpio_to_irq(struct gpio_chip *chip, unsigned offset)
-{
- return MSM_GPIO_TO_INT(chip->base + offset);
-}
-
-#ifdef CONFIG_MSM_GPIOMUX
-static int msm_gpio_request(struct gpio_chip *chip, unsigned offset)
-{
- return msm_gpiomux_get(chip->base + offset);
-}
-
-static void msm_gpio_free(struct gpio_chip *chip, unsigned offset)
-{
- msm_gpiomux_put(chip->base + offset);
-}
-#else
-#define msm_gpio_request NULL
-#define msm_gpio_free NULL
-#endif
-
-struct msm_gpio_chip msm_gpio_chips[] = {
-#if defined(CONFIG_ARCH_MSM7X00A)
- MSM_GPIO_BANK(0, 0, 15),
- MSM_GPIO_BANK(1, 16, 42),
- MSM_GPIO_BANK(2, 43, 67),
- MSM_GPIO_BANK(3, 68, 94),
- MSM_GPIO_BANK(4, 95, 106),
- MSM_GPIO_BANK(5, 107, 121),
-#elif defined(CONFIG_ARCH_MSM7X25) || defined(CONFIG_ARCH_MSM7X27)
- MSM_GPIO_BANK(0, 0, 15),
- MSM_GPIO_BANK(1, 16, 42),
- MSM_GPIO_BANK(2, 43, 67),
- MSM_GPIO_BANK(3, 68, 94),
- MSM_GPIO_BANK(4, 95, 106),
- MSM_GPIO_BANK(5, 107, 132),
-#elif defined(CONFIG_ARCH_MSM7X30)
- MSM_GPIO_BANK(0, 0, 15),
- MSM_GPIO_BANK(1, 16, 43),
- MSM_GPIO_BANK(2, 44, 67),
- MSM_GPIO_BANK(3, 68, 94),
- MSM_GPIO_BANK(4, 95, 106),
- MSM_GPIO_BANK(5, 107, 133),
- MSM_GPIO_BANK(6, 134, 150),
- MSM_GPIO_BANK(7, 151, 181),
-#elif defined(CONFIG_ARCH_QSD8X50)
- MSM_GPIO_BANK(0, 0, 15),
- MSM_GPIO_BANK(1, 16, 42),
- MSM_GPIO_BANK(2, 43, 67),
- MSM_GPIO_BANK(3, 68, 94),
- MSM_GPIO_BANK(4, 95, 103),
- MSM_GPIO_BANK(5, 104, 121),
- MSM_GPIO_BANK(6, 122, 152),
- MSM_GPIO_BANK(7, 153, 164),
-#endif
-};
-
-static void msm_gpio_irq_ack(struct irq_data *d)
-{
- unsigned long irq_flags;
- struct msm_gpio_chip *msm_chip = irq_data_get_irq_chip_data(d);
- spin_lock_irqsave(&msm_chip->lock, irq_flags);
- msm_gpio_clear_detect_status(msm_chip,
- d->irq - gpio_to_irq(msm_chip->chip.base));
- spin_unlock_irqrestore(&msm_chip->lock, irq_flags);
-}
-
-static void msm_gpio_irq_mask(struct irq_data *d)
-{
- unsigned long irq_flags;
- struct msm_gpio_chip *msm_chip = irq_data_get_irq_chip_data(d);
- unsigned offset = d->irq - gpio_to_irq(msm_chip->chip.base);
-
- spin_lock_irqsave(&msm_chip->lock, irq_flags);
- /* level triggered interrupts are also latched */
- if (!(readl(msm_chip->regs.int_edge) & BIT(offset)))
- msm_gpio_clear_detect_status(msm_chip, offset);
- msm_chip->int_enable[0] &= ~BIT(offset);
- writel(msm_chip->int_enable[0], msm_chip->regs.int_en);
- spin_unlock_irqrestore(&msm_chip->lock, irq_flags);
-}
-
-static void msm_gpio_irq_unmask(struct irq_data *d)
-{
- unsigned long irq_flags;
- struct msm_gpio_chip *msm_chip = irq_data_get_irq_chip_data(d);
- unsigned offset = d->irq - gpio_to_irq(msm_chip->chip.base);
-
- spin_lock_irqsave(&msm_chip->lock, irq_flags);
- /* level triggered interrupts are also latched */
- if (!(readl(msm_chip->regs.int_edge) & BIT(offset)))
- msm_gpio_clear_detect_status(msm_chip, offset);
- msm_chip->int_enable[0] |= BIT(offset);
- writel(msm_chip->int_enable[0], msm_chip->regs.int_en);
- spin_unlock_irqrestore(&msm_chip->lock, irq_flags);
-}
-
-static int msm_gpio_irq_set_wake(struct irq_data *d, unsigned int on)
-{
- unsigned long irq_flags;
- struct msm_gpio_chip *msm_chip = irq_data_get_irq_chip_data(d);
- unsigned offset = d->irq - gpio_to_irq(msm_chip->chip.base);
-
- spin_lock_irqsave(&msm_chip->lock, irq_flags);
-
- if (on)
- msm_chip->int_enable[1] |= BIT(offset);
- else
- msm_chip->int_enable[1] &= ~BIT(offset);
-
- spin_unlock_irqrestore(&msm_chip->lock, irq_flags);
- return 0;
-}
-
-static int msm_gpio_irq_set_type(struct irq_data *d, unsigned int flow_type)
-{
- unsigned long irq_flags;
- struct msm_gpio_chip *msm_chip = irq_data_get_irq_chip_data(d);
- unsigned offset = d->irq - gpio_to_irq(msm_chip->chip.base);
- unsigned val, mask = BIT(offset);
-
- spin_lock_irqsave(&msm_chip->lock, irq_flags);
- val = readl(msm_chip->regs.int_edge);
- if (flow_type & IRQ_TYPE_EDGE_BOTH) {
- writel(val | mask, msm_chip->regs.int_edge);
- __irq_set_handler_locked(d->irq, handle_edge_irq);
- } else {
- writel(val & ~mask, msm_chip->regs.int_edge);
- __irq_set_handler_locked(d->irq, handle_level_irq);
- }
- if ((flow_type & IRQ_TYPE_EDGE_BOTH) == IRQ_TYPE_EDGE_BOTH) {
- msm_chip->both_edge_detect |= mask;
- msm_gpio_update_both_edge_detect(msm_chip);
- } else {
- msm_chip->both_edge_detect &= ~mask;
- val = readl(msm_chip->regs.int_pos);
- if (flow_type & (IRQF_TRIGGER_RISING | IRQF_TRIGGER_HIGH))
- writel(val | mask, msm_chip->regs.int_pos);
- else
- writel(val & ~mask, msm_chip->regs.int_pos);
- }
- spin_unlock_irqrestore(&msm_chip->lock, irq_flags);
- return 0;
-}
-
-static void msm_gpio_irq_handler(unsigned int irq, struct irq_desc *desc)
-{
- int i, j, mask;
- unsigned val;
-
- for (i = 0; i < ARRAY_SIZE(msm_gpio_chips); i++) {
- struct msm_gpio_chip *msm_chip = &msm_gpio_chips[i];
- val = readl(msm_chip->regs.int_status);
- val &= msm_chip->int_enable[0];
- while (val) {
- mask = val & -val;
- j = fls(mask) - 1;
- /* printk("%s %08x %08x bit %d gpio %d irq %d\n",
- __func__, v, m, j, msm_chip->chip.start + j,
- FIRST_GPIO_IRQ + msm_chip->chip.start + j); */
- val &= ~mask;
- generic_handle_irq(FIRST_GPIO_IRQ +
- msm_chip->chip.base + j);
- }
- }
- desc->irq_data.chip->irq_ack(&desc->irq_data);
-}
-
-static struct irq_chip msm_gpio_irq_chip = {
- .name = "msmgpio",
- .irq_ack = msm_gpio_irq_ack,
- .irq_mask = msm_gpio_irq_mask,
- .irq_unmask = msm_gpio_irq_unmask,
- .irq_set_wake = msm_gpio_irq_set_wake,
- .irq_set_type = msm_gpio_irq_set_type,
-};
-
-static int __init msm_init_gpio(void)
-{
- int i, j = 0;
-
- for (i = FIRST_GPIO_IRQ; i < FIRST_GPIO_IRQ + NR_GPIO_IRQS; i++) {
- if (i - FIRST_GPIO_IRQ >=
- msm_gpio_chips[j].chip.base +
- msm_gpio_chips[j].chip.ngpio)
- j++;
- irq_set_chip_data(i, &msm_gpio_chips[j]);
- irq_set_chip_and_handler(i, &msm_gpio_irq_chip,
- handle_edge_irq);
- set_irq_flags(i, IRQF_VALID);
- }
-
- for (i = 0; i < ARRAY_SIZE(msm_gpio_chips); i++) {
- spin_lock_init(&msm_gpio_chips[i].lock);
- writel(0, msm_gpio_chips[i].regs.int_en);
- gpiochip_add(&msm_gpio_chips[i].chip);
- }
-
- irq_set_chained_handler(INT_GPIO_GROUP1, msm_gpio_irq_handler);
- irq_set_chained_handler(INT_GPIO_GROUP2, msm_gpio_irq_handler);
- irq_set_irq_wake(INT_GPIO_GROUP1, 1);
- irq_set_irq_wake(INT_GPIO_GROUP2, 2);
- return 0;
-}
-
-postcore_initcall(msm_init_gpio);
diff --git a/arch/arm/mach-msm/gpio_hw.h b/arch/arm/mach-msm/gpio_hw.h
deleted file mode 100644
index 6b50660..0000000
--- a/arch/arm/mach-msm/gpio_hw.h
+++ /dev/null
@@ -1,278 +0,0 @@
-/* arch/arm/mach-msm/gpio_hw.h
- *
- * Copyright (C) 2007 Google, Inc.
- * Author: Brian Swetland <swetland@google.com>
- * Copyright (c) 2008-2010, Code Aurora Forum. All rights reserved.
- *
- * This software is licensed under the terms of the GNU General Public
- * License version 2, as published by the Free Software Foundation, and
- * may be copied, distributed, and modified under those terms.
- *
- * 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.
- *
- */
-
-#ifndef __ARCH_ARM_MACH_MSM_GPIO_HW_H
-#define __ARCH_ARM_MACH_MSM_GPIO_HW_H
-
-#include <mach/msm_iomap.h>
-
-/* see 80-VA736-2 Rev C pp 695-751
-**
-** These are actually the *shadow* gpio registers, since the
-** real ones (which allow full access) are only available to the
-** ARM9 side of the world.
-**
-** Since the _BASE need to be page-aligned when we're mapping them
-** to virtual addresses, adjust for the additional offset in these
-** macros.
-*/
-
-#if defined(CONFIG_ARCH_MSM7X30)
-#define MSM_GPIO1_REG(off) (MSM_GPIO1_BASE + (off))
-#define MSM_GPIO2_REG(off) (MSM_GPIO2_BASE + 0x400 + (off))
-#else
-#define MSM_GPIO1_REG(off) (MSM_GPIO1_BASE + 0x800 + (off))
-#define MSM_GPIO2_REG(off) (MSM_GPIO2_BASE + 0xC00 + (off))
-#endif
-
-#if defined(CONFIG_ARCH_MSM7X00A) || defined(CONFIG_ARCH_MSM7X25) ||\
- defined(CONFIG_ARCH_MSM7X27)
-
-/* output value */
-#define MSM_GPIO_OUT_0 MSM_GPIO1_REG(0x00) /* gpio 15-0 */
-#define MSM_GPIO_OUT_1 MSM_GPIO2_REG(0x00) /* gpio 42-16 */
-#define MSM_GPIO_OUT_2 MSM_GPIO1_REG(0x04) /* gpio 67-43 */
-#define MSM_GPIO_OUT_3 MSM_GPIO1_REG(0x08) /* gpio 94-68 */
-#define MSM_GPIO_OUT_4 MSM_GPIO1_REG(0x0C) /* gpio 106-95 */
-#define MSM_GPIO_OUT_5 MSM_GPIO1_REG(0x50) /* gpio 107-121 */
-
-/* same pin map as above, output enable */
-#define MSM_GPIO_OE_0 MSM_GPIO1_REG(0x10)
-#define MSM_GPIO_OE_1 MSM_GPIO2_REG(0x08)
-#define MSM_GPIO_OE_2 MSM_GPIO1_REG(0x14)
-#define MSM_GPIO_OE_3 MSM_GPIO1_REG(0x18)
-#define MSM_GPIO_OE_4 MSM_GPIO1_REG(0x1C)
-#define MSM_GPIO_OE_5 MSM_GPIO1_REG(0x54)
-
-/* same pin map as above, input read */
-#define MSM_GPIO_IN_0 MSM_GPIO1_REG(0x34)
-#define MSM_GPIO_IN_1 MSM_GPIO2_REG(0x20)
-#define MSM_GPIO_IN_2 MSM_GPIO1_REG(0x38)
-#define MSM_GPIO_IN_3 MSM_GPIO1_REG(0x3C)
-#define MSM_GPIO_IN_4 MSM_GPIO1_REG(0x40)
-#define MSM_GPIO_IN_5 MSM_GPIO1_REG(0x44)
-
-/* same pin map as above, 1=edge 0=level interrup */
-#define MSM_GPIO_INT_EDGE_0 MSM_GPIO1_REG(0x60)
-#define MSM_GPIO_INT_EDGE_1 MSM_GPIO2_REG(0x50)
-#define MSM_GPIO_INT_EDGE_2 MSM_GPIO1_REG(0x64)
-#define MSM_GPIO_INT_EDGE_3 MSM_GPIO1_REG(0x68)
-#define MSM_GPIO_INT_EDGE_4 MSM_GPIO1_REG(0x6C)
-#define MSM_GPIO_INT_EDGE_5 MSM_GPIO1_REG(0xC0)
-
-/* same pin map as above, 1=positive 0=negative */
-#define MSM_GPIO_INT_POS_0 MSM_GPIO1_REG(0x70)
-#define MSM_GPIO_INT_POS_1 MSM_GPIO2_REG(0x58)
-#define MSM_GPIO_INT_POS_2 MSM_GPIO1_REG(0x74)
-#define MSM_GPIO_INT_POS_3 MSM_GPIO1_REG(0x78)
-#define MSM_GPIO_INT_POS_4 MSM_GPIO1_REG(0x7C)
-#define MSM_GPIO_INT_POS_5 MSM_GPIO1_REG(0xBC)
-
-/* same pin map as above, interrupt enable */
-#define MSM_GPIO_INT_EN_0 MSM_GPIO1_REG(0x80)
-#define MSM_GPIO_INT_EN_1 MSM_GPIO2_REG(0x60)
-#define MSM_GPIO_INT_EN_2 MSM_GPIO1_REG(0x84)
-#define MSM_GPIO_INT_EN_3 MSM_GPIO1_REG(0x88)
-#define MSM_GPIO_INT_EN_4 MSM_GPIO1_REG(0x8C)
-#define MSM_GPIO_INT_EN_5 MSM_GPIO1_REG(0xB8)
-
-/* same pin map as above, write 1 to clear interrupt */
-#define MSM_GPIO_INT_CLEAR_0 MSM_GPIO1_REG(0x90)
-#define MSM_GPIO_INT_CLEAR_1 MSM_GPIO2_REG(0x68)
-#define MSM_GPIO_INT_CLEAR_2 MSM_GPIO1_REG(0x94)
-#define MSM_GPIO_INT_CLEAR_3 MSM_GPIO1_REG(0x98)
-#define MSM_GPIO_INT_CLEAR_4 MSM_GPIO1_REG(0x9C)
-#define MSM_GPIO_INT_CLEAR_5 MSM_GPIO1_REG(0xB4)
-
-/* same pin map as above, 1=interrupt pending */
-#define MSM_GPIO_INT_STATUS_0 MSM_GPIO1_REG(0xA0)
-#define MSM_GPIO_INT_STATUS_1 MSM_GPIO2_REG(0x70)
-#define MSM_GPIO_INT_STATUS_2 MSM_GPIO1_REG(0xA4)
-#define MSM_GPIO_INT_STATUS_3 MSM_GPIO1_REG(0xA8)
-#define MSM_GPIO_INT_STATUS_4 MSM_GPIO1_REG(0xAC)
-#define MSM_GPIO_INT_STATUS_5 MSM_GPIO1_REG(0xB0)
-
-#endif
-
-#if defined(CONFIG_ARCH_QSD8X50)
-/* output value */
-#define MSM_GPIO_OUT_0 MSM_GPIO1_REG(0x00) /* gpio 15-0 */
-#define MSM_GPIO_OUT_1 MSM_GPIO2_REG(0x00) /* gpio 42-16 */
-#define MSM_GPIO_OUT_2 MSM_GPIO1_REG(0x04) /* gpio 67-43 */
-#define MSM_GPIO_OUT_3 MSM_GPIO1_REG(0x08) /* gpio 94-68 */
-#define MSM_GPIO_OUT_4 MSM_GPIO1_REG(0x0C) /* gpio 103-95 */
-#define MSM_GPIO_OUT_5 MSM_GPIO1_REG(0x10) /* gpio 121-104 */
-#define MSM_GPIO_OUT_6 MSM_GPIO1_REG(0x14) /* gpio 152-122 */
-#define MSM_GPIO_OUT_7 MSM_GPIO1_REG(0x18) /* gpio 164-153 */
-
-/* same pin map as above, output enable */
-#define MSM_GPIO_OE_0 MSM_GPIO1_REG(0x20)
-#define MSM_GPIO_OE_1 MSM_GPIO2_REG(0x08)
-#define MSM_GPIO_OE_2 MSM_GPIO1_REG(0x24)
-#define MSM_GPIO_OE_3 MSM_GPIO1_REG(0x28)
-#define MSM_GPIO_OE_4 MSM_GPIO1_REG(0x2C)
-#define MSM_GPIO_OE_5 MSM_GPIO1_REG(0x30)
-#define MSM_GPIO_OE_6 MSM_GPIO1_REG(0x34)
-#define MSM_GPIO_OE_7 MSM_GPIO1_REG(0x38)
-
-/* same pin map as above, input read */
-#define MSM_GPIO_IN_0 MSM_GPIO1_REG(0x50)
-#define MSM_GPIO_IN_1 MSM_GPIO2_REG(0x20)
-#define MSM_GPIO_IN_2 MSM_GPIO1_REG(0x54)
-#define MSM_GPIO_IN_3 MSM_GPIO1_REG(0x58)
-#define MSM_GPIO_IN_4 MSM_GPIO1_REG(0x5C)
-#define MSM_GPIO_IN_5 MSM_GPIO1_REG(0x60)
-#define MSM_GPIO_IN_6 MSM_GPIO1_REG(0x64)
-#define MSM_GPIO_IN_7 MSM_GPIO1_REG(0x68)
-
-/* same pin map as above, 1=edge 0=level interrup */
-#define MSM_GPIO_INT_EDGE_0 MSM_GPIO1_REG(0x70)
-#define MSM_GPIO_INT_EDGE_1 MSM_GPIO2_REG(0x50)
-#define MSM_GPIO_INT_EDGE_2 MSM_GPIO1_REG(0x74)
-#define MSM_GPIO_INT_EDGE_3 MSM_GPIO1_REG(0x78)
-#define MSM_GPIO_INT_EDGE_4 MSM_GPIO1_REG(0x7C)
-#define MSM_GPIO_INT_EDGE_5 MSM_GPIO1_REG(0x80)
-#define MSM_GPIO_INT_EDGE_6 MSM_GPIO1_REG(0x84)
-#define MSM_GPIO_INT_EDGE_7 MSM_GPIO1_REG(0x88)
-
-/* same pin map as above, 1=positive 0=negative */
-#define MSM_GPIO_INT_POS_0 MSM_GPIO1_REG(0x90)
-#define MSM_GPIO_INT_POS_1 MSM_GPIO2_REG(0x58)
-#define MSM_GPIO_INT_POS_2 MSM_GPIO1_REG(0x94)
-#define MSM_GPIO_INT_POS_3 MSM_GPIO1_REG(0x98)
-#define MSM_GPIO_INT_POS_4 MSM_GPIO1_REG(0x9C)
-#define MSM_GPIO_INT_POS_5 MSM_GPIO1_REG(0xA0)
-#define MSM_GPIO_INT_POS_6 MSM_GPIO1_REG(0xA4)
-#define MSM_GPIO_INT_POS_7 MSM_GPIO1_REG(0xA8)
-
-/* same pin map as above, interrupt enable */
-#define MSM_GPIO_INT_EN_0 MSM_GPIO1_REG(0xB0)
-#define MSM_GPIO_INT_EN_1 MSM_GPIO2_REG(0x60)
-#define MSM_GPIO_INT_EN_2 MSM_GPIO1_REG(0xB4)
-#define MSM_GPIO_INT_EN_3 MSM_GPIO1_REG(0xB8)
-#define MSM_GPIO_INT_EN_4 MSM_GPIO1_REG(0xBC)
-#define MSM_GPIO_INT_EN_5 MSM_GPIO1_REG(0xC0)
-#define MSM_GPIO_INT_EN_6 MSM_GPIO1_REG(0xC4)
-#define MSM_GPIO_INT_EN_7 MSM_GPIO1_REG(0xC8)
-
-/* same pin map as above, write 1 to clear interrupt */
-#define MSM_GPIO_INT_CLEAR_0 MSM_GPIO1_REG(0xD0)
-#define MSM_GPIO_INT_CLEAR_1 MSM_GPIO2_REG(0x68)
-#define MSM_GPIO_INT_CLEAR_2 MSM_GPIO1_REG(0xD4)
-#define MSM_GPIO_INT_CLEAR_3 MSM_GPIO1_REG(0xD8)
-#define MSM_GPIO_INT_CLEAR_4 MSM_GPIO1_REG(0xDC)
-#define MSM_GPIO_INT_CLEAR_5 MSM_GPIO1_REG(0xE0)
-#define MSM_GPIO_INT_CLEAR_6 MSM_GPIO1_REG(0xE4)
-#define MSM_GPIO_INT_CLEAR_7 MSM_GPIO1_REG(0xE8)
-
-/* same pin map as above, 1=interrupt pending */
-#define MSM_GPIO_INT_STATUS_0 MSM_GPIO1_REG(0xF0)
-#define MSM_GPIO_INT_STATUS_1 MSM_GPIO2_REG(0x70)
-#define MSM_GPIO_INT_STATUS_2 MSM_GPIO1_REG(0xF4)
-#define MSM_GPIO_INT_STATUS_3 MSM_GPIO1_REG(0xF8)
-#define MSM_GPIO_INT_STATUS_4 MSM_GPIO1_REG(0xFC)
-#define MSM_GPIO_INT_STATUS_5 MSM_GPIO1_REG(0x100)
-#define MSM_GPIO_INT_STATUS_6 MSM_GPIO1_REG(0x104)
-#define MSM_GPIO_INT_STATUS_7 MSM_GPIO1_REG(0x108)
-
-#endif
-
-#if defined(CONFIG_ARCH_MSM7X30)
-
-/* output value */
-#define MSM_GPIO_OUT_0 MSM_GPIO1_REG(0x00) /* gpio 15-0 */
-#define MSM_GPIO_OUT_1 MSM_GPIO2_REG(0x00) /* gpio 43-16 */
-#define MSM_GPIO_OUT_2 MSM_GPIO1_REG(0x04) /* gpio 67-44 */
-#define MSM_GPIO_OUT_3 MSM_GPIO1_REG(0x08) /* gpio 94-68 */
-#define MSM_GPIO_OUT_4 MSM_GPIO1_REG(0x0C) /* gpio 106-95 */
-#define MSM_GPIO_OUT_5 MSM_GPIO1_REG(0x50) /* gpio 133-107 */
-#define MSM_GPIO_OUT_6 MSM_GPIO1_REG(0xC4) /* gpio 150-134 */
-#define MSM_GPIO_OUT_7 MSM_GPIO1_REG(0x214) /* gpio 181-151 */
-
-/* same pin map as above, output enable */
-#define MSM_GPIO_OE_0 MSM_GPIO1_REG(0x10)
-#define MSM_GPIO_OE_1 MSM_GPIO2_REG(0x08)
-#define MSM_GPIO_OE_2 MSM_GPIO1_REG(0x14)
-#define MSM_GPIO_OE_3 MSM_GPIO1_REG(0x18)
-#define MSM_GPIO_OE_4 MSM_GPIO1_REG(0x1C)
-#define MSM_GPIO_OE_5 MSM_GPIO1_REG(0x54)
-#define MSM_GPIO_OE_6 MSM_GPIO1_REG(0xC8)
-#define MSM_GPIO_OE_7 MSM_GPIO1_REG(0x218)
-
-/* same pin map as above, input read */
-#define MSM_GPIO_IN_0 MSM_GPIO1_REG(0x34)
-#define MSM_GPIO_IN_1 MSM_GPIO2_REG(0x20)
-#define MSM_GPIO_IN_2 MSM_GPIO1_REG(0x38)
-#define MSM_GPIO_IN_3 MSM_GPIO1_REG(0x3C)
-#define MSM_GPIO_IN_4 MSM_GPIO1_REG(0x40)
-#define MSM_GPIO_IN_5 MSM_GPIO1_REG(0x44)
-#define MSM_GPIO_IN_6 MSM_GPIO1_REG(0xCC)
-#define MSM_GPIO_IN_7 MSM_GPIO1_REG(0x21C)
-
-/* same pin map as above, 1=edge 0=level interrup */
-#define MSM_GPIO_INT_EDGE_0 MSM_GPIO1_REG(0x60)
-#define MSM_GPIO_INT_EDGE_1 MSM_GPIO2_REG(0x50)
-#define MSM_GPIO_INT_EDGE_2 MSM_GPIO1_REG(0x64)
-#define MSM_GPIO_INT_EDGE_3 MSM_GPIO1_REG(0x68)
-#define MSM_GPIO_INT_EDGE_4 MSM_GPIO1_REG(0x6C)
-#define MSM_GPIO_INT_EDGE_5 MSM_GPIO1_REG(0xC0)
-#define MSM_GPIO_INT_EDGE_6 MSM_GPIO1_REG(0xD0)
-#define MSM_GPIO_INT_EDGE_7 MSM_GPIO1_REG(0x240)
-
-/* same pin map as above, 1=positive 0=negative */
-#define MSM_GPIO_INT_POS_0 MSM_GPIO1_REG(0x70)
-#define MSM_GPIO_INT_POS_1 MSM_GPIO2_REG(0x58)
-#define MSM_GPIO_INT_POS_2 MSM_GPIO1_REG(0x74)
-#define MSM_GPIO_INT_POS_3 MSM_GPIO1_REG(0x78)
-#define MSM_GPIO_INT_POS_4 MSM_GPIO1_REG(0x7C)
-#define MSM_GPIO_INT_POS_5 MSM_GPIO1_REG(0xBC)
-#define MSM_GPIO_INT_POS_6 MSM_GPIO1_REG(0xD4)
-#define MSM_GPIO_INT_POS_7 MSM_GPIO1_REG(0x228)
-
-/* same pin map as above, interrupt enable */
-#define MSM_GPIO_INT_EN_0 MSM_GPIO1_REG(0x80)
-#define MSM_GPIO_INT_EN_1 MSM_GPIO2_REG(0x60)
-#define MSM_GPIO_INT_EN_2 MSM_GPIO1_REG(0x84)
-#define MSM_GPIO_INT_EN_3 MSM_GPIO1_REG(0x88)
-#define MSM_GPIO_INT_EN_4 MSM_GPIO1_REG(0x8C)
-#define MSM_GPIO_INT_EN_5 MSM_GPIO1_REG(0xB8)
-#define MSM_GPIO_INT_EN_6 MSM_GPIO1_REG(0xD8)
-#define MSM_GPIO_INT_EN_7 MSM_GPIO1_REG(0x22C)
-
-/* same pin map as above, write 1 to clear interrupt */
-#define MSM_GPIO_INT_CLEAR_0 MSM_GPIO1_REG(0x90)
-#define MSM_GPIO_INT_CLEAR_1 MSM_GPIO2_REG(0x68)
-#define MSM_GPIO_INT_CLEAR_2 MSM_GPIO1_REG(0x94)
-#define MSM_GPIO_INT_CLEAR_3 MSM_GPIO1_REG(0x98)
-#define MSM_GPIO_INT_CLEAR_4 MSM_GPIO1_REG(0x9C)
-#define MSM_GPIO_INT_CLEAR_5 MSM_GPIO1_REG(0xB4)
-#define MSM_GPIO_INT_CLEAR_6 MSM_GPIO1_REG(0xDC)
-#define MSM_GPIO_INT_CLEAR_7 MSM_GPIO1_REG(0x230)
-
-/* same pin map as above, 1=interrupt pending */
-#define MSM_GPIO_INT_STATUS_0 MSM_GPIO1_REG(0xA0)
-#define MSM_GPIO_INT_STATUS_1 MSM_GPIO2_REG(0x70)
-#define MSM_GPIO_INT_STATUS_2 MSM_GPIO1_REG(0xA4)
-#define MSM_GPIO_INT_STATUS_3 MSM_GPIO1_REG(0xA8)
-#define MSM_GPIO_INT_STATUS_4 MSM_GPIO1_REG(0xAC)
-#define MSM_GPIO_INT_STATUS_5 MSM_GPIO1_REG(0xB0)
-#define MSM_GPIO_INT_STATUS_6 MSM_GPIO1_REG(0xE0)
-#define MSM_GPIO_INT_STATUS_7 MSM_GPIO1_REG(0x234)
-
-#endif
-
-#endif
diff --git a/arch/arm/mach-msm/gpiomux.h b/arch/arm/mach-msm/gpiomux.h
index b178d9c..00459f6 100644
--- a/arch/arm/mach-msm/gpiomux.h
+++ b/arch/arm/mach-msm/gpiomux.h
@@ -19,6 +19,7 @@
#include <linux/bitops.h>
#include <linux/errno.h>
+#include <mach/msm_gpiomux.h>
#if defined(CONFIG_MSM_V2_TLMM)
#include "gpiomux-v2.h"
@@ -71,12 +72,6 @@
*/
extern struct msm_gpiomux_config msm_gpiomux_configs[GPIOMUX_NGPIOS];
-/* Increment a gpio's reference count, possibly activating the line. */
-int __must_check msm_gpiomux_get(unsigned gpio);
-
-/* Decrement a gpio's reference count, possibly suspending the line. */
-int msm_gpiomux_put(unsigned gpio);
-
/* Install a new configuration to the gpio line. To avoid overwriting
* a configuration, leave the VALID bit out.
*/
@@ -94,16 +89,6 @@
*/
void __msm_gpiomux_write(unsigned gpio, gpiomux_config_t val);
#else
-static inline int __must_check msm_gpiomux_get(unsigned gpio)
-{
- return -ENOSYS;
-}
-
-static inline int msm_gpiomux_put(unsigned gpio)
-{
- return -ENOSYS;
-}
-
static inline int msm_gpiomux_write(unsigned gpio,
gpiomux_config_t active,
gpiomux_config_t suspended)
diff --git a/arch/arm/mach-msm/include/mach/msm_gpiomux.h b/arch/arm/mach-msm/include/mach/msm_gpiomux.h
new file mode 100644
index 0000000..0c7d393
--- /dev/null
+++ b/arch/arm/mach-msm/include/mach/msm_gpiomux.h
@@ -0,0 +1,38 @@
+/* Copyright (c) 2011, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only 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.
+ */
+
+#ifndef _LINUX_MSM_GPIOMUX_H
+#define _LINUX_MSM_GPIOMUX_H
+
+#ifdef CONFIG_MSM_GPIOMUX
+
+/* Increment a gpio's reference count, possibly activating the line. */
+int __must_check msm_gpiomux_get(unsigned gpio);
+
+/* Decrement a gpio's reference count, possibly suspending the line. */
+int msm_gpiomux_put(unsigned gpio);
+
+#else
+
+static inline int __must_check msm_gpiomux_get(unsigned gpio)
+{
+ return -ENOSYS;
+}
+
+static inline int msm_gpiomux_put(unsigned gpio)
+{
+ return -ENOSYS;
+}
+
+#endif
+
+#endif /* _LINUX_MSM_GPIOMUX_H */
diff --git a/arch/arm/mach-msm/include/mach/msm_iomap-7x00.h b/arch/arm/mach-msm/include/mach/msm_iomap-7x00.h
index 8f99d97..94fe9fe 100644
--- a/arch/arm/mach-msm/include/mach/msm_iomap-7x00.h
+++ b/arch/arm/mach-msm/include/mach/msm_iomap-7x00.h
@@ -55,13 +55,11 @@
#define MSM_DMOV_PHYS 0xA9700000
#define MSM_DMOV_SIZE SZ_4K
-#define MSM_GPIO1_BASE IOMEM(0xE0003000)
-#define MSM_GPIO1_PHYS 0xA9200000
-#define MSM_GPIO1_SIZE SZ_4K
+#define MSM7X00_GPIO1_PHYS 0xA9200000
+#define MSM7X00_GPIO1_SIZE SZ_4K
-#define MSM_GPIO2_BASE IOMEM(0xE0004000)
-#define MSM_GPIO2_PHYS 0xA9300000
-#define MSM_GPIO2_SIZE SZ_4K
+#define MSM7X00_GPIO2_PHYS 0xA9300000
+#define MSM7X00_GPIO2_SIZE SZ_4K
#define MSM_CLK_CTL_BASE IOMEM(0xE0005000)
#define MSM_CLK_CTL_PHYS 0xA8600000
diff --git a/arch/arm/mach-msm/include/mach/msm_iomap-7x30.h b/arch/arm/mach-msm/include/mach/msm_iomap-7x30.h
index 4d84be1..3769444 100644
--- a/arch/arm/mach-msm/include/mach/msm_iomap-7x30.h
+++ b/arch/arm/mach-msm/include/mach/msm_iomap-7x30.h
@@ -46,13 +46,11 @@
#define MSM_DMOV_PHYS 0xAC400000
#define MSM_DMOV_SIZE SZ_4K
-#define MSM_GPIO1_BASE IOMEM(0xE0003000)
-#define MSM_GPIO1_PHYS 0xAC001000
-#define MSM_GPIO1_SIZE SZ_4K
+#define MSM7X30_GPIO1_PHYS 0xAC001000
+#define MSM7X30_GPIO1_SIZE SZ_4K
-#define MSM_GPIO2_BASE IOMEM(0xE0004000)
-#define MSM_GPIO2_PHYS 0xAC101000
-#define MSM_GPIO2_SIZE SZ_4K
+#define MSM7X30_GPIO2_PHYS 0xAC101000
+#define MSM7X30_GPIO2_SIZE SZ_4K
#define MSM_CLK_CTL_BASE IOMEM(0xE0005000)
#define MSM_CLK_CTL_PHYS 0xAB800000
diff --git a/arch/arm/mach-msm/include/mach/msm_iomap-8x50.h b/arch/arm/mach-msm/include/mach/msm_iomap-8x50.h
index d414320..d67cd73 100644
--- a/arch/arm/mach-msm/include/mach/msm_iomap-8x50.h
+++ b/arch/arm/mach-msm/include/mach/msm_iomap-8x50.h
@@ -46,13 +46,11 @@
#define MSM_DMOV_PHYS 0xA9700000
#define MSM_DMOV_SIZE SZ_4K
-#define MSM_GPIO1_BASE IOMEM(0xE0003000)
-#define MSM_GPIO1_PHYS 0xA9000000
-#define MSM_GPIO1_SIZE SZ_4K
+#define QSD8X50_GPIO1_PHYS 0xA9000000
+#define QSD8X50_GPIO1_SIZE SZ_4K
-#define MSM_GPIO2_BASE IOMEM(0xE0004000)
-#define MSM_GPIO2_PHYS 0xA9100000
-#define MSM_GPIO2_SIZE SZ_4K
+#define QSD8X50_GPIO2_PHYS 0xA9100000
+#define QSD8X50_GPIO2_SIZE SZ_4K
#define MSM_CLK_CTL_BASE IOMEM(0xE0005000)
#define MSM_CLK_CTL_PHYS 0xA8600000
diff --git a/arch/arm/mach-msm/include/mach/msm_iomap.h b/arch/arm/mach-msm/include/mach/msm_iomap.h
index 2f494b6..4ded152 100644
--- a/arch/arm/mach-msm/include/mach/msm_iomap.h
+++ b/arch/arm/mach-msm/include/mach/msm_iomap.h
@@ -61,5 +61,7 @@
#define MSM_QGIC_CPU_BASE IOMEM(0xF0001000)
#define MSM_TMR_BASE IOMEM(0xF0200000)
#define MSM_TMR0_BASE IOMEM(0xF0201000)
+#define MSM_GPIO1_BASE IOMEM(0xE0003000)
+#define MSM_GPIO2_BASE IOMEM(0xE0004000)
#endif
diff --git a/arch/arm/mach-msm/io.c b/arch/arm/mach-msm/io.c
index cec6ed1..140ddbb 100644
--- a/arch/arm/mach-msm/io.c
+++ b/arch/arm/mach-msm/io.c
@@ -43,8 +43,8 @@
MSM_DEVICE(VIC),
MSM_CHIP_DEVICE(CSR, MSM7X00),
MSM_DEVICE(DMOV),
- MSM_DEVICE(GPIO1),
- MSM_DEVICE(GPIO2),
+ MSM_CHIP_DEVICE(GPIO1, MSM7X00),
+ MSM_CHIP_DEVICE(GPIO2, MSM7X00),
MSM_DEVICE(CLK_CTL),
#ifdef CONFIG_MSM_DEBUG_UART
MSM_DEVICE(DEBUG_UART),
@@ -76,8 +76,8 @@
MSM_DEVICE(VIC),
MSM_CHIP_DEVICE(CSR, QSD8X50),
MSM_DEVICE(DMOV),
- MSM_DEVICE(GPIO1),
- MSM_DEVICE(GPIO2),
+ MSM_CHIP_DEVICE(GPIO1, QSD8X50),
+ MSM_CHIP_DEVICE(GPIO2, QSD8X50),
MSM_DEVICE(CLK_CTL),
MSM_DEVICE(SIRC),
MSM_DEVICE(SCPLL),
@@ -135,8 +135,8 @@
MSM_DEVICE(VIC),
MSM_CHIP_DEVICE(CSR, MSM7X30),
MSM_DEVICE(DMOV),
- MSM_DEVICE(GPIO1),
- MSM_DEVICE(GPIO2),
+ MSM_CHIP_DEVICE(GPIO1, MSM7X30),
+ MSM_CHIP_DEVICE(GPIO2, MSM7X30),
MSM_DEVICE(CLK_CTL),
MSM_DEVICE(CLK_CTL_SH2),
MSM_DEVICE(AD5),
diff --git a/arch/arm/mach-mx5/board-cpuimx51.c b/arch/arm/mach-mx5/board-cpuimx51.c
index 7c893fa..68934ea 100644
--- a/arch/arm/mach-mx5/board-cpuimx51.c
+++ b/arch/arm/mach-mx5/board-cpuimx51.c
@@ -81,7 +81,7 @@
.flags = UPF_BOOT_AUTOCONF | UPF_SKIP_TEST | UPF_IOREMAP,
}, {
.mapbase = (unsigned long)(MX51_CS1_BASE_ADDR + 0x2000000),
- .irq = irq_to_gpio(CPUIMX51_QUARTD_GPIO),
+ .irq = gpio_to_irq(CPUIMX51_QUARTD_GPIO),
.irqflags = IRQF_TRIGGER_HIGH,
.uartclk = CPUIMX51_QUART_XTAL,
.regshift = CPUIMX51_QUART_REGSHIFT,
diff --git a/arch/arm/mach-mx5/board-mx51_babbage.c b/arch/arm/mach-mx5/board-mx51_babbage.c
index e400b09..11b0ff6 100644
--- a/arch/arm/mach-mx5/board-mx51_babbage.c
+++ b/arch/arm/mach-mx5/board-mx51_babbage.c
@@ -369,7 +369,7 @@
ARRAY_SIZE(mx51babbage_pads));
imx51_add_imx_uart(0, &uart_pdata);
- imx51_add_imx_uart(1, &uart_pdata);
+ imx51_add_imx_uart(1, NULL);
imx51_add_imx_uart(2, &uart_pdata);
babbage_fec_reset();
diff --git a/arch/arm/mach-mx5/board-mx51_efikamx.c b/arch/arm/mach-mx5/board-mx51_efikamx.c
index f70700d..551daf8 100644
--- a/arch/arm/mach-mx5/board-mx51_efikamx.c
+++ b/arch/arm/mach-mx5/board-mx51_efikamx.c
@@ -108,9 +108,9 @@
gpio_request(EFIKAMX_PCBID2, "pcbid2");
gpio_direction_input(EFIKAMX_PCBID2);
- id = gpio_get_value(EFIKAMX_PCBID0);
- id |= gpio_get_value(EFIKAMX_PCBID1) << 1;
- id |= gpio_get_value(EFIKAMX_PCBID2) << 2;
+ id = gpio_get_value(EFIKAMX_PCBID0) ? 1 : 0;
+ id |= (gpio_get_value(EFIKAMX_PCBID1) ? 1 : 0) << 1;
+ id |= (gpio_get_value(EFIKAMX_PCBID2) ? 1 : 0) << 2;
switch (id) {
case 7:
diff --git a/arch/arm/mach-mx5/board-mx51_efikasb.c b/arch/arm/mach-mx5/board-mx51_efikasb.c
index 2e4d9d3..8a9bca2 100644
--- a/arch/arm/mach-mx5/board-mx51_efikasb.c
+++ b/arch/arm/mach-mx5/board-mx51_efikasb.c
@@ -156,23 +156,24 @@
{
.code = KEY_POWER,
.gpio = EFIKASB_PWRKEY,
- .type = EV_PWR,
+ .type = EV_KEY,
.desc = "Power Button",
.wakeup = 1,
- .debounce_interval = 10, /* ms */
+ .active_low = 1,
},
{
.code = SW_LID,
.gpio = EFIKASB_LID,
.type = EV_SW,
.desc = "Lid Switch",
+ .active_low = 1,
},
{
- /* SW_RFKILLALL vs KEY_RFKILL ? */
- .code = SW_RFKILL_ALL,
+ .code = KEY_RFKILL,
.gpio = EFIKASB_RFKILL,
- .type = EV_SW,
+ .type = EV_KEY,
.desc = "rfkill",
+ .active_low = 1,
},
};
@@ -224,8 +225,8 @@
gpio_request(EFIKASB_PCBID1, "pcb id1");
gpio_direction_input(EFIKASB_PCBID1);
- id = gpio_get_value(EFIKASB_PCBID0);
- id |= gpio_get_value(EFIKASB_PCBID1) << 1;
+ id = gpio_get_value(EFIKASB_PCBID0) ? 1 : 0;
+ id |= (gpio_get_value(EFIKASB_PCBID1) ? 1 : 0) << 1;
switch (id) {
default:
diff --git a/arch/arm/mach-mx5/clock-mx51-mx53.c b/arch/arm/mach-mx5/clock-mx51-mx53.c
index 7f20308..f7bf996 100644
--- a/arch/arm/mach-mx5/clock-mx51-mx53.c
+++ b/arch/arm/mach-mx5/clock-mx51-mx53.c
@@ -271,7 +271,11 @@
int i = 0;
pllbase = _get_pll_base(clk);
- reg = __raw_readl(pllbase + MXC_PLL_DP_CTL) | MXC_PLL_DP_CTL_UPEN;
+ reg = __raw_readl(pllbase + MXC_PLL_DP_CTL);
+ if (reg & MXC_PLL_DP_CTL_UPEN)
+ return 0;
+
+ reg |= MXC_PLL_DP_CTL_UPEN;
__raw_writel(reg, pllbase + MXC_PLL_DP_CTL);
/* Wait for lock */
diff --git a/arch/arm/mach-mx5/mx51_efika.c b/arch/arm/mach-mx5/mx51_efika.c
index 4435e03..c920945 100644
--- a/arch/arm/mach-mx5/mx51_efika.c
+++ b/arch/arm/mach-mx5/mx51_efika.c
@@ -186,7 +186,7 @@
mdelay(10);
- return mx51_initialize_usb_hw(0, MXC_EHCI_ITC_NO_THRESHOLD);
+ return mx51_initialize_usb_hw(pdev->id, MXC_EHCI_ITC_NO_THRESHOLD);
}
static struct mxc_usbh_platform_data usbh1_config = {
diff --git a/arch/arm/mach-omap2/Kconfig b/arch/arm/mach-omap2/Kconfig
index 4ae6257..57b66d5 100644
--- a/arch/arm/mach-omap2/Kconfig
+++ b/arch/arm/mach-omap2/Kconfig
@@ -7,7 +7,6 @@
default y
select AEABI
select REGULATOR
- select PM
select PM_RUNTIME
select VFP
select NEON if ARCH_OMAP3 || ARCH_OMAP4
diff --git a/arch/arm/mach-omap2/board-am3517crane.c b/arch/arm/mach-omap2/board-am3517crane.c
index 5f2b55f..933e935 100644
--- a/arch/arm/mach-omap2/board-am3517crane.c
+++ b/arch/arm/mach-omap2/board-am3517crane.c
@@ -45,8 +45,6 @@
static struct omap_board_mux board_mux[] __initdata = {
{ .reg_offset = OMAP_MUX_TERMINATOR },
};
-#else
-#define board_mux NULL
#endif
static void __init am3517_crane_init_early(void)
diff --git a/arch/arm/mach-omap2/board-omap3beagle.c b/arch/arm/mach-omap2/board-omap3beagle.c
index 32f5f89..3ae16b4 100644
--- a/arch/arm/mach-omap2/board-omap3beagle.c
+++ b/arch/arm/mach-omap2/board-omap3beagle.c
@@ -491,23 +491,22 @@
/* Custom OPP enabled for all xM versions */
if (cpu_is_omap3630()) {
- struct omap_hwmod *mh = omap_hwmod_lookup("mpu");
- struct omap_hwmod *dh = omap_hwmod_lookup("iva");
- struct device *dev;
+ struct device *mpu_dev, *iva_dev;
- if (!mh || !dh) {
+ mpu_dev = omap2_get_mpuss_device();
+ iva_dev = omap2_get_iva_device();
+
+ if (!mpu_dev || !iva_dev) {
pr_err("%s: Aiee.. no mpu/dsp devices? %p %p\n",
- __func__, mh, dh);
+ __func__, mpu_dev, iva_dev);
return;
}
/* Enable MPU 1GHz and lower opps */
- dev = &mh->od->pdev.dev;
- r = opp_enable(dev, 800000000);
+ r = opp_enable(mpu_dev, 800000000);
/* TODO: MPU 1GHz needs SR and ABB */
/* Enable IVA 800MHz and lower opps */
- dev = &dh->od->pdev.dev;
- r |= opp_enable(dev, 660000000);
+ r |= opp_enable(iva_dev, 660000000);
/* TODO: DSP 800MHz needs SR and ABB */
if (r) {
pr_err("%s: failed to enable higher opp %d\n",
@@ -516,10 +515,8 @@
* Cleanup - disable the higher freqs - we dont care
* about the results
*/
- dev = &mh->od->pdev.dev;
- opp_disable(dev, 800000000);
- dev = &dh->od->pdev.dev;
- opp_disable(dev, 660000000);
+ opp_disable(mpu_dev, 800000000);
+ opp_disable(iva_dev, 660000000);
}
}
return;
diff --git a/arch/arm/mach-omap2/cminst44xx.h b/arch/arm/mach-omap2/cminst44xx.h
index f2ea645..a018a73 100644
--- a/arch/arm/mach-omap2/cminst44xx.h
+++ b/arch/arm/mach-omap2/cminst44xx.h
@@ -18,13 +18,36 @@
extern void omap4_cminst_clkdm_force_wakeup(u8 part, s16 inst, u16 cdoffs);
extern int omap4_cminst_wait_module_ready(u8 part, u16 inst, s16 cdoffs, u16 clkctrl_offs);
-extern int omap4_cminst_wait_module_idle(u8 part, u16 inst, s16 cdoffs, u16 clkctrl_offs);
+
+# ifdef CONFIG_ARCH_OMAP4
+extern int omap4_cminst_wait_module_idle(u8 part, u16 inst, s16 cdoffs,
+ u16 clkctrl_offs);
extern void omap4_cminst_module_enable(u8 mode, u8 part, u16 inst, s16 cdoffs,
u16 clkctrl_offs);
extern void omap4_cminst_module_disable(u8 part, u16 inst, s16 cdoffs,
u16 clkctrl_offs);
+# else
+
+static inline int omap4_cminst_wait_module_idle(u8 part, u16 inst, s16 cdoffs,
+ u16 clkctrl_offs)
+{
+ return 0;
+}
+
+static inline void omap4_cminst_module_enable(u8 mode, u8 part, u16 inst,
+ s16 cdoffs, u16 clkctrl_offs)
+{
+}
+
+static inline void omap4_cminst_module_disable(u8 part, u16 inst, s16 cdoffs,
+ u16 clkctrl_offs)
+{
+}
+
+# endif
+
/*
* In an ideal world, we would not export these low-level functions,
* but this will probably take some time to fix properly
diff --git a/arch/arm/mach-omap2/display.c b/arch/arm/mach-omap2/display.c
index 543fcb8..a5b7a23 100644
--- a/arch/arm/mach-omap2/display.c
+++ b/arch/arm/mach-omap2/display.c
@@ -25,6 +25,7 @@
#include <video/omapdss.h>
#include <plat/omap_hwmod.h>
#include <plat/omap_device.h>
+#include <plat/omap-pm.h>
static struct platform_device omap_display_device = {
.name = "omapdss",
@@ -42,20 +43,6 @@
},
};
-/* oh_core is used for getting opt-clocks */
-static struct omap_hwmod *oh_core;
-
-static bool opt_clock_available(const char *clk_role)
-{
- int i;
-
- for (i = 0; i < oh_core->opt_clks_cnt; i++) {
- if (!strcmp(oh_core->opt_clks[i].role, clk_role))
- return true;
- }
- return false;
-}
-
struct omap_dss_hwmod_data {
const char *oh_name;
const char *dev_name;
@@ -109,16 +96,9 @@
oh_count = ARRAY_SIZE(omap4_dss_hwmod_data);
}
- /* opt_clks are always associated with dss hwmod */
- oh_core = omap_hwmod_lookup("dss_core");
- if (!oh_core) {
- pr_err("Could not look up dss_core.\n");
- return -ENODEV;
- }
-
pdata.board_data = board_data;
- pdata.board_data->get_last_off_on_transaction_id = NULL;
- pdata.opt_clock_available = opt_clock_available;
+ pdata.board_data->get_context_loss_count =
+ omap_pm_get_dev_context_loss_count;
for (i = 0; i < oh_count; i++) {
oh = omap_hwmod_lookup(curr_dss_hwmod[i].oh_name);
diff --git a/arch/arm/mach-omap2/mux.c b/arch/arm/mach-omap2/mux.c
index c7fb22a..655e948 100644
--- a/arch/arm/mach-omap2/mux.c
+++ b/arch/arm/mach-omap2/mux.c
@@ -821,11 +821,10 @@
if (!omap_mux_options)
return;
- options = kmalloc(strlen(omap_mux_options) + 1, GFP_KERNEL);
+ options = kstrdup(omap_mux_options, GFP_KERNEL);
if (!options)
return;
- strcpy(options, omap_mux_options);
next_opt = options;
while ((token = strsep(&next_opt, ",")) != NULL) {
@@ -855,24 +854,19 @@
for (i = 0; i < OMAP_MUX_NR_MODES; i++) {
if (src->muxnames[i]) {
- dst->muxnames[i] =
- kmalloc(strlen(src->muxnames[i]) + 1,
- GFP_KERNEL);
+ dst->muxnames[i] = kstrdup(src->muxnames[i],
+ GFP_KERNEL);
if (!dst->muxnames[i])
goto free;
- strcpy(dst->muxnames[i], src->muxnames[i]);
}
}
#ifdef CONFIG_DEBUG_FS
for (i = 0; i < OMAP_MUX_NR_SIDES; i++) {
if (src->balls[i]) {
- dst->balls[i] =
- kmalloc(strlen(src->balls[i]) + 1,
- GFP_KERNEL);
+ dst->balls[i] = kstrdup(src->balls[i], GFP_KERNEL);
if (!dst->balls[i])
goto free;
- strcpy(dst->balls[i], src->balls[i]);
}
}
#endif
diff --git a/arch/arm/mach-omap2/smartreflex.c b/arch/arm/mach-omap2/smartreflex.c
index 2ce2fb7..34c01a7 100644
--- a/arch/arm/mach-omap2/smartreflex.c
+++ b/arch/arm/mach-omap2/smartreflex.c
@@ -621,7 +621,7 @@
sr_v2_disable(sr);
}
- pm_runtime_put_sync(&sr->pdev->dev);
+ pm_runtime_put_sync_suspend(&sr->pdev->dev);
}
/**
@@ -860,6 +860,7 @@
irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
pm_runtime_enable(&pdev->dev);
+ pm_runtime_irq_safe(&pdev->dev);
sr_info->pdev = pdev;
sr_info->srid = pdev->id;
diff --git a/arch/arm/mach-omap2/timer.c b/arch/arm/mach-omap2/timer.c
index e964072..cf1de7d 100644
--- a/arch/arm/mach-omap2/timer.c
+++ b/arch/arm/mach-omap2/timer.c
@@ -293,7 +293,8 @@
pr_info("OMAP clocksource: GPTIMER%d at %lu Hz\n",
gptimer_id, clksrc.rate);
- __omap_dm_timer_load_start(clksrc.io_base, OMAP_TIMER_CTRL_ST, 0, 1);
+ __omap_dm_timer_load_start(clksrc.io_base,
+ OMAP_TIMER_CTRL_ST | OMAP_TIMER_CTRL_AR, 0, 1);
init_sched_clock(&cd, dmtimer_update_sched_clock, 32, clksrc.rate);
if (clocksource_register_hz(&clocksource_gpt, clksrc.rate))
diff --git a/arch/arm/mach-omap2/twl-common.c b/arch/arm/mach-omap2/twl-common.c
index 2543342..daa056e 100644
--- a/arch/arm/mach-omap2/twl-common.c
+++ b/arch/arm/mach-omap2/twl-common.c
@@ -48,14 +48,7 @@
omap_register_i2c_bus(bus, clkrate, &pmic_i2c_board_info, 1);
}
-static struct twl4030_usb_data omap4_usb_pdata = {
- .phy_init = omap4430_phy_init,
- .phy_exit = omap4430_phy_exit,
- .phy_power = omap4430_phy_power,
- .phy_set_clock = omap4430_phy_set_clk,
- .phy_suspend = omap4430_phy_suspend,
-};
-
+#if defined(CONFIG_ARCH_OMAP3)
static struct twl4030_usb_data omap3_usb_pdata = {
.usb_mode = T2_USB_MODE_ULPI,
};
@@ -122,6 +115,45 @@
.consumer_supplies = omap3_vpll2_supplies,
};
+void __init omap3_pmic_get_config(struct twl4030_platform_data *pmic_data,
+ u32 pdata_flags, u32 regulators_flags)
+{
+ if (!pmic_data->irq_base)
+ pmic_data->irq_base = TWL4030_IRQ_BASE;
+ if (!pmic_data->irq_end)
+ pmic_data->irq_end = TWL4030_IRQ_END;
+
+ /* Common platform data configurations */
+ if (pdata_flags & TWL_COMMON_PDATA_USB && !pmic_data->usb)
+ pmic_data->usb = &omap3_usb_pdata;
+
+ if (pdata_flags & TWL_COMMON_PDATA_BCI && !pmic_data->bci)
+ pmic_data->bci = &omap3_bci_pdata;
+
+ if (pdata_flags & TWL_COMMON_PDATA_MADC && !pmic_data->madc)
+ pmic_data->madc = &omap3_madc_pdata;
+
+ if (pdata_flags & TWL_COMMON_PDATA_AUDIO && !pmic_data->audio)
+ pmic_data->audio = &omap3_audio_pdata;
+
+ /* Common regulator configurations */
+ if (regulators_flags & TWL_COMMON_REGULATOR_VDAC && !pmic_data->vdac)
+ pmic_data->vdac = &omap3_vdac_idata;
+
+ if (regulators_flags & TWL_COMMON_REGULATOR_VPLL2 && !pmic_data->vpll2)
+ pmic_data->vpll2 = &omap3_vpll2_idata;
+}
+#endif /* CONFIG_ARCH_OMAP3 */
+
+#if defined(CONFIG_ARCH_OMAP4)
+static struct twl4030_usb_data omap4_usb_pdata = {
+ .phy_init = omap4430_phy_init,
+ .phy_exit = omap4430_phy_exit,
+ .phy_power = omap4430_phy_power,
+ .phy_set_clock = omap4430_phy_set_clk,
+ .phy_suspend = omap4430_phy_suspend,
+};
+
static struct regulator_init_data omap4_vdac_idata = {
.constraints = {
.min_uV = 1800000,
@@ -273,32 +305,4 @@
!pmic_data->clk32kg)
pmic_data->clk32kg = &omap4_clk32kg_idata;
}
-
-void __init omap3_pmic_get_config(struct twl4030_platform_data *pmic_data,
- u32 pdata_flags, u32 regulators_flags)
-{
- if (!pmic_data->irq_base)
- pmic_data->irq_base = TWL4030_IRQ_BASE;
- if (!pmic_data->irq_end)
- pmic_data->irq_end = TWL4030_IRQ_END;
-
- /* Common platform data configurations */
- if (pdata_flags & TWL_COMMON_PDATA_USB && !pmic_data->usb)
- pmic_data->usb = &omap3_usb_pdata;
-
- if (pdata_flags & TWL_COMMON_PDATA_BCI && !pmic_data->bci)
- pmic_data->bci = &omap3_bci_pdata;
-
- if (pdata_flags & TWL_COMMON_PDATA_MADC && !pmic_data->madc)
- pmic_data->madc = &omap3_madc_pdata;
-
- if (pdata_flags & TWL_COMMON_PDATA_AUDIO && !pmic_data->audio)
- pmic_data->audio = &omap3_audio_pdata;
-
- /* Common regulator configurations */
- if (regulators_flags & TWL_COMMON_REGULATOR_VDAC && !pmic_data->vdac)
- pmic_data->vdac = &omap3_vdac_idata;
-
- if (regulators_flags & TWL_COMMON_REGULATOR_VPLL2 && !pmic_data->vpll2)
- pmic_data->vpll2 = &omap3_vpll2_idata;
-}
+#endif /* CONFIG_ARCH_OMAP4 */
diff --git a/arch/arm/mach-s3c64xx/mach-crag6410.c b/arch/arm/mach-s3c64xx/mach-crag6410.c
index 9026249..af0c2fe 100644
--- a/arch/arm/mach-s3c64xx/mach-crag6410.c
+++ b/arch/arm/mach-s3c64xx/mach-crag6410.c
@@ -65,7 +65,7 @@
#include <plat/iic.h>
#include <plat/pm.h>
-#include <sound/wm8915.h>
+#include <sound/wm8996.h>
#include <sound/wm8962.h>
#include <sound/wm9081.h>
@@ -614,7 +614,7 @@
.disable_touch = true,
};
-static struct wm8915_retune_mobile_config wm8915_retune[] = {
+static struct wm8996_retune_mobile_config wm8996_retune[] = {
{
.name = "Sub LPF",
.rate = 48000,
@@ -635,12 +635,12 @@
},
};
-static struct wm8915_pdata wm8915_pdata __initdata = {
+static struct wm8996_pdata wm8996_pdata __initdata = {
.ldo_ena = S3C64XX_GPN(7),
.gpio_base = CODEC_GPIO_BASE,
.micdet_def = 1,
- .inl_mode = WM8915_DIFFERRENTIAL_1,
- .inr_mode = WM8915_DIFFERRENTIAL_1,
+ .inl_mode = WM8996_DIFFERRENTIAL_1,
+ .inr_mode = WM8996_DIFFERRENTIAL_1,
.irq_flags = IRQF_TRIGGER_RISING,
@@ -652,8 +652,8 @@
0x020e, /* GPIO5 == CLKOUT */
},
- .retune_mobile_cfgs = wm8915_retune,
- .num_retune_mobile_cfgs = ARRAY_SIZE(wm8915_retune),
+ .retune_mobile_cfgs = wm8996_retune,
+ .num_retune_mobile_cfgs = ARRAY_SIZE(wm8996_retune),
};
static struct wm8962_pdata wm8962_pdata __initdata = {
@@ -679,8 +679,8 @@
.platform_data = &glenfarclas_pmic_pdata },
{ I2C_BOARD_INFO("wm1250-ev1", 0x27) },
- { I2C_BOARD_INFO("wm8915", 0x1a),
- .platform_data = &wm8915_pdata,
+ { I2C_BOARD_INFO("wm8996", 0x1a),
+ .platform_data = &wm8996_pdata,
.irq = GLENFARCLAS_PMIC_IRQ_BASE + WM831X_IRQ_GPIO_2,
},
{ I2C_BOARD_INFO("wm9081", 0x6c),
diff --git a/arch/arm/mach-sa1100/pci-nanoengine.c b/arch/arm/mach-sa1100/pci-nanoengine.c
index 964c6c3..dd39fee 100644
--- a/arch/arm/mach-sa1100/pci-nanoengine.c
+++ b/arch/arm/mach-sa1100/pci-nanoengine.c
@@ -28,6 +28,7 @@
#include <asm/mach-types.h>
#include <mach/nanoengine.h>
+#include <mach/hardware.h>
static DEFINE_SPINLOCK(nano_lock);
diff --git a/arch/arm/mach-shmobile/clock-sh7367.c b/arch/arm/mach-shmobile/clock-sh7367.c
index 6b186ae..5218c34 100644
--- a/arch/arm/mach-shmobile/clock-sh7367.c
+++ b/arch/arm/mach-shmobile/clock-sh7367.c
@@ -259,9 +259,6 @@
[CMMSTP003] = MSTP(&r_clk, CMMSTPCR0, 3, 0), /* KEYSC */
};
-#define CLKDEV_CON_ID(_id, _clk) { .con_id = _id, .clk = _clk }
-#define CLKDEV_DEV_ID(_id, _clk) { .dev_id = _id, .clk = _clk }
-
static struct clk_lookup lookups[] = {
/* main clocks */
CLKDEV_CON_ID("r_clk", &r_clk),
diff --git a/arch/arm/mach-shmobile/clock-sh7372.c b/arch/arm/mach-shmobile/clock-sh7372.c
index 91f5779..6b1619a 100644
--- a/arch/arm/mach-shmobile/clock-sh7372.c
+++ b/arch/arm/mach-shmobile/clock-sh7372.c
@@ -561,10 +561,6 @@
[MSTP403] = MSTP(&r_clk, SMSTPCR4, 3, 0), /* KEYSC */
};
-#define CLKDEV_CON_ID(_id, _clk) { .con_id = _id, .clk = _clk }
-#define CLKDEV_DEV_ID(_id, _clk) { .dev_id = _id, .clk = _clk }
-#define CLKDEV_ICK_ID(_cid, _did, _clk) { .con_id = _cid, .dev_id = _did, .clk = _clk }
-
static struct clk_lookup lookups[] = {
/* main clocks */
CLKDEV_CON_ID("dv_clki_div2_clk", &sh7372_dv_clki_div2_clk),
diff --git a/arch/arm/mach-shmobile/clock-sh7377.c b/arch/arm/mach-shmobile/clock-sh7377.c
index 9594246..8cee7b1 100644
--- a/arch/arm/mach-shmobile/clock-sh7377.c
+++ b/arch/arm/mach-shmobile/clock-sh7377.c
@@ -267,9 +267,6 @@
[MSTP403] = MSTP(&r_clk, SMSTPCR4, 3, 0), /* KEYSC */
};
-#define CLKDEV_CON_ID(_id, _clk) { .con_id = _id, .clk = _clk }
-#define CLKDEV_DEV_ID(_id, _clk) { .dev_id = _id, .clk = _clk }
-
static struct clk_lookup lookups[] = {
/* main clocks */
CLKDEV_CON_ID("r_clk", &r_clk),
diff --git a/arch/arm/mach-shmobile/clock-sh73a0.c b/arch/arm/mach-shmobile/clock-sh73a0.c
index bcacb1e..6db2cca 100644
--- a/arch/arm/mach-shmobile/clock-sh73a0.c
+++ b/arch/arm/mach-shmobile/clock-sh73a0.c
@@ -306,10 +306,6 @@
[MSTP403] = MSTP(&r_clk, SMSTPCR4, 3, 0), /* KEYSC */
};
-#define CLKDEV_CON_ID(_id, _clk) { .con_id = _id, .clk = _clk }
-#define CLKDEV_DEV_ID(_id, _clk) { .dev_id = _id, .clk = _clk }
-#define CLKDEV_ICK_ID(_cid, _did, _clk) { .con_id = _cid, .dev_id = _did, .clk = _clk }
-
static struct clk_lookup lookups[] = {
/* main clocks */
CLKDEV_CON_ID("r_clk", &r_clk),
diff --git a/arch/arm/mm/alignment.c b/arch/arm/mm/alignment.c
index be7c638..cfbcf8b 100644
--- a/arch/arm/mm/alignment.c
+++ b/arch/arm/mm/alignment.c
@@ -22,6 +22,7 @@
#include <linux/sched.h>
#include <linux/uaccess.h>
+#include <asm/system.h>
#include <asm/unaligned.h>
#include "fault.h"
@@ -95,6 +96,33 @@
"signal+warn"
};
+/* Return true if and only if the ARMv6 unaligned access model is in use. */
+static bool cpu_is_v6_unaligned(void)
+{
+ return cpu_architecture() >= CPU_ARCH_ARMv6 && (cr_alignment & CR_U);
+}
+
+static int safe_usermode(int new_usermode, bool warn)
+{
+ /*
+ * ARMv6 and later CPUs can perform unaligned accesses for
+ * most single load and store instructions up to word size.
+ * LDM, STM, LDRD and STRD still need to be handled.
+ *
+ * Ignoring the alignment fault is not an option on these
+ * CPUs since we spin re-faulting the instruction without
+ * making any progress.
+ */
+ if (cpu_is_v6_unaligned() && !(new_usermode & (UM_FIXUP | UM_SIGNAL))) {
+ new_usermode |= UM_FIXUP;
+
+ if (warn)
+ printk(KERN_WARNING "alignment: ignoring faults is unsafe on this CPU. Defaulting to fixup mode.\n");
+ }
+
+ return new_usermode;
+}
+
static int alignment_proc_show(struct seq_file *m, void *v)
{
seq_printf(m, "User:\t\t%lu\n", ai_user);
@@ -125,7 +153,7 @@
if (get_user(mode, buffer))
return -EFAULT;
if (mode >= '0' && mode <= '5')
- ai_usermode = mode - '0';
+ ai_usermode = safe_usermode(mode - '0', true);
}
return count;
}
@@ -886,9 +914,16 @@
if (ai_usermode & UM_FIXUP)
goto fixup;
- if (ai_usermode & UM_SIGNAL)
- force_sig(SIGBUS, current);
- else {
+ if (ai_usermode & UM_SIGNAL) {
+ siginfo_t si;
+
+ si.si_signo = SIGBUS;
+ si.si_errno = 0;
+ si.si_code = BUS_ADRALN;
+ si.si_addr = (void __user *)addr;
+
+ force_sig_info(si.si_signo, &si, current);
+ } else {
/*
* We're about to disable the alignment trap and return to
* user space. But if an interrupt occurs before actually
@@ -926,20 +961,11 @@
return -ENOMEM;
#endif
- /*
- * ARMv6 and later CPUs can perform unaligned accesses for
- * most single load and store instructions up to word size.
- * LDM, STM, LDRD and STRD still need to be handled.
- *
- * Ignoring the alignment fault is not an option on these
- * CPUs since we spin re-faulting the instruction without
- * making any progress.
- */
- if (cpu_architecture() >= CPU_ARCH_ARMv6 && (cr_alignment & CR_U)) {
+ if (cpu_is_v6_unaligned()) {
cr_alignment &= ~CR_A;
cr_no_alignment &= ~CR_A;
set_cr(cr_alignment);
- ai_usermode = UM_FIXUP;
+ ai_usermode = safe_usermode(ai_usermode, false);
}
hook_fault_code(1, do_alignment, SIGBUS, BUS_ADRALN,
diff --git a/arch/arm/mm/init.c b/arch/arm/mm/init.c
index 2fee782..91bca35 100644
--- a/arch/arm/mm/init.c
+++ b/arch/arm/mm/init.c
@@ -441,7 +441,7 @@
static inline void poison_init_mem(void *s, size_t count)
{
u32 *p = (u32 *)s;
- while ((count = count - 4))
+ for (; count != 0; count -= 4)
*p++ = 0xe7fddef0;
}
diff --git a/arch/arm/mm/proc-arm946.S b/arch/arm/mm/proc-arm946.S
index f8f7ea3..683af3a 100644
--- a/arch/arm/mm/proc-arm946.S
+++ b/arch/arm/mm/proc-arm946.S
@@ -410,6 +410,7 @@
.long 0x41009460
.long 0xff00fff0
.long 0
+ .long 0
b __arm946_setup
.long cpu_arch_name
.long cpu_elf_name
@@ -418,6 +419,6 @@
.long arm946_processor_functions
.long 0
.long 0
- .long arm940_cache_fns
+ .long arm946_cache_fns
.size __arm946_proc_info, . - __arm946_proc_info
diff --git a/arch/arm/plat-mxc/include/mach/debug-macro.S b/arch/arm/plat-mxc/include/mach/debug-macro.S
index 91fc7cd..e4dde91 100644
--- a/arch/arm/plat-mxc/include/mach/debug-macro.S
+++ b/arch/arm/plat-mxc/include/mach/debug-macro.S
@@ -44,6 +44,14 @@
#define UART_PADDR MX51_UART1_BASE_ADDR
#endif
+/* iMX50/53 have same addresses, but not iMX51 */
+#if defined(CONFIG_SOC_IMX50) || defined(CONFIG_SOC_IMX53)
+#ifdef UART_PADDR
+#error "CONFIG_DEBUG_LL is incompatible with multiple archs"
+#endif
+#define UART_PADDR MX53_UART1_BASE_ADDR
+#endif
+
#define UART_VADDR IMX_IO_ADDRESS(UART_PADDR)
.macro addruart, rp, rv
diff --git a/arch/arm/plat-mxc/include/mach/iomux-mx53.h b/arch/arm/plat-mxc/include/mach/iomux-mx53.h
index 9440b9e..5408fd1 100644
--- a/arch/arm/plat-mxc/include/mach/iomux-mx53.h
+++ b/arch/arm/plat-mxc/include/mach/iomux-mx53.h
@@ -30,6 +30,9 @@
#define MX53_SDHC_PAD_CTRL (PAD_CTL_HYS | PAD_CTL_PKE | PAD_CTL_PUE | \
PAD_CTL_PUS_47K_UP | PAD_CTL_DSE_HIGH | \
PAD_CTL_SRE_FAST)
+#define PAD_CTRL_I2C (PAD_CTL_SRE_FAST | PAD_CTL_ODE | PAD_CTL_PKE | \
+ PAD_CTL_PUE | PAD_CTL_DSE_HIGH | PAD_CTL_PUS_100K_UP \
+ | PAD_CTL_HYS)
#define _MX53_PAD_GPIO_19__KPP_COL_5 IOMUX_PAD(0x348, 0x20, 0, 0x840, 0, 0)
#define _MX53_PAD_GPIO_19__GPIO4_5 IOMUX_PAD(0x348, 0x20, 1, 0x0, 0, 0)
@@ -1256,7 +1259,7 @@
#define MX53_PAD_KEY_COL3__GPIO4_12 (_MX53_PAD_KEY_COL3__GPIO4_12 | MUX_PAD_CTRL(NO_PAD_CTRL))
#define MX53_PAD_KEY_COL3__USBOH3_H2_DP (_MX53_PAD_KEY_COL3__USBOH3_H2_DP | MUX_PAD_CTRL(NO_PAD_CTRL))
#define MX53_PAD_KEY_COL3__SPDIF_IN1 (_MX53_PAD_KEY_COL3__SPDIF_IN1 | MUX_PAD_CTRL(NO_PAD_CTRL))
-#define MX53_PAD_KEY_COL3__I2C2_SCL (_MX53_PAD_KEY_COL3__I2C2_SCL | MUX_PAD_CTRL(NO_PAD_CTRL))
+#define MX53_PAD_KEY_COL3__I2C2_SCL (_MX53_PAD_KEY_COL3__I2C2_SCL | MUX_PAD_CTRL(PAD_CTRL_I2C))
#define MX53_PAD_KEY_COL3__ECSPI1_SS3 (_MX53_PAD_KEY_COL3__ECSPI1_SS3 | MUX_PAD_CTRL(NO_PAD_CTRL))
#define MX53_PAD_KEY_COL3__FEC_CRS (_MX53_PAD_KEY_COL3__FEC_CRS | MUX_PAD_CTRL(NO_PAD_CTRL))
#define MX53_PAD_KEY_COL3__USBPHY1_SIECLOCK (_MX53_PAD_KEY_COL3__USBPHY1_SIECLOCK | MUX_PAD_CTRL(NO_PAD_CTRL))
@@ -1264,7 +1267,7 @@
#define MX53_PAD_KEY_ROW3__GPIO4_13 (_MX53_PAD_KEY_ROW3__GPIO4_13 | MUX_PAD_CTRL(NO_PAD_CTRL))
#define MX53_PAD_KEY_ROW3__USBOH3_H2_DM (_MX53_PAD_KEY_ROW3__USBOH3_H2_DM | MUX_PAD_CTRL(NO_PAD_CTRL))
#define MX53_PAD_KEY_ROW3__CCM_ASRC_EXT_CLK (_MX53_PAD_KEY_ROW3__CCM_ASRC_EXT_CLK | MUX_PAD_CTRL(NO_PAD_CTRL))
-#define MX53_PAD_KEY_ROW3__I2C2_SDA (_MX53_PAD_KEY_ROW3__I2C2_SDA | MUX_PAD_CTRL(NO_PAD_CTRL))
+#define MX53_PAD_KEY_ROW3__I2C2_SDA (_MX53_PAD_KEY_ROW3__I2C2_SDA | MUX_PAD_CTRL(PAD_CTRL_I2C))
#define MX53_PAD_KEY_ROW3__OSC32K_32K_OUT (_MX53_PAD_KEY_ROW3__OSC32K_32K_OUT | MUX_PAD_CTRL(NO_PAD_CTRL))
#define MX53_PAD_KEY_ROW3__CCM_PLL4_BYP (_MX53_PAD_KEY_ROW3__CCM_PLL4_BYP | MUX_PAD_CTRL(NO_PAD_CTRL))
#define MX53_PAD_KEY_ROW3__USBPHY1_LINESTATE_0 (_MX53_PAD_KEY_ROW3__USBPHY1_LINESTATE_0 | MUX_PAD_CTRL(NO_PAD_CTRL))
@@ -1536,7 +1539,7 @@
#define MX53_PAD_CSI0_DAT8__KPP_COL_7 (_MX53_PAD_CSI0_DAT8__KPP_COL_7 | MUX_PAD_CTRL(NO_PAD_CTRL))
#define MX53_PAD_CSI0_DAT8__ECSPI2_SCLK (_MX53_PAD_CSI0_DAT8__ECSPI2_SCLK | MUX_PAD_CTRL(NO_PAD_CTRL))
#define MX53_PAD_CSI0_DAT8__USBOH3_USBH3_OC (_MX53_PAD_CSI0_DAT8__USBOH3_USBH3_OC | MUX_PAD_CTRL(NO_PAD_CTRL))
-#define MX53_PAD_CSI0_DAT8__I2C1_SDA (_MX53_PAD_CSI0_DAT8__I2C1_SDA | MUX_PAD_CTRL(NO_PAD_CTRL))
+#define MX53_PAD_CSI0_DAT8__I2C1_SDA (_MX53_PAD_CSI0_DAT8__I2C1_SDA | MUX_PAD_CTRL(PAD_CTRL_I2C))
#define MX53_PAD_CSI0_DAT8__EMI_EMI_DEBUG_37 (_MX53_PAD_CSI0_DAT8__EMI_EMI_DEBUG_37 | MUX_PAD_CTRL(NO_PAD_CTRL))
#define MX53_PAD_CSI0_DAT8__TPIU_TRACE_5 (_MX53_PAD_CSI0_DAT8__TPIU_TRACE_5 | MUX_PAD_CTRL(NO_PAD_CTRL))
#define MX53_PAD_CSI0_DAT9__IPU_CSI0_D_9 (_MX53_PAD_CSI0_DAT9__IPU_CSI0_D_9 | MUX_PAD_CTRL(NO_PAD_CTRL))
@@ -1544,7 +1547,7 @@
#define MX53_PAD_CSI0_DAT9__KPP_ROW_7 (_MX53_PAD_CSI0_DAT9__KPP_ROW_7 | MUX_PAD_CTRL(NO_PAD_CTRL))
#define MX53_PAD_CSI0_DAT9__ECSPI2_MOSI (_MX53_PAD_CSI0_DAT9__ECSPI2_MOSI | MUX_PAD_CTRL(NO_PAD_CTRL))
#define MX53_PAD_CSI0_DAT9__USBOH3_USBH3_PWR (_MX53_PAD_CSI0_DAT9__USBOH3_USBH3_PWR | MUX_PAD_CTRL(NO_PAD_CTRL))
-#define MX53_PAD_CSI0_DAT9__I2C1_SCL (_MX53_PAD_CSI0_DAT9__I2C1_SCL | MUX_PAD_CTRL(NO_PAD_CTRL))
+#define MX53_PAD_CSI0_DAT9__I2C1_SCL (_MX53_PAD_CSI0_DAT9__I2C1_SCL | MUX_PAD_CTRL(PAD_CTRL_I2C))
#define MX53_PAD_CSI0_DAT9__EMI_EMI_DEBUG_38 (_MX53_PAD_CSI0_DAT9__EMI_EMI_DEBUG_38 | MUX_PAD_CTRL(NO_PAD_CTRL))
#define MX53_PAD_CSI0_DAT9__TPIU_TRACE_6 (_MX53_PAD_CSI0_DAT9__TPIU_TRACE_6 | MUX_PAD_CTRL(NO_PAD_CTRL))
#define MX53_PAD_CSI0_DAT10__IPU_CSI0_D_10 (_MX53_PAD_CSI0_DAT10__IPU_CSI0_D_10 | MUX_PAD_CTRL(NO_PAD_CTRL))
@@ -1631,25 +1634,25 @@
#define MX53_PAD_EIM_EB2__CCM_DI1_EXT_CLK (_MX53_PAD_EIM_EB2__CCM_DI1_EXT_CLK | MUX_PAD_CTRL(NO_PAD_CTRL))
#define MX53_PAD_EIM_EB2__IPU_SER_DISP1_CS (_MX53_PAD_EIM_EB2__IPU_SER_DISP1_CS | MUX_PAD_CTRL(NO_PAD_CTRL))
#define MX53_PAD_EIM_EB2__ECSPI1_SS0 (_MX53_PAD_EIM_EB2__ECSPI1_SS0 | MUX_PAD_CTRL(NO_PAD_CTRL))
-#define MX53_PAD_EIM_EB2__I2C2_SCL (_MX53_PAD_EIM_EB2__I2C2_SCL | MUX_PAD_CTRL(NO_PAD_CTRL))
+#define MX53_PAD_EIM_EB2__I2C2_SCL (_MX53_PAD_EIM_EB2__I2C2_SCL | MUX_PAD_CTRL(PAD_CTRL_I2C))
#define MX53_PAD_EIM_D16__EMI_WEIM_D_16 (_MX53_PAD_EIM_D16__EMI_WEIM_D_16 | MUX_PAD_CTRL(NO_PAD_CTRL))
#define MX53_PAD_EIM_D16__GPIO3_16 (_MX53_PAD_EIM_D16__GPIO3_16 | MUX_PAD_CTRL(NO_PAD_CTRL))
#define MX53_PAD_EIM_D16__IPU_DI0_PIN5 (_MX53_PAD_EIM_D16__IPU_DI0_PIN5 | MUX_PAD_CTRL(NO_PAD_CTRL))
#define MX53_PAD_EIM_D16__IPU_DISPB1_SER_CLK (_MX53_PAD_EIM_D16__IPU_DISPB1_SER_CLK | MUX_PAD_CTRL(NO_PAD_CTRL))
#define MX53_PAD_EIM_D16__ECSPI1_SCLK (_MX53_PAD_EIM_D16__ECSPI1_SCLK | MUX_PAD_CTRL(NO_PAD_CTRL))
-#define MX53_PAD_EIM_D16__I2C2_SDA (_MX53_PAD_EIM_D16__I2C2_SDA | MUX_PAD_CTRL(NO_PAD_CTRL))
+#define MX53_PAD_EIM_D16__I2C2_SDA (_MX53_PAD_EIM_D16__I2C2_SDA | MUX_PAD_CTRL(PAD_CTRL_I2C))
#define MX53_PAD_EIM_D17__EMI_WEIM_D_17 (_MX53_PAD_EIM_D17__EMI_WEIM_D_17 | MUX_PAD_CTRL(NO_PAD_CTRL))
#define MX53_PAD_EIM_D17__GPIO3_17 (_MX53_PAD_EIM_D17__GPIO3_17 | MUX_PAD_CTRL(NO_PAD_CTRL))
#define MX53_PAD_EIM_D17__IPU_DI0_PIN6 (_MX53_PAD_EIM_D17__IPU_DI0_PIN6 | MUX_PAD_CTRL(NO_PAD_CTRL))
#define MX53_PAD_EIM_D17__IPU_DISPB1_SER_DIN (_MX53_PAD_EIM_D17__IPU_DISPB1_SER_DIN | MUX_PAD_CTRL(NO_PAD_CTRL))
#define MX53_PAD_EIM_D17__ECSPI1_MISO (_MX53_PAD_EIM_D17__ECSPI1_MISO | MUX_PAD_CTRL(NO_PAD_CTRL))
-#define MX53_PAD_EIM_D17__I2C3_SCL (_MX53_PAD_EIM_D17__I2C3_SCL | MUX_PAD_CTRL(NO_PAD_CTRL))
+#define MX53_PAD_EIM_D17__I2C3_SCL (_MX53_PAD_EIM_D17__I2C3_SCL | MUX_PAD_CTRL(PAD_CTRL_I2C))
#define MX53_PAD_EIM_D18__EMI_WEIM_D_18 (_MX53_PAD_EIM_D18__EMI_WEIM_D_18 | MUX_PAD_CTRL(NO_PAD_CTRL))
#define MX53_PAD_EIM_D18__GPIO3_18 (_MX53_PAD_EIM_D18__GPIO3_18 | MUX_PAD_CTRL(NO_PAD_CTRL))
#define MX53_PAD_EIM_D18__IPU_DI0_PIN7 (_MX53_PAD_EIM_D18__IPU_DI0_PIN7 | MUX_PAD_CTRL(NO_PAD_CTRL))
#define MX53_PAD_EIM_D18__IPU_DISPB1_SER_DIO (_MX53_PAD_EIM_D18__IPU_DISPB1_SER_DIO | MUX_PAD_CTRL(NO_PAD_CTRL))
#define MX53_PAD_EIM_D18__ECSPI1_MOSI (_MX53_PAD_EIM_D18__ECSPI1_MOSI | MUX_PAD_CTRL(NO_PAD_CTRL))
-#define MX53_PAD_EIM_D18__I2C3_SDA (_MX53_PAD_EIM_D18__I2C3_SDA | MUX_PAD_CTRL(NO_PAD_CTRL))
+#define MX53_PAD_EIM_D18__I2C3_SDA (_MX53_PAD_EIM_D18__I2C3_SDA | MUX_PAD_CTRL(PAD_CTRL_I2C))
#define MX53_PAD_EIM_D18__IPU_DI1_D0_CS (_MX53_PAD_EIM_D18__IPU_DI1_D0_CS | MUX_PAD_CTRL(NO_PAD_CTRL))
#define MX53_PAD_EIM_D19__EMI_WEIM_D_19 (_MX53_PAD_EIM_D19__EMI_WEIM_D_19 | MUX_PAD_CTRL(NO_PAD_CTRL))
#define MX53_PAD_EIM_D19__GPIO3_19 (_MX53_PAD_EIM_D19__GPIO3_19 | MUX_PAD_CTRL(NO_PAD_CTRL))
@@ -1672,7 +1675,7 @@
#define MX53_PAD_EIM_D21__IPU_DI0_PIN17 (_MX53_PAD_EIM_D21__IPU_DI0_PIN17 | MUX_PAD_CTRL(NO_PAD_CTRL))
#define MX53_PAD_EIM_D21__IPU_DISPB0_SER_CLK (_MX53_PAD_EIM_D21__IPU_DISPB0_SER_CLK | MUX_PAD_CTRL(NO_PAD_CTRL))
#define MX53_PAD_EIM_D21__CSPI_SCLK (_MX53_PAD_EIM_D21__CSPI_SCLK | MUX_PAD_CTRL(NO_PAD_CTRL))
-#define MX53_PAD_EIM_D21__I2C1_SCL (_MX53_PAD_EIM_D21__I2C1_SCL | MUX_PAD_CTRL(NO_PAD_CTRL))
+#define MX53_PAD_EIM_D21__I2C1_SCL (_MX53_PAD_EIM_D21__I2C1_SCL | MUX_PAD_CTRL(PAD_CTRL_I2C))
#define MX53_PAD_EIM_D21__USBOH3_USBOTG_OC (_MX53_PAD_EIM_D21__USBOH3_USBOTG_OC | MUX_PAD_CTRL(NO_PAD_CTRL))
#define MX53_PAD_EIM_D22__EMI_WEIM_D_22 (_MX53_PAD_EIM_D22__EMI_WEIM_D_22 | MUX_PAD_CTRL(NO_PAD_CTRL))
#define MX53_PAD_EIM_D22__GPIO3_22 (_MX53_PAD_EIM_D22__GPIO3_22 | MUX_PAD_CTRL(NO_PAD_CTRL))
@@ -1732,7 +1735,7 @@
#define MX53_PAD_EIM_D28__UART2_CTS (_MX53_PAD_EIM_D28__UART2_CTS | MUX_PAD_CTRL(MX53_UART_PAD_CTRL))
#define MX53_PAD_EIM_D28__IPU_DISPB0_SER_DIO (_MX53_PAD_EIM_D28__IPU_DISPB0_SER_DIO | MUX_PAD_CTRL(NO_PAD_CTRL))
#define MX53_PAD_EIM_D28__CSPI_MOSI (_MX53_PAD_EIM_D28__CSPI_MOSI | MUX_PAD_CTRL(NO_PAD_CTRL))
-#define MX53_PAD_EIM_D28__I2C1_SDA (_MX53_PAD_EIM_D28__I2C1_SDA | MUX_PAD_CTRL(NO_PAD_CTRL))
+#define MX53_PAD_EIM_D28__I2C1_SDA (_MX53_PAD_EIM_D28__I2C1_SDA | MUX_PAD_CTRL(PAD_CTRL_I2C))
#define MX53_PAD_EIM_D28__IPU_EXT_TRIG (_MX53_PAD_EIM_D28__IPU_EXT_TRIG | MUX_PAD_CTRL(NO_PAD_CTRL))
#define MX53_PAD_EIM_D28__IPU_DI0_PIN13 (_MX53_PAD_EIM_D28__IPU_DI0_PIN13 | MUX_PAD_CTRL(NO_PAD_CTRL))
#define MX53_PAD_EIM_D29__EMI_WEIM_D_29 (_MX53_PAD_EIM_D29__EMI_WEIM_D_29 | MUX_PAD_CTRL(NO_PAD_CTRL))
@@ -2297,7 +2300,7 @@
#define MX53_PAD_GPIO_9__SCC_FAIL_STATE (_MX53_PAD_GPIO_9__SCC_FAIL_STATE | MUX_PAD_CTRL(NO_PAD_CTRL))
#define MX53_PAD_GPIO_3__ESAI1_HCKR (_MX53_PAD_GPIO_3__ESAI1_HCKR | MUX_PAD_CTRL(NO_PAD_CTRL))
#define MX53_PAD_GPIO_3__GPIO1_3 (_MX53_PAD_GPIO_3__GPIO1_3 | MUX_PAD_CTRL(NO_PAD_CTRL))
-#define MX53_PAD_GPIO_3__I2C3_SCL (_MX53_PAD_GPIO_3__I2C3_SCL | MUX_PAD_CTRL(NO_PAD_CTRL))
+#define MX53_PAD_GPIO_3__I2C3_SCL (_MX53_PAD_GPIO_3__I2C3_SCL | MUX_PAD_CTRL(PAD_CTRL_I2C))
#define MX53_PAD_GPIO_3__DPLLIP1_TOG_EN (_MX53_PAD_GPIO_3__DPLLIP1_TOG_EN | MUX_PAD_CTRL(NO_PAD_CTRL))
#define MX53_PAD_GPIO_3__CCM_CLKO2 (_MX53_PAD_GPIO_3__CCM_CLKO2 | MUX_PAD_CTRL(NO_PAD_CTRL))
#define MX53_PAD_GPIO_3__OBSERVE_MUX_OBSRV_INT_OUT0 (_MX53_PAD_GPIO_3__OBSERVE_MUX_OBSRV_INT_OUT0 | MUX_PAD_CTRL(NO_PAD_CTRL))
@@ -2305,7 +2308,7 @@
#define MX53_PAD_GPIO_3__MLB_MLBCLK (_MX53_PAD_GPIO_3__MLB_MLBCLK | MUX_PAD_CTRL(NO_PAD_CTRL))
#define MX53_PAD_GPIO_6__ESAI1_SCKT (_MX53_PAD_GPIO_6__ESAI1_SCKT | MUX_PAD_CTRL(NO_PAD_CTRL))
#define MX53_PAD_GPIO_6__GPIO1_6 (_MX53_PAD_GPIO_6__GPIO1_6 | MUX_PAD_CTRL(NO_PAD_CTRL))
-#define MX53_PAD_GPIO_6__I2C3_SDA (_MX53_PAD_GPIO_6__I2C3_SDA | MUX_PAD_CTRL(NO_PAD_CTRL))
+#define MX53_PAD_GPIO_6__I2C3_SDA (_MX53_PAD_GPIO_6__I2C3_SDA | MUX_PAD_CTRL(PAD_CTRL_I2C))
#define MX53_PAD_GPIO_6__CCM_CCM_OUT_0 (_MX53_PAD_GPIO_6__CCM_CCM_OUT_0 | MUX_PAD_CTRL(NO_PAD_CTRL))
#define MX53_PAD_GPIO_6__CSU_CSU_INT_DEB (_MX53_PAD_GPIO_6__CSU_CSU_INT_DEB | MUX_PAD_CTRL(NO_PAD_CTRL))
#define MX53_PAD_GPIO_6__OBSERVE_MUX_OBSRV_INT_OUT1 (_MX53_PAD_GPIO_6__OBSERVE_MUX_OBSRV_INT_OUT1 | MUX_PAD_CTRL(NO_PAD_CTRL))
@@ -2333,7 +2336,7 @@
#define MX53_PAD_GPIO_5__CCM_CLKO (_MX53_PAD_GPIO_5__CCM_CLKO | MUX_PAD_CTRL(NO_PAD_CTRL))
#define MX53_PAD_GPIO_5__CSU_CSU_ALARM_AUT_2 (_MX53_PAD_GPIO_5__CSU_CSU_ALARM_AUT_2 | MUX_PAD_CTRL(NO_PAD_CTRL))
#define MX53_PAD_GPIO_5__OBSERVE_MUX_OBSRV_INT_OUT4 (_MX53_PAD_GPIO_5__OBSERVE_MUX_OBSRV_INT_OUT4 | MUX_PAD_CTRL(NO_PAD_CTRL))
-#define MX53_PAD_GPIO_5__I2C3_SCL (_MX53_PAD_GPIO_5__I2C3_SCL | MUX_PAD_CTRL(NO_PAD_CTRL))
+#define MX53_PAD_GPIO_5__I2C3_SCL (_MX53_PAD_GPIO_5__I2C3_SCL | MUX_PAD_CTRL(PAD_CTRL_I2C))
#define MX53_PAD_GPIO_5__CCM_PLL1_BYP (_MX53_PAD_GPIO_5__CCM_PLL1_BYP | MUX_PAD_CTRL(NO_PAD_CTRL))
#define MX53_PAD_GPIO_7__ESAI1_TX4_RX1 (_MX53_PAD_GPIO_7__ESAI1_TX4_RX1 | MUX_PAD_CTRL(NO_PAD_CTRL))
#define MX53_PAD_GPIO_7__GPIO1_7 (_MX53_PAD_GPIO_7__GPIO1_7 | MUX_PAD_CTRL(NO_PAD_CTRL))
@@ -2356,7 +2359,7 @@
#define MX53_PAD_GPIO_16__TZIC_PWRFAIL_INT (_MX53_PAD_GPIO_16__TZIC_PWRFAIL_INT | MUX_PAD_CTRL(NO_PAD_CTRL))
#define MX53_PAD_GPIO_16__RTC_CE_RTC_EXT_TRIG1 (_MX53_PAD_GPIO_16__RTC_CE_RTC_EXT_TRIG1 | MUX_PAD_CTRL(NO_PAD_CTRL))
#define MX53_PAD_GPIO_16__SPDIF_IN1 (_MX53_PAD_GPIO_16__SPDIF_IN1 | MUX_PAD_CTRL(NO_PAD_CTRL))
-#define MX53_PAD_GPIO_16__I2C3_SDA (_MX53_PAD_GPIO_16__I2C3_SDA | MUX_PAD_CTRL(NO_PAD_CTRL))
+#define MX53_PAD_GPIO_16__I2C3_SDA (_MX53_PAD_GPIO_16__I2C3_SDA | MUX_PAD_CTRL(PAD_CTRL_I2C))
#define MX53_PAD_GPIO_16__SJC_DE_B (_MX53_PAD_GPIO_16__SJC_DE_B | MUX_PAD_CTRL(NO_PAD_CTRL))
#define MX53_PAD_GPIO_17__ESAI1_TX0 (_MX53_PAD_GPIO_17__ESAI1_TX0 | MUX_PAD_CTRL(NO_PAD_CTRL))
#define MX53_PAD_GPIO_17__GPIO7_12 (_MX53_PAD_GPIO_17__GPIO7_12 | MUX_PAD_CTRL(NO_PAD_CTRL))
diff --git a/arch/arm/plat-omap/Kconfig b/arch/arm/plat-omap/Kconfig
index 6e6735f..bb8f4a6 100644
--- a/arch/arm/plat-omap/Kconfig
+++ b/arch/arm/plat-omap/Kconfig
@@ -13,6 +13,7 @@
bool "TI OMAP1"
select CLKDEV_LOOKUP
select CLKSRC_MMIO
+ select GENERIC_IRQ_CHIP
help
"Systems based on omap7xx, omap15xx or omap16xx"
diff --git a/arch/arm/plat-omap/include/plat/dma.h b/arch/arm/plat-omap/include/plat/dma.h
index d1c916f..dc562a5 100644
--- a/arch/arm/plat-omap/include/plat/dma.h
+++ b/arch/arm/plat-omap/include/plat/dma.h
@@ -195,6 +195,11 @@
#define OMAP36XX_DMA_UART4_TX 81 /* S_DMA_80 */
#define OMAP36XX_DMA_UART4_RX 82 /* S_DMA_81 */
+
+/* Only for AM35xx */
+#define AM35XX_DMA_UART4_TX 54
+#define AM35XX_DMA_UART4_RX 55
+
/*----------------------------------------------------------------------------*/
#define OMAP1_DMA_TOUT_IRQ (1 << 0)
diff --git a/arch/arm/plat-omap/include/plat/irqs.h b/arch/arm/plat-omap/include/plat/irqs.h
index 926d25c..30e1071 100644
--- a/arch/arm/plat-omap/include/plat/irqs.h
+++ b/arch/arm/plat-omap/include/plat/irqs.h
@@ -357,6 +357,7 @@
#define INT_35XX_EMAC_C0_TX_PULSE_IRQ 69
#define INT_35XX_EMAC_C0_MISC_PULSE_IRQ 70
#define INT_35XX_USBOTG_IRQ 71
+#define INT_35XX_UART4 84
#define INT_35XX_CCDC_VD0_IRQ 88
#define INT_35XX_CCDC_VD1_IRQ 92
#define INT_35XX_CCDC_VD2_IRQ 93
diff --git a/arch/arm/plat-omap/include/plat/serial.h b/arch/arm/plat-omap/include/plat/serial.h
index 2723f91..de3b10c 100644
--- a/arch/arm/plat-omap/include/plat/serial.h
+++ b/arch/arm/plat-omap/include/plat/serial.h
@@ -56,6 +56,9 @@
#define TI816X_UART2_BASE 0x48022000
#define TI816X_UART3_BASE 0x48024000
+/* AM3505/3517 UART4 */
+#define AM35XX_UART4_BASE 0x4809E000 /* Only on AM3505/3517 */
+
/* External port on Zoom2/3 */
#define ZOOM_UART_BASE 0x10000000
#define ZOOM_UART_VIRT 0xfa400000
diff --git a/arch/arm/plat-omap/iovmm.c b/arch/arm/plat-omap/iovmm.c
index c60737c..79e7fed 100644
--- a/arch/arm/plat-omap/iovmm.c
+++ b/arch/arm/plat-omap/iovmm.c
@@ -423,9 +423,6 @@
{
unsigned int i;
struct scatterlist *sg;
- void *va;
-
- va = phys_to_virt(pa);
for_each_sg(sgt->sgl, sg, sgt->nents, i) {
unsigned bytes;
diff --git a/arch/arm/tools/mach-types b/arch/arm/tools/mach-types
index 3b3776d..fff68d0 100644
--- a/arch/arm/tools/mach-types
+++ b/arch/arm/tools/mach-types
@@ -910,7 +910,7 @@
uemd MACH_UEMD UEMD 3281
ccwmx51mut MACH_CCWMX51MUT CCWMX51MUT 3282
rockhopper MACH_ROCKHOPPER ROCKHOPPER 3283
-nookcolor MACH_NOOKCOLOR NOOKCOLOR 3284
+encore MACH_ENCORE ENCORE 3284
hkdkc100 MACH_HKDKC100 HKDKC100 3285
ts42xx MACH_TS42XX TS42XX 3286
aebl MACH_AEBL AEBL 3287
diff --git a/arch/avr32/Kconfig b/arch/avr32/Kconfig
index e9d689b..197e96f 100644
--- a/arch/avr32/Kconfig
+++ b/arch/avr32/Kconfig
@@ -10,6 +10,7 @@
select GENERIC_IRQ_PROBE
select HARDIRQS_SW_RESEND
select GENERIC_IRQ_SHOW
+ select ARCH_HAVE_NMI_SAFE_CMPXCHG
help
AVR32 is a high-performance 32-bit RISC microprocessor core,
designed for cost-sensitive embedded applications, with particular
diff --git a/arch/cris/arch-v10/drivers/sync_serial.c b/arch/cris/arch-v10/drivers/sync_serial.c
index 85026537..466af40 100644
--- a/arch/cris/arch-v10/drivers/sync_serial.c
+++ b/arch/cris/arch-v10/drivers/sync_serial.c
@@ -158,7 +158,7 @@
static int sync_serial_release(struct inode *inode, struct file *file);
static unsigned int sync_serial_poll(struct file *filp, poll_table *wait);
-static int sync_serial_ioctl(struct file *file,
+static long sync_serial_ioctl(struct file *file,
unsigned int cmd, unsigned long arg);
static ssize_t sync_serial_write(struct file *file, const char *buf,
size_t count, loff_t *ppos);
@@ -625,11 +625,11 @@
*R_IRQ_MASK1_SET = 1 << port->data_avail_bit;
DEBUG(printk(KERN_DEBUG "sser%d rec started\n", dev));
}
- ret = 0;
+ err = 0;
out:
mutex_unlock(&sync_serial_mutex);
- return ret;
+ return err;
}
static int sync_serial_release(struct inode *inode, struct file *file)
diff --git a/arch/cris/arch-v10/kernel/irq.c b/arch/cris/arch-v10/kernel/irq.c
index 907cfb5..ba0e596 100644
--- a/arch/cris/arch-v10/kernel/irq.c
+++ b/arch/cris/arch-v10/kernel/irq.c
@@ -20,6 +20,9 @@
#define crisv10_mask_irq(irq_nr) (*R_VECT_MASK_CLR = 1 << (irq_nr));
#define crisv10_unmask_irq(irq_nr) (*R_VECT_MASK_SET = 1 << (irq_nr));
+extern void kgdb_init(void);
+extern void breakpoint(void);
+
/* don't use set_int_vector, it bypasses the linux interrupt handlers. it is
* global just so that the kernel gdb can use it.
*/
diff --git a/arch/cris/include/asm/thread_info.h b/arch/cris/include/asm/thread_info.h
index 29b74a1..332f19c 100644
--- a/arch/cris/include/asm/thread_info.h
+++ b/arch/cris/include/asm/thread_info.h
@@ -11,8 +11,6 @@
#ifdef __KERNEL__
-#define __HAVE_ARCH_THREAD_INFO_ALLOCATOR
-
#ifndef __ASSEMBLY__
#include <asm/types.h>
#include <asm/processor.h>
@@ -67,8 +65,10 @@
#define init_thread_info (init_thread_union.thread_info)
+#define __HAVE_ARCH_THREAD_INFO_ALLOCATOR
/* thread information allocation */
-#define alloc_thread_info(tsk, node) ((struct thread_info *) __get_free_pages(GFP_KERNEL,1))
+#define alloc_thread_info_node(tsk, node) \
+ ((struct thread_info *) __get_free_pages(GFP_KERNEL, 1))
#define free_thread_info(ti) free_pages((unsigned long) (ti), 1)
#endif /* !__ASSEMBLY__ */
diff --git a/arch/frv/Kconfig b/arch/frv/Kconfig
index cb884e4..bad27a6 100644
--- a/arch/frv/Kconfig
+++ b/arch/frv/Kconfig
@@ -7,6 +7,7 @@
select HAVE_PERF_EVENTS
select HAVE_GENERIC_HARDIRQS
select GENERIC_IRQ_SHOW
+ select ARCH_HAVE_NMI_SAFE_CMPXCHG
config ZONE_DMA
bool
diff --git a/arch/ia64/Kconfig b/arch/ia64/Kconfig
index 137b277..3ff7785 100644
--- a/arch/ia64/Kconfig
+++ b/arch/ia64/Kconfig
@@ -27,6 +27,8 @@
select GENERIC_PENDING_IRQ if SMP
select IRQ_PER_CPU
select GENERIC_IRQ_SHOW
+ select ARCH_WANT_OPTIONAL_GPIOLIB
+ select ARCH_HAVE_NMI_SAFE_CMPXCHG
default y
help
The Itanium Processor Family is Intel's 64-bit successor to
@@ -89,6 +91,9 @@
config HAVE_SETUP_PER_CPU_AREA
def_bool y
+config GENERIC_GPIO
+ def_bool y
+
config DMI
bool
default y
@@ -157,7 +162,6 @@
select ACPI_NUMA
select SWIOTLB
select PCI_MSI
- select DMAR
help
This selects the system type of your hardware. A "generic" kernel
will run on any supported IA-64 system. However, if you configure
diff --git a/arch/ia64/configs/generic_defconfig b/arch/ia64/configs/generic_defconfig
index 1d7bca0..0e5cd14 100644
--- a/arch/ia64/configs/generic_defconfig
+++ b/arch/ia64/configs/generic_defconfig
@@ -234,3 +234,4 @@
# CONFIG_CRYPTO_ANSI_CPRNG is not set
CONFIG_CRC_T10DIF=y
CONFIG_MISC_DEVICES=y
+CONFIG_DMAR=y
diff --git a/arch/ia64/include/asm/gpio.h b/arch/ia64/include/asm/gpio.h
new file mode 100644
index 0000000..590a20d
--- /dev/null
+++ b/arch/ia64/include/asm/gpio.h
@@ -0,0 +1,55 @@
+/*
+ * Generic GPIO API implementation for IA-64.
+ *
+ * A stright copy of that for PowerPC which was:
+ *
+ * Copyright (c) 2007-2008 MontaVista Software, Inc.
+ *
+ * Author: Anton Vorontsov <avorontsov@ru.mvista.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#ifndef _ASM_IA64_GPIO_H
+#define _ASM_IA64_GPIO_H
+
+#include <linux/errno.h>
+#include <asm-generic/gpio.h>
+
+#ifdef CONFIG_GPIOLIB
+
+/*
+ * We don't (yet) implement inlined/rapid versions for on-chip gpios.
+ * Just call gpiolib.
+ */
+static inline int gpio_get_value(unsigned int gpio)
+{
+ return __gpio_get_value(gpio);
+}
+
+static inline void gpio_set_value(unsigned int gpio, int value)
+{
+ __gpio_set_value(gpio, value);
+}
+
+static inline int gpio_cansleep(unsigned int gpio)
+{
+ return __gpio_cansleep(gpio);
+}
+
+static inline int gpio_to_irq(unsigned int gpio)
+{
+ return __gpio_to_irq(gpio);
+}
+
+static inline int irq_to_gpio(unsigned int irq)
+{
+ return -EINVAL;
+}
+
+#endif /* CONFIG_GPIOLIB */
+
+#endif /* _ASM_IA64_GPIO_H */
diff --git a/arch/ia64/kernel/efi.c b/arch/ia64/kernel/efi.c
index 6fc03af..c38d22e 100644
--- a/arch/ia64/kernel/efi.c
+++ b/arch/ia64/kernel/efi.c
@@ -156,7 +156,7 @@
#define STUB_SET_VARIABLE(prefix, adjust_arg) \
static efi_status_t \
prefix##_set_variable (efi_char16_t *name, efi_guid_t *vendor, \
- unsigned long attr, unsigned long data_size, \
+ u32 attr, unsigned long data_size, \
void *data) \
{ \
struct ia64_fpreg fr[6]; \
diff --git a/arch/m68k/Kconfig b/arch/m68k/Kconfig
index 284cd37..9e8ee9d 100644
--- a/arch/m68k/Kconfig
+++ b/arch/m68k/Kconfig
@@ -6,6 +6,7 @@
select GENERIC_ATOMIC64 if MMU
select HAVE_GENERIC_HARDIRQS if !MMU
select GENERIC_IRQ_SHOW if !MMU
+ select ARCH_HAVE_NMI_SAFE_CMPXCHG if RMW_INSNS
config RWSEM_GENERIC_SPINLOCK
bool
diff --git a/arch/m68k/Kconfig.mmu b/arch/m68k/Kconfig.mmu
index 16539b1..13e20bb 100644
--- a/arch/m68k/Kconfig.mmu
+++ b/arch/m68k/Kconfig.mmu
@@ -372,12 +372,6 @@
Include support in the kernel for pcmcia on Amiga 1200 and Amiga
600. If you intend to use pcmcia cards say Y; otherwise say N.
-config STRAM_PROC
- bool "ST-RAM statistics in /proc"
- depends on ATARI
- help
- Say Y here to report ST-RAM usage statistics in /proc/stram.
-
config HEARTBEAT
bool "Use power LED as a heartbeat" if AMIGA || APOLLO || ATARI || MAC ||Q40
default y if !AMIGA && !APOLLO && !ATARI && !MAC && !Q40 && HP300
diff --git a/arch/m68k/amiga/chipram.c b/arch/m68k/amiga/chipram.c
index dd0447d..99449fb 100644
--- a/arch/m68k/amiga/chipram.c
+++ b/arch/m68k/amiga/chipram.c
@@ -16,6 +16,7 @@
#include <linux/string.h>
#include <linux/module.h>
+#include <asm/atomic.h>
#include <asm/page.h>
#include <asm/amigahw.h>
@@ -23,111 +24,100 @@
EXPORT_SYMBOL(amiga_chip_size);
static struct resource chipram_res = {
- .name = "Chip RAM", .start = CHIP_PHYSADDR
+ .name = "Chip RAM", .start = CHIP_PHYSADDR
};
-static unsigned long chipavail;
+static atomic_t chipavail;
void __init amiga_chip_init(void)
{
- if (!AMIGAHW_PRESENT(CHIP_RAM))
- return;
+ if (!AMIGAHW_PRESENT(CHIP_RAM))
+ return;
- chipram_res.end = amiga_chip_size-1;
- request_resource(&iomem_resource, &chipram_res);
+ chipram_res.end = CHIP_PHYSADDR + amiga_chip_size - 1;
+ request_resource(&iomem_resource, &chipram_res);
- chipavail = amiga_chip_size;
+ atomic_set(&chipavail, amiga_chip_size);
}
void *amiga_chip_alloc(unsigned long size, const char *name)
{
- struct resource *res;
+ struct resource *res;
+ void *p;
- /* round up */
- size = PAGE_ALIGN(size);
+ res = kzalloc(sizeof(struct resource), GFP_KERNEL);
+ if (!res)
+ return NULL;
-#ifdef DEBUG
- printk("amiga_chip_alloc: allocate %ld bytes\n", size);
-#endif
- res = kzalloc(sizeof(struct resource), GFP_KERNEL);
- if (!res)
- return NULL;
- res->name = name;
+ res->name = name;
+ p = amiga_chip_alloc_res(size, res);
+ if (!p) {
+ kfree(res);
+ return NULL;
+ }
- if (allocate_resource(&chipram_res, res, size, 0, UINT_MAX, PAGE_SIZE, NULL, NULL) < 0) {
- kfree(res);
- return NULL;
- }
- chipavail -= size;
-#ifdef DEBUG
- printk("amiga_chip_alloc: returning %lx\n", res->start);
-#endif
- return (void *)ZTWO_VADDR(res->start);
+ return p;
}
EXPORT_SYMBOL(amiga_chip_alloc);
- /*
- * Warning:
- * amiga_chip_alloc_res is meant only for drivers that need to allocate
- * Chip RAM before kmalloc() is functional. As a consequence, those
- * drivers must not free that Chip RAM afterwards.
- */
+ /*
+ * Warning:
+ * amiga_chip_alloc_res is meant only for drivers that need to
+ * allocate Chip RAM before kmalloc() is functional. As a consequence,
+ * those drivers must not free that Chip RAM afterwards.
+ */
-void * __init amiga_chip_alloc_res(unsigned long size, struct resource *res)
+void *amiga_chip_alloc_res(unsigned long size, struct resource *res)
{
- unsigned long start;
+ int error;
- /* round up */
- size = PAGE_ALIGN(size);
- /* dmesg into chipmem prefers memory at the safe end */
- start = CHIP_PHYSADDR + chipavail - size;
+ /* round up */
+ size = PAGE_ALIGN(size);
-#ifdef DEBUG
- printk("amiga_chip_alloc_res: allocate %ld bytes\n", size);
-#endif
- if (allocate_resource(&chipram_res, res, size, start, UINT_MAX, PAGE_SIZE, NULL, NULL) < 0) {
- printk("amiga_chip_alloc_res: first alloc failed!\n");
- if (allocate_resource(&chipram_res, res, size, 0, UINT_MAX, PAGE_SIZE, NULL, NULL) < 0)
- return NULL;
- }
- chipavail -= size;
-#ifdef DEBUG
- printk("amiga_chip_alloc_res: returning %lx\n", res->start);
-#endif
- return (void *)ZTWO_VADDR(res->start);
+ pr_debug("amiga_chip_alloc_res: allocate %lu bytes\n", size);
+ error = allocate_resource(&chipram_res, res, size, 0, UINT_MAX,
+ PAGE_SIZE, NULL, NULL);
+ if (error < 0) {
+ pr_err("amiga_chip_alloc_res: allocate_resource() failed %d!\n",
+ error);
+ return NULL;
+ }
+
+ atomic_sub(size, &chipavail);
+ pr_debug("amiga_chip_alloc_res: returning %pR\n", res);
+ return (void *)ZTWO_VADDR(res->start);
}
void amiga_chip_free(void *ptr)
{
- unsigned long start = ZTWO_PADDR(ptr);
- struct resource **p, *res;
- unsigned long size;
+ unsigned long start = ZTWO_PADDR(ptr);
+ struct resource *res;
+ unsigned long size;
- for (p = &chipram_res.child; (res = *p); p = &res->sibling) {
- if (res->start != start)
- continue;
- *p = res->sibling;
- size = res->end-start;
-#ifdef DEBUG
- printk("amiga_chip_free: free %ld bytes at %p\n", size, ptr);
-#endif
- chipavail += size;
+ res = lookup_resource(&chipram_res, start);
+ if (!res) {
+ pr_err("amiga_chip_free: trying to free nonexistent region at "
+ "%p\n", ptr);
+ return;
+ }
+
+ size = resource_size(res);
+ pr_debug("amiga_chip_free: free %lu bytes at %p\n", size, ptr);
+ atomic_add(size, &chipavail);
+ release_resource(res);
kfree(res);
- return;
- }
- printk("amiga_chip_free: trying to free nonexistent region at %p\n", ptr);
}
EXPORT_SYMBOL(amiga_chip_free);
unsigned long amiga_chip_avail(void)
{
-#ifdef DEBUG
- printk("amiga_chip_avail : %ld bytes\n", chipavail);
-#endif
- return chipavail;
+ unsigned long n = atomic_read(&chipavail);
+
+ pr_debug("amiga_chip_avail : %lu bytes\n", n);
+ return n;
}
EXPORT_SYMBOL(amiga_chip_avail);
diff --git a/arch/m68k/atari/stram.c b/arch/m68k/atari/stram.c
index 6ec3b7f..0810c8d 100644
--- a/arch/m68k/atari/stram.c
+++ b/arch/m68k/atari/stram.c
@@ -1,5 +1,5 @@
/*
- * arch/m68k/atari/stram.c: Functions for ST-RAM allocations
+ * Functions for ST-RAM allocations
*
* Copyright 1994-97 Roman Hodek <Roman.Hodek@informatik.uni-erlangen.de>
*
@@ -30,91 +30,35 @@
#include <asm/atari_stram.h>
#include <asm/io.h>
-#undef DEBUG
-
-#ifdef DEBUG
-#define DPRINTK(fmt,args...) printk( fmt, ##args )
-#else
-#define DPRINTK(fmt,args...)
-#endif
-
-#if defined(CONFIG_PROC_FS) && defined(CONFIG_STRAM_PROC)
-/* abbrev for the && above... */
-#define DO_PROC
-#include <linux/proc_fs.h>
-#include <linux/seq_file.h>
-#endif
/*
- * ++roman:
- *
- * New version of ST-Ram buffer allocation. Instead of using the
- * 1 MB - 4 KB that remain when the ST-Ram chunk starts at $1000
- * (1 MB granularity!), such buffers are reserved like this:
- *
- * - If the kernel resides in ST-Ram anyway, we can take the buffer
- * from behind the current kernel data space the normal way
- * (incrementing start_mem).
- *
- * - If the kernel is in TT-Ram, stram_init() initializes start and
- * end of the available region. Buffers are allocated from there
- * and mem_init() later marks the such used pages as reserved.
- * Since each TT-Ram chunk is at least 4 MB in size, I hope there
- * won't be an overrun of the ST-Ram region by normal kernel data
- * space.
- *
- * For that, ST-Ram may only be allocated while kernel initialization
- * is going on, or exactly: before mem_init() is called. There is also
- * no provision now for freeing ST-Ram buffers. It seems that isn't
- * really needed.
- *
+ * The ST-RAM allocator allocates memory from a pool of reserved ST-RAM of
+ * configurable size, set aside on ST-RAM init.
+ * As long as this pool is not exhausted, allocation of real ST-RAM can be
+ * guaranteed.
*/
-/* Start and end (virtual) of ST-RAM */
-static void *stram_start, *stram_end;
-
-/* set after memory_init() executed and allocations via start_mem aren't
- * possible anymore */
-static int mem_init_done;
-
/* set if kernel is in ST-RAM */
static int kernel_in_stram;
-typedef struct stram_block {
- struct stram_block *next;
- void *start;
- unsigned long size;
- unsigned flags;
- const char *owner;
-} BLOCK;
+static struct resource stram_pool = {
+ .name = "ST-RAM Pool"
+};
-/* values for flags field */
-#define BLOCK_FREE 0x01 /* free structure in the BLOCKs pool */
-#define BLOCK_KMALLOCED 0x02 /* structure allocated by kmalloc() */
-#define BLOCK_GFP 0x08 /* block allocated with __get_dma_pages() */
+static unsigned long pool_size = 1024*1024;
-/* list of allocated blocks */
-static BLOCK *alloc_list;
-/* We can't always use kmalloc() to allocate BLOCK structures, since
- * stram_alloc() can be called rather early. So we need some pool of
- * statically allocated structures. 20 of them is more than enough, so in most
- * cases we never should need to call kmalloc(). */
-#define N_STATIC_BLOCKS 20
-static BLOCK static_blocks[N_STATIC_BLOCKS];
+static int __init atari_stram_setup(char *arg)
+{
+ if (!MACH_IS_ATARI)
+ return 0;
-/***************************** Prototypes *****************************/
+ pool_size = memparse(arg, NULL);
+ return 0;
+}
-static BLOCK *add_region( void *addr, unsigned long size );
-static BLOCK *find_region( void *addr );
-static int remove_region( BLOCK *block );
+early_param("stram_pool", atari_stram_setup);
-/************************* End of Prototypes **************************/
-
-
-/* ------------------------------------------------------------------------ */
-/* Public Interface */
-/* ------------------------------------------------------------------------ */
/*
* This init function is called very early by atari/config.c
@@ -123,25 +67,23 @@
void __init atari_stram_init(void)
{
int i;
+ void *stram_start;
- /* initialize static blocks */
- for( i = 0; i < N_STATIC_BLOCKS; ++i )
- static_blocks[i].flags = BLOCK_FREE;
-
- /* determine whether kernel code resides in ST-RAM (then ST-RAM is the
- * first memory block at virtual 0x0) */
+ /*
+ * determine whether kernel code resides in ST-RAM
+ * (then ST-RAM is the first memory block at virtual 0x0)
+ */
stram_start = phys_to_virt(0);
kernel_in_stram = (stram_start == 0);
- for( i = 0; i < m68k_num_memory; ++i ) {
+ for (i = 0; i < m68k_num_memory; ++i) {
if (m68k_memory[i].addr == 0) {
- /* skip first 2kB or page (supervisor-only!) */
- stram_end = stram_start + m68k_memory[i].size;
return;
}
}
+
/* Should never come here! (There is always ST-Ram!) */
- panic( "atari_stram_init: no ST-RAM found!" );
+ panic("atari_stram_init: no ST-RAM found!");
}
@@ -151,226 +93,68 @@
*/
void __init atari_stram_reserve_pages(void *start_mem)
{
- /* always reserve first page of ST-RAM, the first 2 kB are
- * supervisor-only! */
+ /*
+ * always reserve first page of ST-RAM, the first 2 KiB are
+ * supervisor-only!
+ */
if (!kernel_in_stram)
reserve_bootmem(0, PAGE_SIZE, BOOTMEM_DEFAULT);
-}
+ stram_pool.start = (resource_size_t)alloc_bootmem_low_pages(pool_size);
+ stram_pool.end = stram_pool.start + pool_size - 1;
+ request_resource(&iomem_resource, &stram_pool);
-void atari_stram_mem_init_hook (void)
-{
- mem_init_done = 1;
+ pr_debug("atari_stram pool: size = %lu bytes, resource = %pR\n",
+ pool_size, &stram_pool);
}
-/*
- * This is main public interface: somehow allocate a ST-RAM block
- *
- * - If we're before mem_init(), we have to make a static allocation. The
- * region is taken in the kernel data area (if the kernel is in ST-RAM) or
- * from the start of ST-RAM (if the kernel is in TT-RAM) and added to the
- * rsvd_stram_* region. The ST-RAM is somewhere in the middle of kernel
- * address space in the latter case.
- *
- * - If mem_init() already has been called, try with __get_dma_pages().
- * This has the disadvantage that it's very hard to get more than 1 page,
- * and it is likely to fail :-(
- *
- */
-void *atari_stram_alloc(long size, const char *owner)
+void *atari_stram_alloc(unsigned long size, const char *owner)
{
- void *addr = NULL;
- BLOCK *block;
- int flags;
+ struct resource *res;
+ int error;
- DPRINTK("atari_stram_alloc(size=%08lx,owner=%s)\n", size, owner);
+ pr_debug("atari_stram_alloc: allocate %lu bytes\n", size);
- if (!mem_init_done)
- return alloc_bootmem_low(size);
- else {
- /* After mem_init(): can only resort to __get_dma_pages() */
- addr = (void *)__get_dma_pages(GFP_KERNEL, get_order(size));
- flags = BLOCK_GFP;
- DPRINTK( "atari_stram_alloc: after mem_init, "
- "get_pages=%p\n", addr );
+ /* round up */
+ size = PAGE_ALIGN(size);
+
+ res = kzalloc(sizeof(struct resource), GFP_KERNEL);
+ if (!res)
+ return NULL;
+
+ res->name = owner;
+ error = allocate_resource(&stram_pool, res, size, 0, UINT_MAX,
+ PAGE_SIZE, NULL, NULL);
+ if (error < 0) {
+ pr_err("atari_stram_alloc: allocate_resource() failed %d!\n",
+ error);
+ kfree(res);
+ return NULL;
}
- if (addr) {
- if (!(block = add_region( addr, size ))) {
- /* out of memory for BLOCK structure :-( */
- DPRINTK( "atari_stram_alloc: out of mem for BLOCK -- "
- "freeing again\n" );
- free_pages((unsigned long)addr, get_order(size));
- return( NULL );
- }
- block->owner = owner;
- block->flags |= flags;
- }
- return( addr );
+ pr_debug("atari_stram_alloc: returning %pR\n", res);
+ return (void *)res->start;
}
EXPORT_SYMBOL(atari_stram_alloc);
-void atari_stram_free( void *addr )
+void atari_stram_free(void *addr)
{
- BLOCK *block;
+ unsigned long start = (unsigned long)addr;
+ struct resource *res;
+ unsigned long size;
- DPRINTK( "atari_stram_free(addr=%p)\n", addr );
-
- if (!(block = find_region( addr ))) {
- printk( KERN_ERR "Attempt to free non-allocated ST-RAM block at %p "
- "from %p\n", addr, __builtin_return_address(0) );
+ res = lookup_resource(&stram_pool, start);
+ if (!res) {
+ pr_err("atari_stram_free: trying to free nonexistent region "
+ "at %p\n", addr);
return;
}
- DPRINTK( "atari_stram_free: found block (%p): size=%08lx, owner=%s, "
- "flags=%02x\n", block, block->size, block->owner, block->flags );
- if (!(block->flags & BLOCK_GFP))
- goto fail;
-
- DPRINTK("atari_stram_free: is kmalloced, order_size=%d\n",
- get_order(block->size));
- free_pages((unsigned long)addr, get_order(block->size));
- remove_region( block );
- return;
-
- fail:
- printk( KERN_ERR "atari_stram_free: cannot free block at %p "
- "(called from %p)\n", addr, __builtin_return_address(0) );
+ size = resource_size(res);
+ pr_debug("atari_stram_free: free %lu bytes at %p\n", size, addr);
+ release_resource(res);
+ kfree(res);
}
EXPORT_SYMBOL(atari_stram_free);
-
-
-/* ------------------------------------------------------------------------ */
-/* Region Management */
-/* ------------------------------------------------------------------------ */
-
-
-/* insert a region into the alloced list (sorted) */
-static BLOCK *add_region( void *addr, unsigned long size )
-{
- BLOCK **p, *n = NULL;
- int i;
-
- for( i = 0; i < N_STATIC_BLOCKS; ++i ) {
- if (static_blocks[i].flags & BLOCK_FREE) {
- n = &static_blocks[i];
- n->flags = 0;
- break;
- }
- }
- if (!n && mem_init_done) {
- /* if statics block pool exhausted and we can call kmalloc() already
- * (after mem_init()), try that */
- n = kmalloc( sizeof(BLOCK), GFP_KERNEL );
- if (n)
- n->flags = BLOCK_KMALLOCED;
- }
- if (!n) {
- printk( KERN_ERR "Out of memory for ST-RAM descriptor blocks\n" );
- return( NULL );
- }
- n->start = addr;
- n->size = size;
-
- for( p = &alloc_list; *p; p = &((*p)->next) )
- if ((*p)->start > addr) break;
- n->next = *p;
- *p = n;
-
- return( n );
-}
-
-
-/* find a region (by start addr) in the alloced list */
-static BLOCK *find_region( void *addr )
-{
- BLOCK *p;
-
- for( p = alloc_list; p; p = p->next ) {
- if (p->start == addr)
- return( p );
- if (p->start > addr)
- break;
- }
- return( NULL );
-}
-
-
-/* remove a block from the alloced list */
-static int remove_region( BLOCK *block )
-{
- BLOCK **p;
-
- for( p = &alloc_list; *p; p = &((*p)->next) )
- if (*p == block) break;
- if (!*p)
- return( 0 );
-
- *p = block->next;
- if (block->flags & BLOCK_KMALLOCED)
- kfree( block );
- else
- block->flags |= BLOCK_FREE;
- return( 1 );
-}
-
-
-
-/* ------------------------------------------------------------------------ */
-/* /proc statistics file stuff */
-/* ------------------------------------------------------------------------ */
-
-#ifdef DO_PROC
-
-#define PRINT_PROC(fmt,args...) seq_printf( m, fmt, ##args )
-
-static int stram_proc_show(struct seq_file *m, void *v)
-{
- BLOCK *p;
-
- PRINT_PROC("Total ST-RAM: %8u kB\n",
- (stram_end - stram_start) >> 10);
- PRINT_PROC( "Allocated regions:\n" );
- for( p = alloc_list; p; p = p->next ) {
- PRINT_PROC("0x%08lx-0x%08lx: %s (",
- virt_to_phys(p->start),
- virt_to_phys(p->start+p->size-1),
- p->owner);
- if (p->flags & BLOCK_GFP)
- PRINT_PROC( "page-alloced)\n" );
- else
- PRINT_PROC( "??)\n" );
- }
-
- return 0;
-}
-
-static int stram_proc_open(struct inode *inode, struct file *file)
-{
- return single_open(file, stram_proc_show, NULL);
-}
-
-static const struct file_operations stram_proc_fops = {
- .open = stram_proc_open,
- .read = seq_read,
- .llseek = seq_lseek,
- .release = single_release,
-};
-
-static int __init proc_stram_init(void)
-{
- proc_create("stram", 0, NULL, &stram_proc_fops);
- return 0;
-}
-module_init(proc_stram_init);
-#endif
-
-
-/*
- * Local variables:
- * c-indent-level: 4
- * tab-width: 4
- * End:
- */
diff --git a/arch/m68k/include/asm/atari_stram.h b/arch/m68k/include/asm/atari_stram.h
index 7546d13..62e2759 100644
--- a/arch/m68k/include/asm/atari_stram.h
+++ b/arch/m68k/include/asm/atari_stram.h
@@ -6,12 +6,11 @@
*/
/* public interface */
-void *atari_stram_alloc(long size, const char *owner);
+void *atari_stram_alloc(unsigned long size, const char *owner);
void atari_stram_free(void *);
/* functions called internally by other parts of the kernel */
void atari_stram_init(void);
void atari_stram_reserve_pages(void *start_mem);
-void atari_stram_mem_init_hook (void);
#endif /*_M68K_ATARI_STRAM_H */
diff --git a/arch/m68k/include/asm/atarihw.h b/arch/m68k/include/asm/atarihw.h
index f51f709..0392b28 100644
--- a/arch/m68k/include/asm/atarihw.h
+++ b/arch/m68k/include/asm/atarihw.h
@@ -399,8 +399,8 @@
#define CODEC_OVERFLOW_LEFT 2
u_char unused2, unused3, unused4, unused5;
u_char gpio_directions;
-#define GPIO_IN 0
-#define GPIO_OUT 1
+#define CODEC_GPIO_IN 0
+#define CODEC_GPIO_OUT 1
u_char unused6;
u_char gpio_data;
};
diff --git a/arch/m68k/kernel/setup_mm.c b/arch/m68k/kernel/setup_mm.c
index 334d836..c3b4506 100644
--- a/arch/m68k/kernel/setup_mm.c
+++ b/arch/m68k/kernel/setup_mm.c
@@ -216,7 +216,9 @@
void __init setup_arch(char **cmdline_p)
{
+#ifndef CONFIG_SUN3
int i;
+#endif
/* The bootinfo is located right after the kernel bss */
m68k_parse_bootinfo((const struct bi_record *)_end);
diff --git a/arch/m68k/math-emu/fp_log.c b/arch/m68k/math-emu/fp_log.c
index 367ecee..3384a52 100644
--- a/arch/m68k/math-emu/fp_log.c
+++ b/arch/m68k/math-emu/fp_log.c
@@ -105,9 +105,6 @@
fp_monadic_check(dest, src);
- if (IS_ZERO(dest))
- return dest;
-
return dest;
}
diff --git a/arch/m68k/math-emu/multi_arith.h b/arch/m68k/math-emu/multi_arith.h
index 4ad0ca9..4b5eb3d 100644
--- a/arch/m68k/math-emu/multi_arith.h
+++ b/arch/m68k/math-emu/multi_arith.h
@@ -19,246 +19,6 @@
#ifndef MULTI_ARITH_H
#define MULTI_ARITH_H
-#if 0 /* old code... */
-
-/* Unsigned only, because we don't need signs to multiply and divide. */
-typedef unsigned int int128[4];
-
-/* Word order */
-enum {
- MSW128,
- NMSW128,
- NLSW128,
- LSW128
-};
-
-/* big-endian */
-#define LO_WORD(ll) (((unsigned int *) &ll)[1])
-#define HI_WORD(ll) (((unsigned int *) &ll)[0])
-
-/* Convenience functions to stuff various integer values into int128s */
-
-static inline void zero128(int128 a)
-{
- a[LSW128] = a[NLSW128] = a[NMSW128] = a[MSW128] = 0;
-}
-
-/* Human-readable word order in the arguments */
-static inline void set128(unsigned int i3, unsigned int i2, unsigned int i1,
- unsigned int i0, int128 a)
-{
- a[LSW128] = i0;
- a[NLSW128] = i1;
- a[NMSW128] = i2;
- a[MSW128] = i3;
-}
-
-/* Convenience functions (for testing as well) */
-static inline void int64_to_128(unsigned long long src, int128 dest)
-{
- dest[LSW128] = (unsigned int) src;
- dest[NLSW128] = src >> 32;
- dest[NMSW128] = dest[MSW128] = 0;
-}
-
-static inline void int128_to_64(const int128 src, unsigned long long *dest)
-{
- *dest = src[LSW128] | (long long) src[NLSW128] << 32;
-}
-
-static inline void put_i128(const int128 a)
-{
- printk("%08x %08x %08x %08x\n", a[MSW128], a[NMSW128],
- a[NLSW128], a[LSW128]);
-}
-
-/* Internal shifters:
-
- Note that these are only good for 0 < count < 32.
- */
-
-static inline void _lsl128(unsigned int count, int128 a)
-{
- a[MSW128] = (a[MSW128] << count) | (a[NMSW128] >> (32 - count));
- a[NMSW128] = (a[NMSW128] << count) | (a[NLSW128] >> (32 - count));
- a[NLSW128] = (a[NLSW128] << count) | (a[LSW128] >> (32 - count));
- a[LSW128] <<= count;
-}
-
-static inline void _lsr128(unsigned int count, int128 a)
-{
- a[LSW128] = (a[LSW128] >> count) | (a[NLSW128] << (32 - count));
- a[NLSW128] = (a[NLSW128] >> count) | (a[NMSW128] << (32 - count));
- a[NMSW128] = (a[NMSW128] >> count) | (a[MSW128] << (32 - count));
- a[MSW128] >>= count;
-}
-
-/* Should be faster, one would hope */
-
-static inline void lslone128(int128 a)
-{
- asm volatile ("lsl.l #1,%0\n"
- "roxl.l #1,%1\n"
- "roxl.l #1,%2\n"
- "roxl.l #1,%3\n"
- :
- "=d" (a[LSW128]),
- "=d"(a[NLSW128]),
- "=d"(a[NMSW128]),
- "=d"(a[MSW128])
- :
- "0"(a[LSW128]),
- "1"(a[NLSW128]),
- "2"(a[NMSW128]),
- "3"(a[MSW128]));
-}
-
-static inline void lsrone128(int128 a)
-{
- asm volatile ("lsr.l #1,%0\n"
- "roxr.l #1,%1\n"
- "roxr.l #1,%2\n"
- "roxr.l #1,%3\n"
- :
- "=d" (a[MSW128]),
- "=d"(a[NMSW128]),
- "=d"(a[NLSW128]),
- "=d"(a[LSW128])
- :
- "0"(a[MSW128]),
- "1"(a[NMSW128]),
- "2"(a[NLSW128]),
- "3"(a[LSW128]));
-}
-
-/* Generalized 128-bit shifters:
-
- These bit-shift to a multiple of 32, then move whole longwords. */
-
-static inline void lsl128(unsigned int count, int128 a)
-{
- int wordcount, i;
-
- if (count % 32)
- _lsl128(count % 32, a);
-
- if (0 == (wordcount = count / 32))
- return;
-
- /* argh, gak, endian-sensitive */
- for (i = 0; i < 4 - wordcount; i++) {
- a[i] = a[i + wordcount];
- }
- for (i = 3; i >= 4 - wordcount; --i) {
- a[i] = 0;
- }
-}
-
-static inline void lsr128(unsigned int count, int128 a)
-{
- int wordcount, i;
-
- if (count % 32)
- _lsr128(count % 32, a);
-
- if (0 == (wordcount = count / 32))
- return;
-
- for (i = 3; i >= wordcount; --i) {
- a[i] = a[i - wordcount];
- }
- for (i = 0; i < wordcount; i++) {
- a[i] = 0;
- }
-}
-
-static inline int orl128(int a, int128 b)
-{
- b[LSW128] |= a;
-}
-
-static inline int btsthi128(const int128 a)
-{
- return a[MSW128] & 0x80000000;
-}
-
-/* test bits (numbered from 0 = LSB) up to and including "top" */
-static inline int bftestlo128(int top, const int128 a)
-{
- int r = 0;
-
- if (top > 31)
- r |= a[LSW128];
- if (top > 63)
- r |= a[NLSW128];
- if (top > 95)
- r |= a[NMSW128];
-
- r |= a[3 - (top / 32)] & ((1 << (top % 32 + 1)) - 1);
-
- return (r != 0);
-}
-
-/* Aargh. We need these because GCC is broken */
-/* FIXME: do them in assembly, for goodness' sake! */
-static inline void mask64(int pos, unsigned long long *mask)
-{
- *mask = 0;
-
- if (pos < 32) {
- LO_WORD(*mask) = (1 << pos) - 1;
- return;
- }
- LO_WORD(*mask) = -1;
- HI_WORD(*mask) = (1 << (pos - 32)) - 1;
-}
-
-static inline void bset64(int pos, unsigned long long *dest)
-{
- /* This conditional will be optimized away. Thanks, GCC! */
- if (pos < 32)
- asm volatile ("bset %1,%0":"=m"
- (LO_WORD(*dest)):"id"(pos));
- else
- asm volatile ("bset %1,%0":"=m"
- (HI_WORD(*dest)):"id"(pos - 32));
-}
-
-static inline int btst64(int pos, unsigned long long dest)
-{
- if (pos < 32)
- return (0 != (LO_WORD(dest) & (1 << pos)));
- else
- return (0 != (HI_WORD(dest) & (1 << (pos - 32))));
-}
-
-static inline void lsl64(int count, unsigned long long *dest)
-{
- if (count < 32) {
- HI_WORD(*dest) = (HI_WORD(*dest) << count)
- | (LO_WORD(*dest) >> count);
- LO_WORD(*dest) <<= count;
- return;
- }
- count -= 32;
- HI_WORD(*dest) = LO_WORD(*dest) << count;
- LO_WORD(*dest) = 0;
-}
-
-static inline void lsr64(int count, unsigned long long *dest)
-{
- if (count < 32) {
- LO_WORD(*dest) = (LO_WORD(*dest) >> count)
- | (HI_WORD(*dest) << (32 - count));
- HI_WORD(*dest) >>= count;
- return;
- }
- count -= 32;
- LO_WORD(*dest) = HI_WORD(*dest) >> count;
- HI_WORD(*dest) = 0;
-}
-#endif
-
static inline void fp_denormalize(struct fp_ext *reg, unsigned int cnt)
{
reg->exp += cnt;
@@ -481,117 +241,6 @@
}
}
-#if 0
-static inline unsigned int fp_fls128(union fp_mant128 *src)
-{
- unsigned long data;
- unsigned int res, off;
-
- if ((data = src->m32[0]))
- off = 0;
- else if ((data = src->m32[1]))
- off = 32;
- else if ((data = src->m32[2]))
- off = 64;
- else if ((data = src->m32[3]))
- off = 96;
- else
- return 128;
-
- asm ("bfffo %1{#0,#32},%0" : "=d" (res) : "dm" (data));
- return res + off;
-}
-
-static inline void fp_shiftmant128(union fp_mant128 *src, int shift)
-{
- unsigned long sticky;
-
- switch (shift) {
- case 0:
- return;
- case 1:
- asm volatile ("lsl.l #1,%0"
- : "=d" (src->m32[3]) : "0" (src->m32[3]));
- asm volatile ("roxl.l #1,%0"
- : "=d" (src->m32[2]) : "0" (src->m32[2]));
- asm volatile ("roxl.l #1,%0"
- : "=d" (src->m32[1]) : "0" (src->m32[1]));
- asm volatile ("roxl.l #1,%0"
- : "=d" (src->m32[0]) : "0" (src->m32[0]));
- return;
- case 2 ... 31:
- src->m32[0] = (src->m32[0] << shift) | (src->m32[1] >> (32 - shift));
- src->m32[1] = (src->m32[1] << shift) | (src->m32[2] >> (32 - shift));
- src->m32[2] = (src->m32[2] << shift) | (src->m32[3] >> (32 - shift));
- src->m32[3] = (src->m32[3] << shift);
- return;
- case 32 ... 63:
- shift -= 32;
- src->m32[0] = (src->m32[1] << shift) | (src->m32[2] >> (32 - shift));
- src->m32[1] = (src->m32[2] << shift) | (src->m32[3] >> (32 - shift));
- src->m32[2] = (src->m32[3] << shift);
- src->m32[3] = 0;
- return;
- case 64 ... 95:
- shift -= 64;
- src->m32[0] = (src->m32[2] << shift) | (src->m32[3] >> (32 - shift));
- src->m32[1] = (src->m32[3] << shift);
- src->m32[2] = src->m32[3] = 0;
- return;
- case 96 ... 127:
- shift -= 96;
- src->m32[0] = (src->m32[3] << shift);
- src->m32[1] = src->m32[2] = src->m32[3] = 0;
- return;
- case -31 ... -1:
- shift = -shift;
- sticky = 0;
- if (src->m32[3] << (32 - shift))
- sticky = 1;
- src->m32[3] = (src->m32[3] >> shift) | (src->m32[2] << (32 - shift)) | sticky;
- src->m32[2] = (src->m32[2] >> shift) | (src->m32[1] << (32 - shift));
- src->m32[1] = (src->m32[1] >> shift) | (src->m32[0] << (32 - shift));
- src->m32[0] = (src->m32[0] >> shift);
- return;
- case -63 ... -32:
- shift = -shift - 32;
- sticky = 0;
- if ((src->m32[2] << (32 - shift)) || src->m32[3])
- sticky = 1;
- src->m32[3] = (src->m32[2] >> shift) | (src->m32[1] << (32 - shift)) | sticky;
- src->m32[2] = (src->m32[1] >> shift) | (src->m32[0] << (32 - shift));
- src->m32[1] = (src->m32[0] >> shift);
- src->m32[0] = 0;
- return;
- case -95 ... -64:
- shift = -shift - 64;
- sticky = 0;
- if ((src->m32[1] << (32 - shift)) || src->m32[2] || src->m32[3])
- sticky = 1;
- src->m32[3] = (src->m32[1] >> shift) | (src->m32[0] << (32 - shift)) | sticky;
- src->m32[2] = (src->m32[0] >> shift);
- src->m32[1] = src->m32[0] = 0;
- return;
- case -127 ... -96:
- shift = -shift - 96;
- sticky = 0;
- if ((src->m32[0] << (32 - shift)) || src->m32[1] || src->m32[2] || src->m32[3])
- sticky = 1;
- src->m32[3] = (src->m32[0] >> shift) | sticky;
- src->m32[2] = src->m32[1] = src->m32[0] = 0;
- return;
- }
-
- if (shift < 0 && (src->m32[0] || src->m32[1] || src->m32[2] || src->m32[3]))
- src->m32[3] = 1;
- else
- src->m32[3] = 0;
- src->m32[2] = 0;
- src->m32[1] = 0;
- src->m32[0] = 0;
-}
-#endif
-
static inline void fp_putmant128(struct fp_ext *dest, union fp_mant128 *src,
int shift)
{
@@ -637,183 +286,4 @@
}
}
-#if 0 /* old code... */
-static inline int fls(unsigned int a)
-{
- int r;
-
- asm volatile ("bfffo %1{#0,#32},%0"
- : "=d" (r) : "md" (a));
- return r;
-}
-
-/* fls = "find last set" (cf. ffs(3)) */
-static inline int fls128(const int128 a)
-{
- if (a[MSW128])
- return fls(a[MSW128]);
- if (a[NMSW128])
- return fls(a[NMSW128]) + 32;
- /* XXX: it probably never gets beyond this point in actual
- use, but that's indicative of a more general problem in the
- algorithm (i.e. as per the actual 68881 implementation, we
- really only need at most 67 bits of precision [plus
- overflow]) so I'm not going to fix it. */
- if (a[NLSW128])
- return fls(a[NLSW128]) + 64;
- if (a[LSW128])
- return fls(a[LSW128]) + 96;
- else
- return -1;
-}
-
-static inline int zerop128(const int128 a)
-{
- return !(a[LSW128] | a[NLSW128] | a[NMSW128] | a[MSW128]);
-}
-
-static inline int nonzerop128(const int128 a)
-{
- return (a[LSW128] | a[NLSW128] | a[NMSW128] | a[MSW128]);
-}
-
-/* Addition and subtraction */
-/* Do these in "pure" assembly, because "extended" asm is unmanageable
- here */
-static inline void add128(const int128 a, int128 b)
-{
- /* rotating carry flags */
- unsigned int carry[2];
-
- carry[0] = a[LSW128] > (0xffffffff - b[LSW128]);
- b[LSW128] += a[LSW128];
-
- carry[1] = a[NLSW128] > (0xffffffff - b[NLSW128] - carry[0]);
- b[NLSW128] = a[NLSW128] + b[NLSW128] + carry[0];
-
- carry[0] = a[NMSW128] > (0xffffffff - b[NMSW128] - carry[1]);
- b[NMSW128] = a[NMSW128] + b[NMSW128] + carry[1];
-
- b[MSW128] = a[MSW128] + b[MSW128] + carry[0];
-}
-
-/* Note: assembler semantics: "b -= a" */
-static inline void sub128(const int128 a, int128 b)
-{
- /* rotating borrow flags */
- unsigned int borrow[2];
-
- borrow[0] = b[LSW128] < a[LSW128];
- b[LSW128] -= a[LSW128];
-
- borrow[1] = b[NLSW128] < a[NLSW128] + borrow[0];
- b[NLSW128] = b[NLSW128] - a[NLSW128] - borrow[0];
-
- borrow[0] = b[NMSW128] < a[NMSW128] + borrow[1];
- b[NMSW128] = b[NMSW128] - a[NMSW128] - borrow[1];
-
- b[MSW128] = b[MSW128] - a[MSW128] - borrow[0];
-}
-
-/* Poor man's 64-bit expanding multiply */
-static inline void mul64(unsigned long long a, unsigned long long b, int128 c)
-{
- unsigned long long acc;
- int128 acc128;
-
- zero128(acc128);
- zero128(c);
-
- /* first the low words */
- if (LO_WORD(a) && LO_WORD(b)) {
- acc = (long long) LO_WORD(a) * LO_WORD(b);
- c[NLSW128] = HI_WORD(acc);
- c[LSW128] = LO_WORD(acc);
- }
- /* Next the high words */
- if (HI_WORD(a) && HI_WORD(b)) {
- acc = (long long) HI_WORD(a) * HI_WORD(b);
- c[MSW128] = HI_WORD(acc);
- c[NMSW128] = LO_WORD(acc);
- }
- /* The middle words */
- if (LO_WORD(a) && HI_WORD(b)) {
- acc = (long long) LO_WORD(a) * HI_WORD(b);
- acc128[NMSW128] = HI_WORD(acc);
- acc128[NLSW128] = LO_WORD(acc);
- add128(acc128, c);
- }
- /* The first and last words */
- if (HI_WORD(a) && LO_WORD(b)) {
- acc = (long long) HI_WORD(a) * LO_WORD(b);
- acc128[NMSW128] = HI_WORD(acc);
- acc128[NLSW128] = LO_WORD(acc);
- add128(acc128, c);
- }
-}
-
-/* Note: unsigned */
-static inline int cmp128(int128 a, int128 b)
-{
- if (a[MSW128] < b[MSW128])
- return -1;
- if (a[MSW128] > b[MSW128])
- return 1;
- if (a[NMSW128] < b[NMSW128])
- return -1;
- if (a[NMSW128] > b[NMSW128])
- return 1;
- if (a[NLSW128] < b[NLSW128])
- return -1;
- if (a[NLSW128] > b[NLSW128])
- return 1;
-
- return (signed) a[LSW128] - b[LSW128];
-}
-
-inline void div128(int128 a, int128 b, int128 c)
-{
- int128 mask;
-
- /* Algorithm:
-
- Shift the divisor until it's at least as big as the
- dividend, keeping track of the position to which we've
- shifted it, i.e. the power of 2 which we've multiplied it
- by.
-
- Then, for this power of 2 (the mask), and every one smaller
- than it, subtract the mask from the dividend and add it to
- the quotient until the dividend is smaller than the raised
- divisor. At this point, divide the dividend and the mask
- by 2 (i.e. shift one place to the right). Lather, rinse,
- and repeat, until there are no more powers of 2 left. */
-
- /* FIXME: needless to say, there's room for improvement here too. */
-
- /* Shift up */
- /* XXX: since it just has to be "at least as big", we can
- probably eliminate this horribly wasteful loop. I will
- have to prove this first, though */
- set128(0, 0, 0, 1, mask);
- while (cmp128(b, a) < 0 && !btsthi128(b)) {
- lslone128(b);
- lslone128(mask);
- }
-
- /* Shift down */
- zero128(c);
- do {
- if (cmp128(a, b) >= 0) {
- sub128(b, a);
- add128(mask, c);
- }
- lsrone128(mask);
- lsrone128(b);
- } while (nonzerop128(mask));
-
- /* The remainder is in a... */
-}
-#endif
-
#endif /* MULTI_ARITH_H */
diff --git a/arch/m68k/mm/init_mm.c b/arch/m68k/mm/init_mm.c
index 9113c2f..bbe5254 100644
--- a/arch/m68k/mm/init_mm.c
+++ b/arch/m68k/mm/init_mm.c
@@ -83,11 +83,6 @@
int initpages = 0;
int i;
-#ifdef CONFIG_ATARI
- if (MACH_IS_ATARI)
- atari_stram_mem_init_hook();
-#endif
-
/* this will put all memory onto the freelists */
totalram_pages = num_physpages = 0;
for_each_online_pgdat(pgdat) {
diff --git a/arch/parisc/Kconfig b/arch/parisc/Kconfig
index 65adc86..e077b0b 100644
--- a/arch/parisc/Kconfig
+++ b/arch/parisc/Kconfig
@@ -15,6 +15,7 @@
select HAVE_GENERIC_HARDIRQS
select GENERIC_IRQ_PROBE
select IRQ_PER_CPU
+ select ARCH_HAVE_NMI_SAFE_CMPXCHG
help
The PA-RISC microprocessor is designed by Hewlett-Packard and used
diff --git a/arch/parisc/include/asm/atomic.h b/arch/parisc/include/asm/atomic.h
index b1dc71f..4054b31 100644
--- a/arch/parisc/include/asm/atomic.h
+++ b/arch/parisc/include/asm/atomic.h
@@ -258,10 +258,10 @@
#define ATOMIC64_INIT(i) ((atomic64_t) { (i) })
-static __inline__ int
+static __inline__ s64
__atomic64_add_return(s64 i, atomic64_t *v)
{
- int ret;
+ s64 ret;
unsigned long flags;
_atomic_spin_lock_irqsave(v, flags);
diff --git a/arch/parisc/include/asm/futex.h b/arch/parisc/include/asm/futex.h
index 67a33cc..2388bdb 100644
--- a/arch/parisc/include/asm/futex.h
+++ b/arch/parisc/include/asm/futex.h
@@ -5,11 +5,14 @@
#include <linux/futex.h>
#include <linux/uaccess.h>
+#include <asm/atomic.h>
#include <asm/errno.h>
static inline int
futex_atomic_op_inuser (int encoded_op, u32 __user *uaddr)
{
+ unsigned long int flags;
+ u32 val;
int op = (encoded_op >> 28) & 7;
int cmp = (encoded_op >> 24) & 15;
int oparg = (encoded_op << 8) >> 20;
@@ -18,21 +21,58 @@
if (encoded_op & (FUTEX_OP_OPARG_SHIFT << 28))
oparg = 1 << oparg;
- if (! access_ok (VERIFY_WRITE, uaddr, sizeof(u32)))
+ if (!access_ok(VERIFY_WRITE, uaddr, sizeof(*uaddr)))
return -EFAULT;
pagefault_disable();
+ _atomic_spin_lock_irqsave(uaddr, flags);
+
switch (op) {
case FUTEX_OP_SET:
+ /* *(int *)UADDR2 = OPARG; */
+ ret = get_user(oldval, uaddr);
+ if (!ret)
+ ret = put_user(oparg, uaddr);
+ break;
case FUTEX_OP_ADD:
+ /* *(int *)UADDR2 += OPARG; */
+ ret = get_user(oldval, uaddr);
+ if (!ret) {
+ val = oldval + oparg;
+ ret = put_user(val, uaddr);
+ }
+ break;
case FUTEX_OP_OR:
+ /* *(int *)UADDR2 |= OPARG; */
+ ret = get_user(oldval, uaddr);
+ if (!ret) {
+ val = oldval | oparg;
+ ret = put_user(val, uaddr);
+ }
+ break;
case FUTEX_OP_ANDN:
+ /* *(int *)UADDR2 &= ~OPARG; */
+ ret = get_user(oldval, uaddr);
+ if (!ret) {
+ val = oldval & ~oparg;
+ ret = put_user(val, uaddr);
+ }
+ break;
case FUTEX_OP_XOR:
+ /* *(int *)UADDR2 ^= OPARG; */
+ ret = get_user(oldval, uaddr);
+ if (!ret) {
+ val = oldval ^ oparg;
+ ret = put_user(val, uaddr);
+ }
+ break;
default:
ret = -ENOSYS;
}
+ _atomic_spin_unlock_irqrestore(uaddr, flags);
+
pagefault_enable();
if (!ret) {
@@ -54,7 +94,9 @@
futex_atomic_cmpxchg_inatomic(u32 *uval, u32 __user *uaddr,
u32 oldval, u32 newval)
{
+ int ret;
u32 val;
+ unsigned long flags;
/* futex.c wants to do a cmpxchg_inatomic on kernel NULL, which is
* our gateway page, and causes no end of trouble...
@@ -65,12 +107,24 @@
if (!access_ok(VERIFY_WRITE, uaddr, sizeof(u32)))
return -EFAULT;
- if (get_user(val, uaddr))
- return -EFAULT;
- if (val == oldval && put_user(newval, uaddr))
- return -EFAULT;
+ /* HPPA has no cmpxchg in hardware and therefore the
+ * best we can do here is use an array of locks. The
+ * lock selected is based on a hash of the userspace
+ * address. This should scale to a couple of CPUs.
+ */
+
+ _atomic_spin_lock_irqsave(uaddr, flags);
+
+ ret = get_user(val, uaddr);
+
+ if (!ret && val == oldval)
+ ret = put_user(newval, uaddr);
+
*uval = val;
- return 0;
+
+ _atomic_spin_unlock_irqrestore(uaddr, flags);
+
+ return ret;
}
#endif /*__KERNEL__*/
diff --git a/arch/parisc/include/asm/unistd.h b/arch/parisc/include/asm/unistd.h
index 3392de3..d61de64 100644
--- a/arch/parisc/include/asm/unistd.h
+++ b/arch/parisc/include/asm/unistd.h
@@ -821,8 +821,9 @@
#define __NR_open_by_handle_at (__NR_Linux + 326)
#define __NR_syncfs (__NR_Linux + 327)
#define __NR_setns (__NR_Linux + 328)
+#define __NR_sendmmsg (__NR_Linux + 329)
-#define __NR_Linux_syscalls (__NR_setns + 1)
+#define __NR_Linux_syscalls (__NR_sendmmsg + 1)
#define __IGNORE_select /* newselect */
diff --git a/arch/parisc/kernel/syscall_table.S b/arch/parisc/kernel/syscall_table.S
index 34a4f5a..e66366f 100644
--- a/arch/parisc/kernel/syscall_table.S
+++ b/arch/parisc/kernel/syscall_table.S
@@ -427,6 +427,7 @@
ENTRY_COMP(open_by_handle_at)
ENTRY_SAME(syncfs)
ENTRY_SAME(setns)
+ ENTRY_COMP(sendmmsg)
/* Nothing yet */
diff --git a/arch/powerpc/Kconfig b/arch/powerpc/Kconfig
index 374c475..6926b61 100644
--- a/arch/powerpc/Kconfig
+++ b/arch/powerpc/Kconfig
@@ -136,6 +136,7 @@
select HAVE_SYSCALL_TRACEPOINTS
select HAVE_BPF_JIT if (PPC64 && NET)
select HAVE_ARCH_JUMP_LABEL
+ select ARCH_HAVE_NMI_SAFE_CMPXCHG
config EARLY_PRINTK
bool
diff --git a/arch/powerpc/include/asm/jump_label.h b/arch/powerpc/include/asm/jump_label.h
index 1f780b9..938986e 100644
--- a/arch/powerpc/include/asm/jump_label.h
+++ b/arch/powerpc/include/asm/jump_label.h
@@ -22,7 +22,6 @@
asm goto("1:\n\t"
"nop\n\t"
".pushsection __jump_table, \"aw\"\n\t"
- ".align 4\n\t"
JUMP_ENTRY_TYPE "1b, %l[l_yes], %c0\n\t"
".popsection \n\t"
: : "i" (key) : : l_yes);
@@ -41,7 +40,6 @@
jump_label_t code;
jump_label_t target;
jump_label_t key;
- jump_label_t pad;
};
#endif /* _ASM_POWERPC_JUMP_LABEL_H */
diff --git a/arch/powerpc/include/asm/kdump.h b/arch/powerpc/include/asm/kdump.h
index 6857af5..bffd062 100644
--- a/arch/powerpc/include/asm/kdump.h
+++ b/arch/powerpc/include/asm/kdump.h
@@ -3,17 +3,7 @@
#include <asm/page.h>
-/*
- * If CONFIG_RELOCATABLE is enabled we can place the kdump kernel anywhere.
- * To keep enough space in the RMO for the first stage kernel on 64bit, we
- * place it at 64MB. If CONFIG_RELOCATABLE is not enabled we must place
- * the second stage at 32MB.
- */
-#if defined(CONFIG_RELOCATABLE) && defined(CONFIG_PPC64)
-#define KDUMP_KERNELBASE 0x4000000
-#else
#define KDUMP_KERNELBASE 0x2000000
-#endif
/* How many bytes to reserve at zero for kdump. The reserve limit should
* be greater or equal to the trampoline's end address.
diff --git a/arch/powerpc/include/asm/reg.h b/arch/powerpc/include/asm/reg.h
index e8aaf6f..559da19 100644
--- a/arch/powerpc/include/asm/reg.h
+++ b/arch/powerpc/include/asm/reg.h
@@ -1003,7 +1003,6 @@
#define PV_970 0x0039
#define PV_POWER5 0x003A
#define PV_POWER5p 0x003B
-#define PV_POWER7 0x003F
#define PV_970FX 0x003C
#define PV_POWER6 0x003E
#define PV_POWER7 0x003F
@@ -1024,13 +1023,16 @@
#define mtmsrd(v) __mtmsrd((v), 0)
#define mtmsr(v) mtmsrd(v)
#else
-#define mtmsr(v) asm volatile("mtmsr %0" : : "r" (v) : "memory")
+#define mtmsr(v) asm volatile("mtmsr %0" : \
+ : "r" ((unsigned long)(v)) \
+ : "memory")
#endif
#define mfspr(rn) ({unsigned long rval; \
asm volatile("mfspr %0," __stringify(rn) \
: "=r" (rval)); rval;})
-#define mtspr(rn, v) asm volatile("mtspr " __stringify(rn) ",%0" : : "r" (v)\
+#define mtspr(rn, v) asm volatile("mtspr " __stringify(rn) ",%0" : \
+ : "r" ((unsigned long)(v)) \
: "memory")
#ifdef __powerpc64__
diff --git a/arch/powerpc/kernel/cputable.c b/arch/powerpc/kernel/cputable.c
index 9fb9332..fa44ff5 100644
--- a/arch/powerpc/kernel/cputable.c
+++ b/arch/powerpc/kernel/cputable.c
@@ -2051,7 +2051,8 @@
static struct cpu_spec the_cpu_spec;
-static void __init setup_cpu_spec(unsigned long offset, struct cpu_spec *s)
+static struct cpu_spec * __init setup_cpu_spec(unsigned long offset,
+ struct cpu_spec *s)
{
struct cpu_spec *t = &the_cpu_spec;
struct cpu_spec old;
@@ -2114,6 +2115,8 @@
t->cpu_setup(offset, t);
}
#endif /* CONFIG_PPC64 || CONFIG_BOOKE */
+
+ return t;
}
struct cpu_spec * __init identify_cpu(unsigned long offset, unsigned int pvr)
@@ -2124,10 +2127,8 @@
s = PTRRELOC(s);
for (i = 0; i < ARRAY_SIZE(cpu_specs); i++,s++) {
- if ((pvr & s->pvr_mask) == s->pvr_value) {
- setup_cpu_spec(offset, s);
- return s;
- }
+ if ((pvr & s->pvr_mask) == s->pvr_value)
+ return setup_cpu_spec(offset, s);
}
BUG();
diff --git a/arch/powerpc/kernel/iomap.c b/arch/powerpc/kernel/iomap.c
index 1577434..b25f632 100644
--- a/arch/powerpc/kernel/iomap.c
+++ b/arch/powerpc/kernel/iomap.c
@@ -117,6 +117,7 @@
EXPORT_SYMBOL(ioport_map);
EXPORT_SYMBOL(ioport_unmap);
+#ifdef CONFIG_PCI
void __iomem *pci_iomap(struct pci_dev *dev, int bar, unsigned long max)
{
resource_size_t start = pci_resource_start(dev, bar);
@@ -146,3 +147,4 @@
EXPORT_SYMBOL(pci_iomap);
EXPORT_SYMBOL(pci_iounmap);
+#endif /* CONFIG_PCI */
diff --git a/arch/powerpc/kernel/machine_kexec.c b/arch/powerpc/kernel/machine_kexec.c
index 6658a15..9ce1672 100644
--- a/arch/powerpc/kernel/machine_kexec.c
+++ b/arch/powerpc/kernel/machine_kexec.c
@@ -136,12 +136,16 @@
crashk_res.start = KDUMP_KERNELBASE;
#else
if (!crashk_res.start) {
+#ifdef CONFIG_PPC64
/*
- * unspecified address, choose a region of specified size
- * can overlap with initrd (ignoring corruption when retained)
- * ppc64 requires kernel and some stacks to be in first segemnt
+ * On 64bit we split the RMO in half but cap it at half of
+ * a small SLB (128MB) since the crash kernel needs to place
+ * itself and some stacks to be in the first segment.
*/
+ crashk_res.start = min(0x80000000ULL, (ppc64_rma_size / 2));
+#else
crashk_res.start = KDUMP_KERNELBASE;
+#endif
}
crash_base = PAGE_ALIGN(crashk_res.start);
diff --git a/arch/powerpc/kernel/perf_callchain.c b/arch/powerpc/kernel/perf_callchain.c
index d05ae42..564c1d8 100644
--- a/arch/powerpc/kernel/perf_callchain.c
+++ b/arch/powerpc/kernel/perf_callchain.c
@@ -154,8 +154,12 @@
((unsigned long)ptr & 7))
return -EFAULT;
- if (!__get_user_inatomic(*ret, ptr))
+ pagefault_disable();
+ if (!__get_user_inatomic(*ret, ptr)) {
+ pagefault_enable();
return 0;
+ }
+ pagefault_enable();
return read_user_stack_slow(ptr, ret, 8);
}
@@ -166,8 +170,12 @@
((unsigned long)ptr & 3))
return -EFAULT;
- if (!__get_user_inatomic(*ret, ptr))
+ pagefault_disable();
+ if (!__get_user_inatomic(*ret, ptr)) {
+ pagefault_enable();
return 0;
+ }
+ pagefault_enable();
return read_user_stack_slow(ptr, ret, 4);
}
@@ -294,11 +302,17 @@
*/
static int read_user_stack_32(unsigned int __user *ptr, unsigned int *ret)
{
+ int rc;
+
if ((unsigned long)ptr > TASK_SIZE - sizeof(unsigned int) ||
((unsigned long)ptr & 3))
return -EFAULT;
- return __get_user_inatomic(*ret, ptr);
+ pagefault_disable();
+ rc = __get_user_inatomic(*ret, ptr);
+ pagefault_enable();
+
+ return rc;
}
static inline void perf_callchain_user_64(struct perf_callchain_entry *entry,
diff --git a/arch/powerpc/kernel/prom_init.c b/arch/powerpc/kernel/prom_init.c
index c016033..a909f4e 100644
--- a/arch/powerpc/kernel/prom_init.c
+++ b/arch/powerpc/kernel/prom_init.c
@@ -1020,7 +1020,7 @@
}
if (addr == 0)
return 0;
- RELOC(alloc_bottom) = addr;
+ RELOC(alloc_bottom) = addr + size;
prom_debug(" -> %x\n", addr);
prom_debug(" alloc_bottom : %x\n", RELOC(alloc_bottom));
@@ -1830,11 +1830,13 @@
if (room > DEVTREE_CHUNK_SIZE)
room = DEVTREE_CHUNK_SIZE;
if (room < PAGE_SIZE)
- prom_panic("No memory for flatten_device_tree (no room)");
+ prom_panic("No memory for flatten_device_tree "
+ "(no room)\n");
chunk = alloc_up(room, 0);
if (chunk == 0)
- prom_panic("No memory for flatten_device_tree (claim failed)");
- *mem_end = RELOC(alloc_top);
+ prom_panic("No memory for flatten_device_tree "
+ "(claim failed)\n");
+ *mem_end = chunk + room;
}
ret = (void *)*mem_start;
@@ -2042,7 +2044,7 @@
/*
* Check how much room we have between alloc top & bottom (+/- a
- * few pages), crop to 4Mb, as this is our "chuck" size
+ * few pages), crop to 1MB, as this is our "chunk" size
*/
room = RELOC(alloc_top) - RELOC(alloc_bottom) - 0x4000;
if (room > DEVTREE_CHUNK_SIZE)
@@ -2053,7 +2055,7 @@
mem_start = (unsigned long)alloc_up(room, PAGE_SIZE);
if (mem_start == 0)
prom_panic("Can't allocate initial device-tree chunk\n");
- mem_end = RELOC(alloc_top);
+ mem_end = mem_start + room;
/* Get root of tree */
root = call_prom("peer", 1, 1, (phandle)0);
diff --git a/arch/powerpc/kvm/book3s_hv_rmhandlers.S b/arch/powerpc/kvm/book3s_hv_rmhandlers.S
index 6dd3358..de29501 100644
--- a/arch/powerpc/kvm/book3s_hv_rmhandlers.S
+++ b/arch/powerpc/kvm/book3s_hv_rmhandlers.S
@@ -1251,7 +1251,7 @@
reg = 0
.rept 32
li r6,reg*16+VCPU_VSRS
- stxvd2x reg,r6,r3
+ STXVD2X(reg,r6,r3)
reg = reg + 1
.endr
FTR_SECTION_ELSE
@@ -1313,7 +1313,7 @@
reg = 0
.rept 32
li r7,reg*16+VCPU_VSRS
- lxvd2x reg,r7,r4
+ LXVD2X(reg,r7,r4)
reg = reg + 1
.endr
FTR_SECTION_ELSE
diff --git a/arch/powerpc/platforms/Kconfig b/arch/powerpc/platforms/Kconfig
index d0af7fb..b9ba861 100644
--- a/arch/powerpc/platforms/Kconfig
+++ b/arch/powerpc/platforms/Kconfig
@@ -24,7 +24,7 @@
config KVM_GUEST
bool "KVM Guest support"
- default y
+ default n
---help---
This option enables various optimizations for running under the KVM
hypervisor. Overhead for the kernel when not running inside KVM should
diff --git a/arch/powerpc/platforms/pseries/dtl.c b/arch/powerpc/platforms/pseries/dtl.c
index e919007..0e86563 100644
--- a/arch/powerpc/platforms/pseries/dtl.c
+++ b/arch/powerpc/platforms/pseries/dtl.c
@@ -181,7 +181,7 @@
lppaca_of(dtl->cpu).dtl_enable_mask = 0x0;
- unregister_dtl(hwcpu, __pa(dtl->buf));
+ unregister_dtl(hwcpu);
}
static u64 dtl_current_index(struct dtl *dtl)
diff --git a/arch/powerpc/platforms/pseries/hotplug-cpu.c b/arch/powerpc/platforms/pseries/hotplug-cpu.c
index bc02885..83a3ca2 100644
--- a/arch/powerpc/platforms/pseries/hotplug-cpu.c
+++ b/arch/powerpc/platforms/pseries/hotplug-cpu.c
@@ -135,7 +135,7 @@
get_lppaca()->idle = 0;
if (get_preferred_offline_state(cpu) == CPU_STATE_ONLINE) {
- unregister_slb_shadow(hwcpu, __pa(get_slb_shadow()));
+ unregister_slb_shadow(hwcpu);
/*
* Call to start_secondary_resume() will not return.
@@ -150,7 +150,7 @@
WARN_ON(get_preferred_offline_state(cpu) != CPU_STATE_OFFLINE);
set_cpu_current_state(cpu, CPU_STATE_OFFLINE);
- unregister_slb_shadow(hwcpu, __pa(get_slb_shadow()));
+ unregister_slb_shadow(hwcpu);
rtas_stop_self();
/* Should never get here... */
diff --git a/arch/powerpc/platforms/pseries/io_event_irq.c b/arch/powerpc/platforms/pseries/io_event_irq.c
index c829e60..2c4dd1f 100644
--- a/arch/powerpc/platforms/pseries/io_event_irq.c
+++ b/arch/powerpc/platforms/pseries/io_event_irq.c
@@ -212,17 +212,15 @@
struct device_node *np;
ioei_check_exception_token = rtas_token("check-exception");
- if (ioei_check_exception_token == RTAS_UNKNOWN_SERVICE) {
- pr_warning("IO Event IRQ not supported on this system !\n");
+ if (ioei_check_exception_token == RTAS_UNKNOWN_SERVICE)
return -ENODEV;
- }
+
np = of_find_node_by_path("/event-sources/ibm,io-events");
if (np) {
request_event_sources_irqs(np, ioei_interrupt, "IO_EVENT");
+ pr_info("IBM I/O event interrupts enabled\n");
of_node_put(np);
} else {
- pr_err("io_event_irq: No ibm,io-events on system! "
- "IO Event interrupt disabled.\n");
return -ENODEV;
}
return 0;
diff --git a/arch/powerpc/platforms/pseries/kexec.c b/arch/powerpc/platforms/pseries/kexec.c
index 54cf3a4..7d94bdc 100644
--- a/arch/powerpc/platforms/pseries/kexec.c
+++ b/arch/powerpc/platforms/pseries/kexec.c
@@ -25,20 +25,30 @@
{
/* Don't risk a hypervisor call if we're crashing */
if (firmware_has_feature(FW_FEATURE_SPLPAR) && !crash_shutdown) {
- unsigned long addr;
+ int ret;
+ int cpu = smp_processor_id();
+ int hwcpu = hard_smp_processor_id();
- addr = __pa(get_slb_shadow());
- if (unregister_slb_shadow(hard_smp_processor_id(), addr))
- printk("SLB shadow buffer deregistration of "
- "cpu %u (hw_cpu_id %d) failed\n",
- smp_processor_id(),
- hard_smp_processor_id());
+ if (get_lppaca()->dtl_enable_mask) {
+ ret = unregister_dtl(hwcpu);
+ if (ret) {
+ pr_err("WARNING: DTL deregistration for cpu "
+ "%d (hw %d) failed with %d\n",
+ cpu, hwcpu, ret);
+ }
+ }
- addr = __pa(get_lppaca());
- if (unregister_vpa(hard_smp_processor_id(), addr)) {
- printk("VPA deregistration of cpu %u (hw_cpu_id %d) "
- "failed\n", smp_processor_id(),
- hard_smp_processor_id());
+ ret = unregister_slb_shadow(hwcpu);
+ if (ret) {
+ pr_err("WARNING: SLB shadow buffer deregistration "
+ "for cpu %d (hw %d) failed with %d\n",
+ cpu, hwcpu, ret);
+ }
+
+ ret = unregister_vpa(hwcpu);
+ if (ret) {
+ pr_err("WARNING: VPA deregistration for cpu %d "
+ "(hw %d) failed with %d\n", cpu, hwcpu, ret);
}
}
}
diff --git a/arch/powerpc/platforms/pseries/lpar.c b/arch/powerpc/platforms/pseries/lpar.c
index f7205d3..c9a29da 100644
--- a/arch/powerpc/platforms/pseries/lpar.c
+++ b/arch/powerpc/platforms/pseries/lpar.c
@@ -67,9 +67,8 @@
ret = register_vpa(hwcpu, addr);
if (ret) {
- printk(KERN_ERR "WARNING: vpa_init: VPA registration for "
- "cpu %d (hw %d) of area %lx returns %ld\n",
- cpu, hwcpu, addr, ret);
+ pr_err("WARNING: VPA registration for cpu %d (hw %d) of area "
+ "%lx failed with %ld\n", cpu, hwcpu, addr, ret);
return;
}
/*
@@ -80,10 +79,9 @@
if (firmware_has_feature(FW_FEATURE_SPLPAR)) {
ret = register_slb_shadow(hwcpu, addr);
if (ret)
- printk(KERN_ERR
- "WARNING: vpa_init: SLB shadow buffer "
- "registration for cpu %d (hw %d) of area %lx "
- "returns %ld\n", cpu, hwcpu, addr, ret);
+ pr_err("WARNING: SLB shadow buffer registration for "
+ "cpu %d (hw %d) of area %lx failed with %ld\n",
+ cpu, hwcpu, addr, ret);
}
/*
@@ -100,8 +98,9 @@
dtl->enqueue_to_dispatch_time = DISPATCH_LOG_BYTES;
ret = register_dtl(hwcpu, __pa(dtl));
if (ret)
- pr_warn("DTL registration failed for cpu %d (%ld)\n",
- cpu, ret);
+ pr_err("WARNING: DTL registration of cpu %d (hw %d) "
+ "failed with %ld\n", smp_processor_id(),
+ hwcpu, ret);
lppaca_of(cpu).dtl_enable_mask = 2;
}
}
@@ -204,7 +203,7 @@
unsigned long ptel;
} ptes[4];
long lpar_rc;
- int i, j;
+ unsigned long i, j;
/* Read in batches of 4,
* invalidate only valid entries not in the VRMA
diff --git a/arch/powerpc/platforms/pseries/plpar_wrappers.h b/arch/powerpc/platforms/pseries/plpar_wrappers.h
index 4bf2120..41c24c1 100644
--- a/arch/powerpc/platforms/pseries/plpar_wrappers.h
+++ b/arch/powerpc/platforms/pseries/plpar_wrappers.h
@@ -53,9 +53,9 @@
return plpar_hcall_norets(H_REGISTER_VPA, flags, cpu, vpa);
}
-static inline long unregister_vpa(unsigned long cpu, unsigned long vpa)
+static inline long unregister_vpa(unsigned long cpu)
{
- return vpa_call(0x5, cpu, vpa);
+ return vpa_call(0x5, cpu, 0);
}
static inline long register_vpa(unsigned long cpu, unsigned long vpa)
@@ -63,9 +63,9 @@
return vpa_call(0x1, cpu, vpa);
}
-static inline long unregister_slb_shadow(unsigned long cpu, unsigned long vpa)
+static inline long unregister_slb_shadow(unsigned long cpu)
{
- return vpa_call(0x7, cpu, vpa);
+ return vpa_call(0x7, cpu, 0);
}
static inline long register_slb_shadow(unsigned long cpu, unsigned long vpa)
@@ -73,9 +73,9 @@
return vpa_call(0x3, cpu, vpa);
}
-static inline long unregister_dtl(unsigned long cpu, unsigned long vpa)
+static inline long unregister_dtl(unsigned long cpu)
{
- return vpa_call(0x6, cpu, vpa);
+ return vpa_call(0x6, cpu, 0);
}
static inline long register_dtl(unsigned long cpu, unsigned long vpa)
diff --git a/arch/powerpc/platforms/pseries/setup.c b/arch/powerpc/platforms/pseries/setup.c
index d00e529..0969fd9 100644
--- a/arch/powerpc/platforms/pseries/setup.c
+++ b/arch/powerpc/platforms/pseries/setup.c
@@ -324,8 +324,9 @@
dtl->enqueue_to_dispatch_time = DISPATCH_LOG_BYTES;
ret = register_dtl(hard_smp_processor_id(), __pa(dtl));
if (ret)
- pr_warn("DTL registration failed for boot cpu %d (%d)\n",
- smp_processor_id(), ret);
+ pr_err("WARNING: DTL registration of cpu %d (hw %d) failed "
+ "with %d\n", smp_processor_id(),
+ hard_smp_processor_id(), ret);
get_paca()->lppaca_ptr->dtl_enable_mask = 2;
return 0;
diff --git a/arch/powerpc/sysdev/ppc4xx_pci.c b/arch/powerpc/sysdev/ppc4xx_pci.c
index a59ba96..dbfe96b 100644
--- a/arch/powerpc/sysdev/ppc4xx_pci.c
+++ b/arch/powerpc/sysdev/ppc4xx_pci.c
@@ -655,8 +655,6 @@
static struct ppc4xx_pciex_hwops *ppc4xx_pciex_hwops;
-#ifdef CONFIG_44x
-
static int __init ppc4xx_pciex_wait_on_sdr(struct ppc4xx_pciex_port *port,
unsigned int sdr_offset,
unsigned int mask,
@@ -688,6 +686,7 @@
return 0;
}
+
static void __init ppc4xx_pciex_check_link_sdr(struct ppc4xx_pciex_port *port)
{
printk(KERN_INFO "PCIE%d: Checking link...\n", port->index);
@@ -718,6 +717,8 @@
printk(KERN_INFO "PCIE%d: No device detected.\n", port->index);
}
+#ifdef CONFIG_44x
+
/* Check various reset bits of the 440SPe PCIe core */
static int __init ppc440spe_pciex_check_reset(struct device_node *np)
{
diff --git a/arch/s390/Kconfig b/arch/s390/Kconfig
index c03fef7..ed5cb5a 100644
--- a/arch/s390/Kconfig
+++ b/arch/s390/Kconfig
@@ -81,6 +81,7 @@
select INIT_ALL_POSSIBLE
select HAVE_IRQ_WORK
select HAVE_PERF_EVENTS
+ select ARCH_HAVE_NMI_SAFE_CMPXCHG
select HAVE_KERNEL_GZIP
select HAVE_KERNEL_BZIP2
select HAVE_KERNEL_LZMA
@@ -273,11 +274,11 @@
on older machines.
config MARCH_Z196
- bool "IBM zEnterprise 196"
+ bool "IBM zEnterprise 114 and 196"
help
- Select this to enable optimizations for IBM zEnterprise 196
- (2817 series). The kernel will be slightly faster but will not work
- on older machines.
+ Select this to enable optimizations for IBM zEnterprise 114 and 196
+ (2818 and 2817 series). The kernel will be slightly faster but will
+ not work on older machines.
endchoice
diff --git a/arch/s390/include/asm/ipl.h b/arch/s390/include/asm/ipl.h
index 5e95d95..97cc440 100644
--- a/arch/s390/include/asm/ipl.h
+++ b/arch/s390/include/asm/ipl.h
@@ -167,5 +167,6 @@
};
extern int diag308(unsigned long subcode, void *addr);
+extern void diag308_reset(void);
#endif /* _ASM_S390_IPL_H */
diff --git a/arch/s390/include/asm/lowcore.h b/arch/s390/include/asm/lowcore.h
index f26280d..e85c911 100644
--- a/arch/s390/include/asm/lowcore.h
+++ b/arch/s390/include/asm/lowcore.h
@@ -18,6 +18,7 @@
void pgm_check_handler(void);
void mcck_int_handler(void);
void io_int_handler(void);
+void psw_restart_int_handler(void);
#ifdef CONFIG_32BIT
@@ -150,7 +151,10 @@
*/
__u32 ipib; /* 0x0e00 */
__u32 ipib_checksum; /* 0x0e04 */
- __u8 pad_0x0e08[0x0f00-0x0e08]; /* 0x0e08 */
+
+ /* 64 bit save area */
+ __u64 save_area_64; /* 0x0e08 */
+ __u8 pad_0x0e10[0x0f00-0x0e10]; /* 0x0e10 */
/* Extended facility list */
__u64 stfle_fac_list[32]; /* 0x0f00 */
@@ -286,7 +290,10 @@
*/
__u64 ipib; /* 0x0e00 */
__u32 ipib_checksum; /* 0x0e08 */
- __u8 pad_0x0e0c[0x0f00-0x0e0c]; /* 0x0e0c */
+
+ /* 64 bit save area */
+ __u64 save_area_64; /* 0x0e0c */
+ __u8 pad_0x0e14[0x0f00-0x0e14]; /* 0x0e14 */
/* Extended facility list */
__u64 stfle_fac_list[32]; /* 0x0f00 */
diff --git a/arch/s390/include/asm/processor.h b/arch/s390/include/asm/processor.h
index 55dfcc8..a4b6229 100644
--- a/arch/s390/include/asm/processor.h
+++ b/arch/s390/include/asm/processor.h
@@ -119,14 +119,12 @@
* Do necessary setup to start up a new thread.
*/
#define start_thread(regs, new_psw, new_stackp) do { \
- set_fs(USER_DS); \
regs->psw.mask = psw_user_bits; \
regs->psw.addr = new_psw | PSW_ADDR_AMODE; \
regs->gprs[15] = new_stackp; \
} while (0)
#define start_thread31(regs, new_psw, new_stackp) do { \
- set_fs(USER_DS); \
regs->psw.mask = psw_user32_bits; \
regs->psw.addr = new_psw | PSW_ADDR_AMODE; \
regs->gprs[15] = new_stackp; \
diff --git a/arch/s390/include/asm/system.h b/arch/s390/include/asm/system.h
index d382629..6582f69 100644
--- a/arch/s390/include/asm/system.h
+++ b/arch/s390/include/asm/system.h
@@ -113,6 +113,7 @@
extern void cmma_init(void);
extern int memcpy_real(void *, void *, size_t);
+extern void copy_to_absolute_zero(void *dest, void *src, size_t count);
#define finish_arch_switch(prev) do { \
set_fs(current->thread.mm_segment); \
diff --git a/arch/s390/kernel/asm-offsets.c b/arch/s390/kernel/asm-offsets.c
index 05d8f38..532fd43 100644
--- a/arch/s390/kernel/asm-offsets.c
+++ b/arch/s390/kernel/asm-offsets.c
@@ -27,12 +27,9 @@
BLANK();
DEFINE(__TASK_pid, offsetof(struct task_struct, pid));
BLANK();
- DEFINE(__THREAD_per_cause,
- offsetof(struct task_struct, thread.per_event.cause));
- DEFINE(__THREAD_per_address,
- offsetof(struct task_struct, thread.per_event.address));
- DEFINE(__THREAD_per_paid,
- offsetof(struct task_struct, thread.per_event.paid));
+ DEFINE(__THREAD_per_cause, offsetof(struct task_struct, thread.per_event.cause));
+ DEFINE(__THREAD_per_address, offsetof(struct task_struct, thread.per_event.address));
+ DEFINE(__THREAD_per_paid, offsetof(struct task_struct, thread.per_event.paid));
BLANK();
DEFINE(__TI_task, offsetof(struct thread_info, task));
DEFINE(__TI_domain, offsetof(struct thread_info, exec_domain));
@@ -142,6 +139,7 @@
DEFINE(__LC_FPREGS_SAVE_AREA, offsetof(struct _lowcore, floating_pt_save_area));
DEFINE(__LC_GPREGS_SAVE_AREA, offsetof(struct _lowcore, gpregs_save_area));
DEFINE(__LC_CREGS_SAVE_AREA, offsetof(struct _lowcore, cregs_save_area));
+ DEFINE(__LC_SAVE_AREA_64, offsetof(struct _lowcore, save_area_64));
#ifdef CONFIG_32BIT
DEFINE(SAVE_AREA_BASE, offsetof(struct _lowcore, extended_save_area_addr));
#else /* CONFIG_32BIT */
diff --git a/arch/s390/kernel/base.S b/arch/s390/kernel/base.S
index 209938c..2554356 100644
--- a/arch/s390/kernel/base.S
+++ b/arch/s390/kernel/base.S
@@ -76,6 +76,42 @@
.quad 0
.previous
+#
+# Calls diag 308 subcode 1 and continues execution
+#
+# The following conditions must be ensured before calling this function:
+# * Prefix register = 0
+# * Lowcore protection is disabled
+#
+ENTRY(diag308_reset)
+ larl %r4,.Lctlregs # Save control registers
+ stctg %c0,%c15,0(%r4)
+ larl %r4,.Lrestart_psw # Setup restart PSW at absolute 0
+ lghi %r3,0
+ lg %r4,0(%r4) # Save PSW
+ sturg %r4,%r3 # Use sturg, because of large pages
+ lghi %r1,1
+ diag %r1,%r1,0x308
+.Lrestart_part2:
+ lhi %r0,0 # Load r0 with zero
+ lhi %r1,2 # Use mode 2 = ESAME (dump)
+ sigp %r1,%r0,0x12 # Switch to ESAME mode
+ sam64 # Switch to 64 bit addressing mode
+ larl %r4,.Lctlregs # Restore control registers
+ lctlg %c0,%c15,0(%r4)
+ br %r14
+.align 16
+.Lrestart_psw:
+ .long 0x00080000,0x80000000 + .Lrestart_part2
+
+ .section .bss
+.align 8
+.Lctlregs:
+ .rept 16
+ .quad 0
+ .endr
+ .previous
+
#else /* CONFIG_64BIT */
ENTRY(s390_base_mcck_handler)
diff --git a/arch/s390/kernel/compat_signal.c b/arch/s390/kernel/compat_signal.c
index eee9998..a9a285b 100644
--- a/arch/s390/kernel/compat_signal.c
+++ b/arch/s390/kernel/compat_signal.c
@@ -380,20 +380,13 @@
goto badframe;
if (__copy_from_user(&set.sig, &frame->sc.oldmask, _SIGMASK_COPY_SIZE32))
goto badframe;
-
sigdelsetmask(&set, ~_BLOCKABLE);
- spin_lock_irq(¤t->sighand->siglock);
- current->blocked = set;
- recalc_sigpending();
- spin_unlock_irq(¤t->sighand->siglock);
-
+ set_current_blocked(&set);
if (restore_sigregs32(regs, &frame->sregs))
goto badframe;
if (restore_sigregs_gprs_high(regs, frame->gprs_high))
goto badframe;
-
return regs->gprs[2];
-
badframe:
force_sig(SIGSEGV, current);
return 0;
@@ -413,31 +406,22 @@
goto badframe;
if (__copy_from_user(&set, &frame->uc.uc_sigmask, sizeof(set)))
goto badframe;
-
sigdelsetmask(&set, ~_BLOCKABLE);
- spin_lock_irq(¤t->sighand->siglock);
- current->blocked = set;
- recalc_sigpending();
- spin_unlock_irq(¤t->sighand->siglock);
-
+ set_current_blocked(&set);
if (restore_sigregs32(regs, &frame->uc.uc_mcontext))
goto badframe;
if (restore_sigregs_gprs_high(regs, frame->gprs_high))
goto badframe;
-
err = __get_user(ss_sp, &frame->uc.uc_stack.ss_sp);
st.ss_sp = compat_ptr(ss_sp);
err |= __get_user(st.ss_size, &frame->uc.uc_stack.ss_size);
err |= __get_user(st.ss_flags, &frame->uc.uc_stack.ss_flags);
if (err)
goto badframe;
-
set_fs (KERNEL_DS);
do_sigaltstack((stack_t __force __user *)&st, NULL, regs->gprs[15]);
set_fs (old_fs);
-
return regs->gprs[2];
-
badframe:
force_sig(SIGSEGV, current);
return 0;
@@ -605,10 +589,10 @@
* OK, we're invoking a handler
*/
-int
-handle_signal32(unsigned long sig, struct k_sigaction *ka,
- siginfo_t *info, sigset_t *oldset, struct pt_regs * regs)
+int handle_signal32(unsigned long sig, struct k_sigaction *ka,
+ siginfo_t *info, sigset_t *oldset, struct pt_regs *regs)
{
+ sigset_t blocked;
int ret;
/* Set up the stack frame */
@@ -616,15 +600,12 @@
ret = setup_rt_frame32(sig, ka, info, oldset, regs);
else
ret = setup_frame32(sig, ka, oldset, regs);
-
- if (ret == 0) {
- spin_lock_irq(¤t->sighand->siglock);
- sigorsets(¤t->blocked,¤t->blocked,&ka->sa.sa_mask);
- if (!(ka->sa.sa_flags & SA_NODEFER))
- sigaddset(¤t->blocked,sig);
- recalc_sigpending();
- spin_unlock_irq(¤t->sighand->siglock);
- }
- return ret;
+ if (ret)
+ return ret;
+ sigorsets(&blocked, ¤t->blocked, &ka->sa.sa_mask);
+ if (!(ka->sa.sa_flags & SA_NODEFER))
+ sigaddset(&blocked, sig);
+ set_current_blocked(&blocked);
+ return 0;
}
diff --git a/arch/s390/kernel/entry.S b/arch/s390/kernel/entry.S
index 3eab7cf..02ec8fe 100644
--- a/arch/s390/kernel/entry.S
+++ b/arch/s390/kernel/entry.S
@@ -849,6 +849,34 @@
restart_go:
#endif
+#
+# PSW restart interrupt handler
+#
+ENTRY(psw_restart_int_handler)
+ st %r15,__LC_SAVE_AREA_64(%r0) # save r15
+ basr %r15,0
+0: l %r15,.Lrestart_stack-0b(%r15) # load restart stack
+ l %r15,0(%r15)
+ ahi %r15,-SP_SIZE # make room for pt_regs
+ stm %r0,%r14,SP_R0(%r15) # store gprs %r0-%r14 to stack
+ mvc SP_R15(4,%r15),__LC_SAVE_AREA_64(%r0)# store saved %r15 to stack
+ mvc SP_PSW(8,%r15),__LC_RST_OLD_PSW(%r0) # store restart old psw
+ xc __SF_BACKCHAIN(4,%r15),__SF_BACKCHAIN(%r15) # set backchain to 0
+ basr %r14,0
+1: l %r14,.Ldo_restart-1b(%r14)
+ basr %r14,%r14
+
+ basr %r14,0 # load disabled wait PSW if
+2: lpsw restart_psw_crash-2b(%r14) # do_restart returns
+ .align 4
+.Ldo_restart:
+ .long do_restart
+.Lrestart_stack:
+ .long restart_stack
+ .align 8
+restart_psw_crash:
+ .long 0x000a0000,0x00000000 + restart_psw_crash
+
.section .kprobes.text, "ax"
#ifdef CONFIG_CHECK_STACK
diff --git a/arch/s390/kernel/entry64.S b/arch/s390/kernel/entry64.S
index 7a0fd42..5f729d6 100644
--- a/arch/s390/kernel/entry64.S
+++ b/arch/s390/kernel/entry64.S
@@ -865,6 +865,26 @@
restart_go:
#endif
+#
+# PSW restart interrupt handler
+#
+ENTRY(psw_restart_int_handler)
+ stg %r15,__LC_SAVE_AREA_64(%r0) # save r15
+ larl %r15,restart_stack # load restart stack
+ lg %r15,0(%r15)
+ aghi %r15,-SP_SIZE # make room for pt_regs
+ stmg %r0,%r14,SP_R0(%r15) # store gprs %r0-%r14 to stack
+ mvc SP_R15(8,%r15),__LC_SAVE_AREA_64(%r0)# store saved %r15 to stack
+ mvc SP_PSW(16,%r15),__LC_RST_OLD_PSW(%r0)# store restart old psw
+ xc __SF_BACKCHAIN(8,%r15),__SF_BACKCHAIN(%r15) # set backchain to 0
+ brasl %r14,do_restart
+
+ larl %r14,restart_psw_crash # load disabled wait PSW if
+ lpswe 0(%r14) # do_restart returns
+ .align 8
+restart_psw_crash:
+ .quad 0x0002000080000000,0x0000000000000000 + restart_psw_crash
+
.section .kprobes.text, "ax"
#ifdef CONFIG_CHECK_STACK
diff --git a/arch/s390/kernel/ipl.c b/arch/s390/kernel/ipl.c
index a689070..04361d5 100644
--- a/arch/s390/kernel/ipl.c
+++ b/arch/s390/kernel/ipl.c
@@ -45,11 +45,13 @@
* - halt
* - power off
* - reipl
+ * - restart
*/
#define ON_PANIC_STR "on_panic"
#define ON_HALT_STR "on_halt"
#define ON_POFF_STR "on_poff"
#define ON_REIPL_STR "on_reboot"
+#define ON_RESTART_STR "on_restart"
struct shutdown_action;
struct shutdown_trigger {
@@ -1544,17 +1546,20 @@
static char vmcmd_on_panic[128];
static char vmcmd_on_halt[128];
static char vmcmd_on_poff[128];
+static char vmcmd_on_restart[128];
DEFINE_IPL_ATTR_STR_RW(vmcmd, on_reboot, "%s\n", "%s\n", vmcmd_on_reboot);
DEFINE_IPL_ATTR_STR_RW(vmcmd, on_panic, "%s\n", "%s\n", vmcmd_on_panic);
DEFINE_IPL_ATTR_STR_RW(vmcmd, on_halt, "%s\n", "%s\n", vmcmd_on_halt);
DEFINE_IPL_ATTR_STR_RW(vmcmd, on_poff, "%s\n", "%s\n", vmcmd_on_poff);
+DEFINE_IPL_ATTR_STR_RW(vmcmd, on_restart, "%s\n", "%s\n", vmcmd_on_restart);
static struct attribute *vmcmd_attrs[] = {
&sys_vmcmd_on_reboot_attr.attr,
&sys_vmcmd_on_panic_attr.attr,
&sys_vmcmd_on_halt_attr.attr,
&sys_vmcmd_on_poff_attr.attr,
+ &sys_vmcmd_on_restart_attr.attr,
NULL,
};
@@ -1576,6 +1581,8 @@
cmd = vmcmd_on_halt;
else if (strcmp(trigger->name, ON_POFF_STR) == 0)
cmd = vmcmd_on_poff;
+ else if (strcmp(trigger->name, ON_RESTART_STR) == 0)
+ cmd = vmcmd_on_restart;
else
return;
@@ -1707,6 +1714,34 @@
stop_run(&on_panic_trigger);
}
+/* on restart */
+
+static struct shutdown_trigger on_restart_trigger = {ON_RESTART_STR,
+ &reipl_action};
+
+static ssize_t on_restart_show(struct kobject *kobj,
+ struct kobj_attribute *attr, char *page)
+{
+ return sprintf(page, "%s\n", on_restart_trigger.action->name);
+}
+
+static ssize_t on_restart_store(struct kobject *kobj,
+ struct kobj_attribute *attr,
+ const char *buf, size_t len)
+{
+ return set_trigger(buf, &on_restart_trigger, len);
+}
+
+static struct kobj_attribute on_restart_attr =
+ __ATTR(on_restart, 0644, on_restart_show, on_restart_store);
+
+void do_restart(void)
+{
+ smp_send_stop();
+ on_restart_trigger.action->fn(&on_restart_trigger);
+ stop_run(&on_restart_trigger);
+}
+
/* on halt */
static struct shutdown_trigger on_halt_trigger = {ON_HALT_STR, &stop_action};
@@ -1783,7 +1818,9 @@
if (sysfs_create_file(&shutdown_actions_kset->kobj,
&on_poff_attr.attr))
goto fail;
-
+ if (sysfs_create_file(&shutdown_actions_kset->kobj,
+ &on_restart_attr.attr))
+ goto fail;
return;
fail:
panic("shutdown_triggers_init failed\n");
@@ -1959,6 +1996,12 @@
{
struct reset_call *reset;
+#ifdef CONFIG_64BIT
+ if (diag308_set_works) {
+ diag308_reset();
+ return;
+ }
+#endif
list_for_each_entry(reset, &rcall, list)
reset->fn();
}
diff --git a/arch/s390/kernel/reipl64.S b/arch/s390/kernel/reipl64.S
index 78eb7cf..e690975 100644
--- a/arch/s390/kernel/reipl64.S
+++ b/arch/s390/kernel/reipl64.S
@@ -1,5 +1,5 @@
/*
- * Copyright IBM Corp 2000,2009
+ * Copyright IBM Corp 2000,2011
* Author(s): Holger Smolinski <Holger.Smolinski@de.ibm.com>,
* Denis Joseph Barrow,
*/
@@ -8,6 +8,64 @@
#include <asm/asm-offsets.h>
#
+# store_status
+#
+# Prerequisites to run this function:
+# - Prefix register is set to zero
+# - Original prefix register is stored in "dump_prefix_page"
+# - Lowcore protection is off
+#
+ENTRY(store_status)
+ /* Save register one and load save area base */
+ stg %r1,__LC_SAVE_AREA_64(%r0)
+ lghi %r1,SAVE_AREA_BASE
+ /* General purpose registers */
+ stmg %r0,%r15,__LC_GPREGS_SAVE_AREA-SAVE_AREA_BASE(%r1)
+ lg %r2,__LC_SAVE_AREA_64(%r0)
+ stg %r2,__LC_GPREGS_SAVE_AREA-SAVE_AREA_BASE+8(%r1)
+ /* Control registers */
+ stctg %c0,%c15,__LC_CREGS_SAVE_AREA-SAVE_AREA_BASE(%r1)
+ /* Access registers */
+ stam %a0,%a15,__LC_AREGS_SAVE_AREA-SAVE_AREA_BASE(%r1)
+ /* Floating point registers */
+ std %f0, 0x00 + __LC_FPREGS_SAVE_AREA-SAVE_AREA_BASE(%r1)
+ std %f1, 0x08 + __LC_FPREGS_SAVE_AREA-SAVE_AREA_BASE(%r1)
+ std %f2, 0x10 + __LC_FPREGS_SAVE_AREA-SAVE_AREA_BASE(%r1)
+ std %f3, 0x18 + __LC_FPREGS_SAVE_AREA-SAVE_AREA_BASE(%r1)
+ std %f4, 0x20 + __LC_FPREGS_SAVE_AREA-SAVE_AREA_BASE(%r1)
+ std %f5, 0x28 + __LC_FPREGS_SAVE_AREA-SAVE_AREA_BASE(%r1)
+ std %f6, 0x30 + __LC_FPREGS_SAVE_AREA-SAVE_AREA_BASE(%r1)
+ std %f7, 0x38 + __LC_FPREGS_SAVE_AREA-SAVE_AREA_BASE(%r1)
+ std %f8, 0x40 + __LC_FPREGS_SAVE_AREA-SAVE_AREA_BASE(%r1)
+ std %f9, 0x48 + __LC_FPREGS_SAVE_AREA-SAVE_AREA_BASE(%r1)
+ std %f10,0x50 + __LC_FPREGS_SAVE_AREA-SAVE_AREA_BASE(%r1)
+ std %f11,0x58 + __LC_FPREGS_SAVE_AREA-SAVE_AREA_BASE(%r1)
+ std %f12,0x60 + __LC_FPREGS_SAVE_AREA-SAVE_AREA_BASE(%r1)
+ std %f13,0x68 + __LC_FPREGS_SAVE_AREA-SAVE_AREA_BASE(%r1)
+ std %f14,0x70 + __LC_FPREGS_SAVE_AREA-SAVE_AREA_BASE(%r1)
+ std %f15,0x78 + __LC_FPREGS_SAVE_AREA-SAVE_AREA_BASE(%r1)
+ /* Floating point control register */
+ stfpc __LC_FP_CREG_SAVE_AREA-SAVE_AREA_BASE(%r1)
+ /* CPU timer */
+ stpt __LC_CPU_TIMER_SAVE_AREA-SAVE_AREA_BASE(%r1)
+ /* Saved prefix register */
+ larl %r2,dump_prefix_page
+ mvc __LC_PREFIX_SAVE_AREA-SAVE_AREA_BASE(4,%r1),0(%r2)
+ /* Clock comparator - seven bytes */
+ larl %r2,.Lclkcmp
+ stckc 0(%r2)
+ mvc __LC_CLOCK_COMP_SAVE_AREA-SAVE_AREA_BASE + 1(7,%r1),1(%r2)
+ /* Program status word */
+ epsw %r2,%r3
+ st %r2,__LC_PSW_SAVE_AREA-SAVE_AREA_BASE + 0(%r1)
+ st %r3,__LC_PSW_SAVE_AREA-SAVE_AREA_BASE + 4(%r1)
+ larl %r2,store_status
+ stg %r2,__LC_PSW_SAVE_AREA-SAVE_AREA_BASE + 8(%r1)
+ br %r14
+.align 8
+.Lclkcmp: .quad 0x0000000000000000
+
+#
# do_reipl_asm
# Parameter: r2 = schid of reipl device
#
@@ -15,22 +73,7 @@
ENTRY(do_reipl_asm)
basr %r13,0
.Lpg0: lpswe .Lnewpsw-.Lpg0(%r13)
-.Lpg1: # do store status of all registers
-
- stg %r1,.Lregsave-.Lpg0(%r13)
- lghi %r1,0x1000
- stmg %r0,%r15,__LC_GPREGS_SAVE_AREA-0x1000(%r1)
- lg %r0,.Lregsave-.Lpg0(%r13)
- stg %r0,__LC_GPREGS_SAVE_AREA-0x1000+8(%r1)
- stctg %c0,%c15,__LC_CREGS_SAVE_AREA-0x1000(%r1)
- stam %a0,%a15,__LC_AREGS_SAVE_AREA-0x1000(%r1)
- lg %r10,.Ldump_pfx-.Lpg0(%r13)
- mvc __LC_PREFIX_SAVE_AREA-0x1000(4,%r1),0(%r10)
- stfpc __LC_FP_CREG_SAVE_AREA-0x1000(%r1)
- stckc .Lclkcmp-.Lpg0(%r13)
- mvc __LC_CLOCK_COMP_SAVE_AREA-0x1000(7,%r1),.Lclkcmp-.Lpg0(%r13)
- stpt __LC_CPU_TIMER_SAVE_AREA-0x1000(%r1)
- stg %r13, __LC_PSW_SAVE_AREA-0x1000+8(%r1)
+.Lpg1: brasl %r14,store_status
lctlg %c6,%c6,.Lall-.Lpg0(%r13)
lgr %r1,%r2
@@ -67,10 +110,7 @@
st %r14,.Ldispsw+12-.Lpg0(%r13)
lpswe .Ldispsw-.Lpg0(%r13)
.align 8
-.Lclkcmp: .quad 0x0000000000000000
.Lall: .quad 0x00000000ff000000
-.Ldump_pfx: .quad dump_prefix_page
-.Lregsave: .quad 0x0000000000000000
.align 16
/*
* These addresses have to be 31 bit otherwise
diff --git a/arch/s390/kernel/setup.c b/arch/s390/kernel/setup.c
index 0c35dee..7b371c3 100644
--- a/arch/s390/kernel/setup.c
+++ b/arch/s390/kernel/setup.c
@@ -346,7 +346,7 @@
lc = __alloc_bootmem_low(LC_PAGES * PAGE_SIZE, LC_PAGES * PAGE_SIZE, 0);
lc->restart_psw.mask = PSW_BASE_BITS | PSW_DEFAULT_KEY;
lc->restart_psw.addr =
- PSW_ADDR_AMODE | (unsigned long) restart_int_handler;
+ PSW_ADDR_AMODE | (unsigned long) psw_restart_int_handler;
if (user_mode != HOME_SPACE_MODE)
lc->restart_psw.mask |= PSW_ASC_HOME;
lc->external_new_psw.mask = psw_kernel_bits;
@@ -529,6 +529,27 @@
memory_end = memory_size;
}
+void *restart_stack __attribute__((__section__(".data")));
+
+/*
+ * Setup new PSW and allocate stack for PSW restart interrupt
+ */
+static void __init setup_restart_psw(void)
+{
+ psw_t psw;
+
+ restart_stack = __alloc_bootmem(ASYNC_SIZE, ASYNC_SIZE, 0);
+ restart_stack += ASYNC_SIZE;
+
+ /*
+ * Setup restart PSW for absolute zero lowcore. This is necesary
+ * if PSW restart is done on an offline CPU that has lowcore zero
+ */
+ psw.mask = PSW_BASE_BITS | PSW_DEFAULT_KEY;
+ psw.addr = PSW_ADDR_AMODE | (unsigned long) psw_restart_int_handler;
+ copy_to_absolute_zero(&S390_lowcore.restart_psw, &psw, sizeof(psw));
+}
+
static void __init
setup_memory(void)
{
@@ -731,6 +752,7 @@
strcpy(elf_platform, "z10");
break;
case 0x2817:
+ case 0x2818:
strcpy(elf_platform, "z196");
break;
}
@@ -792,6 +814,7 @@
setup_addressing_mode();
setup_memory();
setup_resources();
+ setup_restart_psw();
setup_lowcore();
cpu_init();
diff --git a/arch/s390/kernel/signal.c b/arch/s390/kernel/signal.c
index abbb3c3..9a40e1c 100644
--- a/arch/s390/kernel/signal.c
+++ b/arch/s390/kernel/signal.c
@@ -57,17 +57,15 @@
*/
SYSCALL_DEFINE3(sigsuspend, int, history0, int, history1, old_sigset_t, mask)
{
- mask &= _BLOCKABLE;
- spin_lock_irq(¤t->sighand->siglock);
- current->saved_sigmask = current->blocked;
- siginitset(¤t->blocked, mask);
- recalc_sigpending();
- spin_unlock_irq(¤t->sighand->siglock);
+ sigset_t blocked;
+ current->saved_sigmask = current->blocked;
+ mask &= _BLOCKABLE;
+ siginitset(&blocked, mask);
+ set_current_blocked(&blocked);
set_current_state(TASK_INTERRUPTIBLE);
schedule();
- set_thread_flag(TIF_RESTORE_SIGMASK);
-
+ set_restore_sigmask();
return -ERESTARTNOHAND;
}
@@ -172,18 +170,11 @@
goto badframe;
if (__copy_from_user(&set.sig, &frame->sc.oldmask, _SIGMASK_COPY_SIZE))
goto badframe;
-
sigdelsetmask(&set, ~_BLOCKABLE);
- spin_lock_irq(¤t->sighand->siglock);
- current->blocked = set;
- recalc_sigpending();
- spin_unlock_irq(¤t->sighand->siglock);
-
+ set_current_blocked(&set);
if (restore_sigregs(regs, &frame->sregs))
goto badframe;
-
return regs->gprs[2];
-
badframe:
force_sig(SIGSEGV, current);
return 0;
@@ -199,21 +190,14 @@
goto badframe;
if (__copy_from_user(&set.sig, &frame->uc.uc_sigmask, sizeof(set)))
goto badframe;
-
sigdelsetmask(&set, ~_BLOCKABLE);
- spin_lock_irq(¤t->sighand->siglock);
- current->blocked = set;
- recalc_sigpending();
- spin_unlock_irq(¤t->sighand->siglock);
-
+ set_current_blocked(&set);
if (restore_sigregs(regs, &frame->uc.uc_mcontext))
goto badframe;
-
if (do_sigaltstack(&frame->uc.uc_stack, NULL,
regs->gprs[15]) == -EFAULT)
goto badframe;
return regs->gprs[2];
-
badframe:
force_sig(SIGSEGV, current);
return 0;
@@ -385,14 +369,11 @@
return -EFAULT;
}
-/*
- * OK, we're invoking a handler
- */
-
-static int
-handle_signal(unsigned long sig, struct k_sigaction *ka,
- siginfo_t *info, sigset_t *oldset, struct pt_regs * regs)
+static int handle_signal(unsigned long sig, struct k_sigaction *ka,
+ siginfo_t *info, sigset_t *oldset,
+ struct pt_regs *regs)
{
+ sigset_t blocked;
int ret;
/* Set up the stack frame */
@@ -400,17 +381,13 @@
ret = setup_rt_frame(sig, ka, info, oldset, regs);
else
ret = setup_frame(sig, ka, oldset, regs);
-
- if (ret == 0) {
- spin_lock_irq(¤t->sighand->siglock);
- sigorsets(¤t->blocked,¤t->blocked,&ka->sa.sa_mask);
- if (!(ka->sa.sa_flags & SA_NODEFER))
- sigaddset(¤t->blocked,sig);
- recalc_sigpending();
- spin_unlock_irq(¤t->sighand->siglock);
- }
-
- return ret;
+ if (ret)
+ return ret;
+ sigorsets(&blocked, ¤t->blocked, &ka->sa.sa_mask);
+ if (!(ka->sa.sa_flags & SA_NODEFER))
+ sigaddset(&blocked, sig);
+ set_current_blocked(&blocked);
+ return 0;
}
/*
diff --git a/arch/s390/kernel/smp.c b/arch/s390/kernel/smp.c
index a6d85c0..6ab16ac 100644
--- a/arch/s390/kernel/smp.c
+++ b/arch/s390/kernel/smp.c
@@ -452,23 +452,27 @@
*/
int __cpuinit start_secondary(void *cpuvoid)
{
- /* Setup the cpu */
cpu_init();
preempt_disable();
- /* Enable TOD clock interrupts on the secondary cpu. */
init_cpu_timer();
- /* Enable cpu timer interrupts on the secondary cpu. */
init_cpu_vtimer();
- /* Enable pfault pseudo page faults on this cpu. */
pfault_init();
- /* call cpu notifiers */
notify_cpu_starting(smp_processor_id());
- /* Mark this cpu as online */
ipi_call_lock();
set_cpu_online(smp_processor_id(), true);
ipi_call_unlock();
- /* Switch on interrupts */
+ __ctl_clear_bit(0, 28); /* Disable lowcore protection */
+ S390_lowcore.restart_psw.mask = PSW_BASE_BITS | PSW_DEFAULT_KEY;
+ S390_lowcore.restart_psw.addr =
+ PSW_ADDR_AMODE | (unsigned long) psw_restart_int_handler;
+ __ctl_set_bit(0, 28); /* Enable lowcore protection */
+ /*
+ * Wait until the cpu which brought this one up marked it
+ * active before enabling interrupts.
+ */
+ while (!cpumask_test_cpu(smp_processor_id(), cpu_active_mask))
+ cpu_relax();
local_irq_enable();
/* cpu_idle will call schedule for us */
cpu_idle();
@@ -507,7 +511,11 @@
memset((char *)lowcore + 512, 0, sizeof(*lowcore) - 512);
lowcore->async_stack = async_stack + ASYNC_SIZE;
lowcore->panic_stack = panic_stack + PAGE_SIZE;
-
+ lowcore->restart_psw.mask = PSW_BASE_BITS | PSW_DEFAULT_KEY;
+ lowcore->restart_psw.addr =
+ PSW_ADDR_AMODE | (unsigned long) restart_int_handler;
+ if (user_mode != HOME_SPACE_MODE)
+ lowcore->restart_psw.mask |= PSW_ASC_HOME;
#ifndef CONFIG_64BIT
if (MACHINE_HAS_IEEE) {
unsigned long save_area;
diff --git a/arch/s390/mm/maccess.c b/arch/s390/mm/maccess.c
index 51e5cd9..5dbbaa6 100644
--- a/arch/s390/mm/maccess.c
+++ b/arch/s390/mm/maccess.c
@@ -85,3 +85,19 @@
arch_local_irq_restore(flags);
return rc;
}
+
+/*
+ * Copy memory to absolute zero
+ */
+void copy_to_absolute_zero(void *dest, void *src, size_t count)
+{
+ unsigned long cr0;
+
+ BUG_ON((unsigned long) dest + count >= sizeof(struct _lowcore));
+ preempt_disable();
+ __ctl_store(cr0, 0, 0);
+ __ctl_clear_bit(0, 28); /* disable lowcore protection */
+ memcpy_real(dest + store_prefix(), src, count);
+ __ctl_load(cr0, 0, 0);
+ preempt_enable();
+}
diff --git a/arch/s390/mm/pgtable.c b/arch/s390/mm/pgtable.c
index 2adb239..4d1f2bc 100644
--- a/arch/s390/mm/pgtable.c
+++ b/arch/s390/mm/pgtable.c
@@ -528,6 +528,7 @@
static inline unsigned long *page_table_alloc_pgste(struct mm_struct *mm,
unsigned long vmaddr)
{
+ return NULL;
}
static inline void page_table_free_pgste(unsigned long *table)
diff --git a/arch/sh/Kconfig b/arch/sh/Kconfig
index 748ff19..ff9177c 100644
--- a/arch/sh/Kconfig
+++ b/arch/sh/Kconfig
@@ -11,6 +11,7 @@
select HAVE_DMA_ATTRS
select HAVE_IRQ_WORK
select HAVE_PERF_EVENTS
+ select ARCH_HAVE_NMI_SAFE_CMPXCHG if (GUSA_RB || CPU_SH4A)
select PERF_USE_VMALLOC
select HAVE_KERNEL_GZIP
select HAVE_KERNEL_BZIP2
diff --git a/arch/sh/Makefile b/arch/sh/Makefile
index e3d8170..99385d0 100644
--- a/arch/sh/Makefile
+++ b/arch/sh/Makefile
@@ -173,6 +173,7 @@
cpuincdir-$(CONFIG_CPU_SH2A) += cpu-sh2a
cpuincdir-$(CONFIG_CPU_SH2) += cpu-sh2
cpuincdir-$(CONFIG_CPU_SH3) += cpu-sh3
+cpuincdir-$(CONFIG_CPU_SH4A) += cpu-sh4a
cpuincdir-$(CONFIG_CPU_SH4) += cpu-sh4
cpuincdir-$(CONFIG_CPU_SH5) += cpu-sh5
cpuincdir-y += cpu-common # Must be last
diff --git a/arch/sh/boards/board-apsh4a3a.c b/arch/sh/boards/board-apsh4a3a.c
index 8e2a270..2823619 100644
--- a/arch/sh/boards/board-apsh4a3a.c
+++ b/arch/sh/boards/board-apsh4a3a.c
@@ -116,7 +116,7 @@
int ret;
clk = clk_get(NULL, "extal");
- if (!clk || IS_ERR(clk))
+ if (IS_ERR(clk))
return PTR_ERR(clk);
ret = clk_set_rate(clk, 33333000);
clk_put(clk);
diff --git a/arch/sh/boards/board-apsh4ad0a.c b/arch/sh/boards/board-apsh4ad0a.c
index e2bd218..b4d6292 100644
--- a/arch/sh/boards/board-apsh4ad0a.c
+++ b/arch/sh/boards/board-apsh4ad0a.c
@@ -94,7 +94,7 @@
int ret;
clk = clk_get(NULL, "extal");
- if (!clk || IS_ERR(clk))
+ if (IS_ERR(clk))
return PTR_ERR(clk);
ret = clk_set_rate(clk, 33333000);
clk_put(clk);
diff --git a/arch/sh/boards/board-sh7785lcr.c b/arch/sh/boards/board-sh7785lcr.c
index ee65ff0..d879848 100644
--- a/arch/sh/boards/board-sh7785lcr.c
+++ b/arch/sh/boards/board-sh7785lcr.c
@@ -299,7 +299,7 @@
int ret;
clk = clk_get(NULL, "extal");
- if (!clk || IS_ERR(clk))
+ if (IS_ERR(clk))
return PTR_ERR(clk);
ret = clk_set_rate(clk, 33333333);
clk_put(clk);
diff --git a/arch/sh/boards/board-urquell.c b/arch/sh/boards/board-urquell.c
index d81c609..24e3316 100644
--- a/arch/sh/boards/board-urquell.c
+++ b/arch/sh/boards/board-urquell.c
@@ -190,7 +190,7 @@
return -EINVAL;
clk = clk_get(NULL, "extal");
- if (!clk || IS_ERR(clk))
+ if (IS_ERR(clk))
return PTR_ERR(clk);
ret = clk_set_rate(clk, 33333333);
clk_put(clk);
diff --git a/arch/sh/boards/mach-highlander/setup.c b/arch/sh/boards/mach-highlander/setup.c
index 87618c9..74b8db1 100644
--- a/arch/sh/boards/mach-highlander/setup.c
+++ b/arch/sh/boards/mach-highlander/setup.c
@@ -335,8 +335,6 @@
&ivdr_clk,
};
-#define CLKDEV_CON_ID(_id, _clk) { .con_id = _id, .clk = _clk }
-
static struct clk_lookup lookups[] = {
/* main clocks */
CLKDEV_CON_ID("ivdr_clk", &ivdr_clk),
diff --git a/arch/sh/boards/mach-sdk7786/setup.c b/arch/sh/boards/mach-sdk7786/setup.c
index 1521aa7..486d1ac 100644
--- a/arch/sh/boards/mach-sdk7786/setup.c
+++ b/arch/sh/boards/mach-sdk7786/setup.c
@@ -194,7 +194,7 @@
return -EINVAL;
clk = clk_get(NULL, "extal");
- if (!clk || IS_ERR(clk))
+ if (IS_ERR(clk))
return PTR_ERR(clk);
ret = clk_set_rate(clk, 33333333);
clk_put(clk);
diff --git a/arch/sh/include/cpu-sh3/cpu/serial.h b/arch/sh/include/cpu-sh3/cpu/serial.h
new file mode 100644
index 0000000..7766329
--- /dev/null
+++ b/arch/sh/include/cpu-sh3/cpu/serial.h
@@ -0,0 +1,10 @@
+#ifndef __CPU_SH3_SERIAL_H
+#define __CPU_SH3_SERIAL_H
+
+#include <linux/serial_sci.h>
+
+extern struct plat_sci_port_ops sh770x_sci_port_ops;
+extern struct plat_sci_port_ops sh7710_sci_port_ops;
+extern struct plat_sci_port_ops sh7720_sci_port_ops;
+
+#endif /* __CPU_SH3_SERIAL_H */
diff --git a/arch/sh/include/cpu-sh4a/cpu/serial.h b/arch/sh/include/cpu-sh4a/cpu/serial.h
new file mode 100644
index 0000000..ff1bc27
--- /dev/null
+++ b/arch/sh/include/cpu-sh4a/cpu/serial.h
@@ -0,0 +1,7 @@
+#ifndef __CPU_SH4A_SERIAL_H
+#define __CPU_SH4A_SERIAL_H
+
+/* arch/sh/kernel/cpu/sh4a/serial-sh7722.c */
+extern struct plat_sci_port_ops sh7722_sci_port_ops;
+
+#endif /* __CPU_SH4A_SERIAL_H */
diff --git a/arch/sh/kernel/cpu/clock-cpg.c b/arch/sh/kernel/cpu/clock-cpg.c
index 8f63a26..f59b1f3 100644
--- a/arch/sh/kernel/cpu/clock-cpg.c
+++ b/arch/sh/kernel/cpu/clock-cpg.c
@@ -35,8 +35,6 @@
&cpu_clk,
};
-#define CLKDEV_CON_ID(_id, _clk) { .con_id = _id, .clk = _clk }
-
static struct clk_lookup lookups[] = {
/* main clocks */
CLKDEV_CON_ID("master_clk", &master_clk),
diff --git a/arch/sh/kernel/cpu/sh3/Makefile b/arch/sh/kernel/cpu/sh3/Makefile
index ecab274..6f13f33 100644
--- a/arch/sh/kernel/cpu/sh3/Makefile
+++ b/arch/sh/kernel/cpu/sh3/Makefile
@@ -7,15 +7,15 @@
obj-$(CONFIG_HIBERNATION) += swsusp.o
# CPU subtype setup
-obj-$(CONFIG_CPU_SUBTYPE_SH7705) += setup-sh7705.o
-obj-$(CONFIG_CPU_SUBTYPE_SH7706) += setup-sh770x.o
-obj-$(CONFIG_CPU_SUBTYPE_SH7707) += setup-sh770x.o
-obj-$(CONFIG_CPU_SUBTYPE_SH7708) += setup-sh770x.o
-obj-$(CONFIG_CPU_SUBTYPE_SH7709) += setup-sh770x.o
-obj-$(CONFIG_CPU_SUBTYPE_SH7710) += setup-sh7710.o
-obj-$(CONFIG_CPU_SUBTYPE_SH7712) += setup-sh7710.o
-obj-$(CONFIG_CPU_SUBTYPE_SH7720) += setup-sh7720.o
-obj-$(CONFIG_CPU_SUBTYPE_SH7721) += setup-sh7720.o
+obj-$(CONFIG_CPU_SUBTYPE_SH7705) += setup-sh7705.o serial-sh770x.o
+obj-$(CONFIG_CPU_SUBTYPE_SH7706) += setup-sh770x.o serial-sh770x.o
+obj-$(CONFIG_CPU_SUBTYPE_SH7707) += setup-sh770x.o serial-sh770x.o
+obj-$(CONFIG_CPU_SUBTYPE_SH7708) += setup-sh770x.o serial-sh770x.o
+obj-$(CONFIG_CPU_SUBTYPE_SH7709) += setup-sh770x.o serial-sh770x.o
+obj-$(CONFIG_CPU_SUBTYPE_SH7710) += setup-sh7710.o serial-sh7710.o
+obj-$(CONFIG_CPU_SUBTYPE_SH7712) += setup-sh7710.o serial-sh7710.o
+obj-$(CONFIG_CPU_SUBTYPE_SH7720) += setup-sh7720.o serial-sh7720.o
+obj-$(CONFIG_CPU_SUBTYPE_SH7721) += setup-sh7720.o serial-sh7720.o
# Primary on-chip clocks (common)
clock-$(CONFIG_CPU_SH3) := clock-sh3.o
diff --git a/arch/sh/kernel/cpu/sh3/serial-sh770x.c b/arch/sh/kernel/cpu/sh3/serial-sh770x.c
new file mode 100644
index 0000000..4f7242c
--- /dev/null
+++ b/arch/sh/kernel/cpu/sh3/serial-sh770x.c
@@ -0,0 +1,33 @@
+#include <linux/serial_sci.h>
+#include <linux/serial_core.h>
+#include <linux/io.h>
+#include <cpu/serial.h>
+
+#define SCPCR 0xA4000116
+#define SCPDR 0xA4000136
+
+static void sh770x_sci_init_pins(struct uart_port *port, unsigned int cflag)
+{
+ unsigned short data;
+
+ /* We need to set SCPCR to enable RTS/CTS */
+ data = __raw_readw(SCPCR);
+ /* Clear out SCP7MD1,0, SCP6MD1,0, SCP4MD1,0*/
+ __raw_writew(data & 0x0fcf, SCPCR);
+
+ if (!(cflag & CRTSCTS)) {
+ /* We need to set SCPCR to enable RTS/CTS */
+ data = __raw_readw(SCPCR);
+ /* Clear out SCP7MD1,0, SCP4MD1,0,
+ Set SCP6MD1,0 = {01} (output) */
+ __raw_writew((data & 0x0fcf) | 0x1000, SCPCR);
+
+ data = __raw_readb(SCPDR);
+ /* Set /RTS2 (bit6) = 0 */
+ __raw_writeb(data & 0xbf, SCPDR);
+ }
+}
+
+struct plat_sci_port_ops sh770x_sci_port_ops = {
+ .init_pins = sh770x_sci_init_pins,
+};
diff --git a/arch/sh/kernel/cpu/sh3/serial-sh7710.c b/arch/sh/kernel/cpu/sh3/serial-sh7710.c
new file mode 100644
index 0000000..42190ef
--- /dev/null
+++ b/arch/sh/kernel/cpu/sh3/serial-sh7710.c
@@ -0,0 +1,20 @@
+#include <linux/serial_sci.h>
+#include <linux/serial_core.h>
+#include <linux/io.h>
+#include <cpu/serial.h>
+
+#define PACR 0xa4050100
+#define PBCR 0xa4050102
+
+static void sh7710_sci_init_pins(struct uart_port *port, unsigned int cflag)
+{
+ if (port->mapbase == 0xA4400000) {
+ __raw_writew(__raw_readw(PACR) & 0xffc0, PACR);
+ __raw_writew(__raw_readw(PBCR) & 0x0fff, PBCR);
+ } else if (port->mapbase == 0xA4410000)
+ __raw_writew(__raw_readw(PBCR) & 0xf003, PBCR);
+}
+
+struct plat_sci_port_ops sh7710_sci_port_ops = {
+ .init_pins = sh7710_sci_init_pins,
+};
diff --git a/arch/sh/kernel/cpu/sh3/serial-sh7720.c b/arch/sh/kernel/cpu/sh3/serial-sh7720.c
new file mode 100644
index 0000000..8832c52
--- /dev/null
+++ b/arch/sh/kernel/cpu/sh3/serial-sh7720.c
@@ -0,0 +1,37 @@
+#include <linux/serial_sci.h>
+#include <linux/serial_core.h>
+#include <linux/io.h>
+#include <cpu/serial.h>
+#include <asm/gpio.h>
+
+static void sh7720_sci_init_pins(struct uart_port *port, unsigned int cflag)
+{
+ unsigned short data;
+
+ if (cflag & CRTSCTS) {
+ /* enable RTS/CTS */
+ if (port->mapbase == 0xa4430000) { /* SCIF0 */
+ /* Clear PTCR bit 9-2; enable all scif pins but sck */
+ data = __raw_readw(PORT_PTCR);
+ __raw_writew((data & 0xfc03), PORT_PTCR);
+ } else if (port->mapbase == 0xa4438000) { /* SCIF1 */
+ /* Clear PVCR bit 9-2 */
+ data = __raw_readw(PORT_PVCR);
+ __raw_writew((data & 0xfc03), PORT_PVCR);
+ }
+ } else {
+ if (port->mapbase == 0xa4430000) { /* SCIF0 */
+ /* Clear PTCR bit 5-2; enable only tx and rx */
+ data = __raw_readw(PORT_PTCR);
+ __raw_writew((data & 0xffc3), PORT_PTCR);
+ } else if (port->mapbase == 0xa4438000) { /* SCIF1 */
+ /* Clear PVCR bit 5-2 */
+ data = __raw_readw(PORT_PVCR);
+ __raw_writew((data & 0xffc3), PORT_PVCR);
+ }
+ }
+}
+
+struct plat_sci_port_ops sh7720_sci_port_ops = {
+ .init_pins = sh7720_sci_init_pins,
+};
diff --git a/arch/sh/kernel/cpu/sh3/setup-sh7705.c b/arch/sh/kernel/cpu/sh3/setup-sh7705.c
index cd2e702..2309618 100644
--- a/arch/sh/kernel/cpu/sh3/setup-sh7705.c
+++ b/arch/sh/kernel/cpu/sh3/setup-sh7705.c
@@ -15,6 +15,7 @@
#include <linux/serial_sci.h>
#include <linux/sh_timer.h>
#include <asm/rtc.h>
+#include <cpu/serial.h>
enum {
UNUSED = 0,
@@ -75,6 +76,8 @@
.scbrr_algo_id = SCBRR_ALGO_4,
.type = PORT_SCIF,
.irqs = { 56, 56, 56 },
+ .ops = &sh770x_sci_port_ops,
+ .regtype = SCIx_SH7705_SCIF_REGTYPE,
};
static struct platform_device scif0_device = {
@@ -92,6 +95,8 @@
.scbrr_algo_id = SCBRR_ALGO_4,
.type = PORT_SCIF,
.irqs = { 52, 52, 52 },
+ .ops = &sh770x_sci_port_ops,
+ .regtype = SCIx_SH7705_SCIF_REGTYPE,
};
static struct platform_device scif1_device = {
diff --git a/arch/sh/kernel/cpu/sh3/setup-sh770x.c b/arch/sh/kernel/cpu/sh3/setup-sh770x.c
index 4551ad6..3f3d5fe 100644
--- a/arch/sh/kernel/cpu/sh3/setup-sh770x.c
+++ b/arch/sh/kernel/cpu/sh3/setup-sh770x.c
@@ -19,6 +19,7 @@
#include <linux/serial.h>
#include <linux/serial_sci.h>
#include <linux/sh_timer.h>
+#include <cpu/serial.h>
enum {
UNUSED = 0,
@@ -108,11 +109,14 @@
static struct plat_sci_port scif0_platform_data = {
.mapbase = 0xfffffe80,
+ .port_reg = 0xa4000136,
.flags = UPF_BOOT_AUTOCONF,
.scscr = SCSCR_TE | SCSCR_RE,
.scbrr_algo_id = SCBRR_ALGO_2,
.type = PORT_SCI,
.irqs = { 23, 23, 23, 0 },
+ .ops = &sh770x_sci_port_ops,
+ .regshift = 1,
};
static struct platform_device scif0_device = {
@@ -132,6 +136,8 @@
.scbrr_algo_id = SCBRR_ALGO_2,
.type = PORT_SCIF,
.irqs = { 56, 56, 56, 56 },
+ .ops = &sh770x_sci_port_ops,
+ .regtype = SCIx_SH3_SCIF_REGTYPE,
};
static struct platform_device scif1_device = {
@@ -146,11 +152,14 @@
defined(CONFIG_CPU_SUBTYPE_SH7709)
static struct plat_sci_port scif2_platform_data = {
.mapbase = 0xa4000140,
+ .port_reg = SCIx_NOT_SUPPORTED,
.flags = UPF_BOOT_AUTOCONF,
.scscr = SCSCR_TE | SCSCR_RE,
.scbrr_algo_id = SCBRR_ALGO_2,
.type = PORT_IRDA,
.irqs = { 52, 52, 52, 52 },
+ .ops = &sh770x_sci_port_ops,
+ .regshift = 1,
};
static struct platform_device scif2_device = {
diff --git a/arch/sh/kernel/cpu/sh3/setup-sh7720.c b/arch/sh/kernel/cpu/sh3/setup-sh7720.c
index 365b94a..9492034 100644
--- a/arch/sh/kernel/cpu/sh3/setup-sh7720.c
+++ b/arch/sh/kernel/cpu/sh3/setup-sh7720.c
@@ -20,6 +20,7 @@
#include <linux/serial_sci.h>
#include <linux/sh_timer.h>
#include <asm/rtc.h>
+#include <cpu/serial.h>
static struct resource rtc_resources[] = {
[0] = {
@@ -55,6 +56,8 @@
.scbrr_algo_id = SCBRR_ALGO_4,
.type = PORT_SCIF,
.irqs = { 80, 80, 80, 80 },
+ .ops = &sh7720_sci_port_ops,
+ .regtype = SCIx_SH7705_SCIF_REGTYPE,
};
static struct platform_device scif0_device = {
@@ -72,6 +75,8 @@
.scbrr_algo_id = SCBRR_ALGO_4,
.type = PORT_SCIF,
.irqs = { 81, 81, 81, 81 },
+ .ops = &sh7720_sci_port_ops,
+ .regtype = SCIx_SH7705_SCIF_REGTYPE,
};
static struct platform_device scif1_device = {
diff --git a/arch/sh/kernel/cpu/sh4/clock-sh4-202.c b/arch/sh/kernel/cpu/sh4/clock-sh4-202.c
index 3f6f8e9..f4e262a 100644
--- a/arch/sh/kernel/cpu/sh4/clock-sh4-202.c
+++ b/arch/sh/kernel/cpu/sh4/clock-sh4-202.c
@@ -147,8 +147,6 @@
&sh4202_shoc_clk,
};
-#define CLKDEV_CON_ID(_id, _clk) { .con_id = _id, .clk = _clk }
-
static struct clk_lookup lookups[] = {
/* main clocks */
CLKDEV_CON_ID("emi_clk", &sh4202_emi_clk),
diff --git a/arch/sh/kernel/cpu/sh4/setup-sh7750.c b/arch/sh/kernel/cpu/sh4/setup-sh7750.c
index e53b4b3..98cc0c7 100644
--- a/arch/sh/kernel/cpu/sh4/setup-sh7750.c
+++ b/arch/sh/kernel/cpu/sh4/setup-sh7750.c
@@ -1,5 +1,5 @@
/*
- * SH7750/SH7751 Setup
+ * SH7091/SH7750/SH7750S/SH7750R/SH7751/SH7751R Setup
*
* Copyright (C) 2006 Paul Mundt
* Copyright (C) 2006 Jamie Lenehan
@@ -38,11 +38,13 @@
static struct plat_sci_port sci_platform_data = {
.mapbase = 0xffe00000,
+ .port_reg = 0xffe0001C,
.flags = UPF_BOOT_AUTOCONF,
.scscr = SCSCR_TE | SCSCR_RE,
.scbrr_algo_id = SCBRR_ALGO_2,
.type = PORT_SCI,
.irqs = { 23, 23, 23, 0 },
+ .regshift = 2,
};
static struct platform_device sci_device = {
diff --git a/arch/sh/kernel/cpu/sh4/setup-sh7760.c b/arch/sh/kernel/cpu/sh4/setup-sh7760.c
index 78bbf23..c0b4c77 100644
--- a/arch/sh/kernel/cpu/sh4/setup-sh7760.c
+++ b/arch/sh/kernel/cpu/sh4/setup-sh7760.c
@@ -133,6 +133,7 @@
.scbrr_algo_id = SCBRR_ALGO_2,
.type = PORT_SCIF,
.irqs = { 52, 53, 55, 54 },
+ .regtype = SCIx_SH4_SCIF_FIFODATA_REGTYPE,
};
static struct platform_device scif0_device = {
@@ -150,6 +151,7 @@
.scscr = SCSCR_RE | SCSCR_TE | SCSCR_REIE,
.scbrr_algo_id = SCBRR_ALGO_2,
.irqs = { 72, 73, 75, 74 },
+ .regtype = SCIx_SH4_SCIF_FIFODATA_REGTYPE,
};
static struct platform_device scif1_device = {
@@ -167,6 +169,7 @@
.scbrr_algo_id = SCBRR_ALGO_2,
.type = PORT_SCIF,
.irqs = { 76, 77, 79, 78 },
+ .regtype = SCIx_SH4_SCIF_FIFODATA_REGTYPE,
};
static struct platform_device scif2_device = {
@@ -184,6 +187,7 @@
.scbrr_algo_id = SCBRR_ALGO_2,
.type = PORT_SCI,
.irqs = { 80, 81, 82, 0 },
+ .regshift = 2,
};
static struct platform_device scif3_device = {
diff --git a/arch/sh/kernel/cpu/sh4a/Makefile b/arch/sh/kernel/cpu/sh4a/Makefile
index cc122b1..c57fb28 100644
--- a/arch/sh/kernel/cpu/sh4a/Makefile
+++ b/arch/sh/kernel/cpu/sh4a/Makefile
@@ -10,7 +10,7 @@
obj-$(CONFIG_CPU_SUBTYPE_SH7785) += setup-sh7785.o
obj-$(CONFIG_CPU_SUBTYPE_SH7786) += setup-sh7786.o intc-shx3.o
obj-$(CONFIG_CPU_SUBTYPE_SH7343) += setup-sh7343.o
-obj-$(CONFIG_CPU_SUBTYPE_SH7722) += setup-sh7722.o
+obj-$(CONFIG_CPU_SUBTYPE_SH7722) += setup-sh7722.o serial-sh7722.o
obj-$(CONFIG_CPU_SUBTYPE_SH7723) += setup-sh7723.o
obj-$(CONFIG_CPU_SUBTYPE_SH7724) += setup-sh7724.o
obj-$(CONFIG_CPU_SUBTYPE_SH7366) += setup-sh7366.o
diff --git a/arch/sh/kernel/cpu/sh4a/clock-sh7343.c b/arch/sh/kernel/cpu/sh4a/clock-sh7343.c
index 93c6460..70e45bd 100644
--- a/arch/sh/kernel/cpu/sh4a/clock-sh7343.c
+++ b/arch/sh/kernel/cpu/sh4a/clock-sh7343.c
@@ -194,8 +194,6 @@
[MSTP200] = MSTP(&div4_clks[DIV4_B], MSTPCR2, 0, 0),
};
-#define CLKDEV_CON_ID(_id, _clk) { .con_id = _id, .clk = _clk }
-
static struct clk_lookup lookups[] = {
/* main clocks */
CLKDEV_CON_ID("rclk", &r_clk),
@@ -233,32 +231,17 @@
CLKDEV_CON_ID("rwdt0", &mstp_clks[MSTP013]),
CLKDEV_CON_ID("mfi0", &mstp_clks[MSTP011]),
CLKDEV_CON_ID("flctl0", &mstp_clks[MSTP010]),
- {
- /* SCIF0 */
- .dev_id = "sh-sci.0",
- .con_id = "sci_fck",
- .clk = &mstp_clks[MSTP007],
- }, {
- /* SCIF1 */
- .dev_id = "sh-sci.1",
- .con_id = "sci_fck",
- .clk = &mstp_clks[MSTP006],
- }, {
- /* SCIF2 */
- .dev_id = "sh-sci.2",
- .con_id = "sci_fck",
- .clk = &mstp_clks[MSTP005],
- }, {
- /* SCIF3 */
- .dev_id = "sh-sci.3",
- .con_id = "sci_fck",
- .clk = &mstp_clks[MSTP004],
- },
+
+ CLKDEV_ICK_ID("sci_fck", "sh-sci.0", &mstp_clks[MSTP007]),
+ CLKDEV_ICK_ID("sci_fck", "sh-sci.1", &mstp_clks[MSTP006]),
+ CLKDEV_ICK_ID("sci_fck", "sh-sci.2", &mstp_clks[MSTP005]),
+ CLKDEV_ICK_ID("sci_fck", "sh-sci.3", &mstp_clks[MSTP004]),
+
CLKDEV_CON_ID("sio0", &mstp_clks[MSTP003]),
CLKDEV_CON_ID("siof0", &mstp_clks[MSTP002]),
CLKDEV_CON_ID("siof1", &mstp_clks[MSTP001]),
- CLKDEV_CON_ID("i2c0", &mstp_clks[MSTP109]),
- CLKDEV_CON_ID("i2c1", &mstp_clks[MSTP108]),
+ CLKDEV_DEV_ID("i2c-sh_mobile.0", &mstp_clks[MSTP109]),
+ CLKDEV_DEV_ID("i2c-sh_mobile.1", &mstp_clks[MSTP108]),
CLKDEV_CON_ID("tpu0", &mstp_clks[MSTP225]),
CLKDEV_CON_ID("irda0", &mstp_clks[MSTP224]),
CLKDEV_CON_ID("sdhi0", &mstp_clks[MSTP218]),
diff --git a/arch/sh/kernel/cpu/sh4a/clock-sh7366.c b/arch/sh/kernel/cpu/sh4a/clock-sh7366.c
index 049dc06..3c31650 100644
--- a/arch/sh/kernel/cpu/sh4a/clock-sh7366.c
+++ b/arch/sh/kernel/cpu/sh4a/clock-sh7366.c
@@ -192,8 +192,6 @@
[MSTP200] = MSTP(&div4_clks[DIV4_B], MSTPCR2, 0, 0),
};
-#define CLKDEV_CON_ID(_id, _clk) { .con_id = _id, .clk = _clk }
-
static struct clk_lookup lookups[] = {
/* main clocks */
CLKDEV_CON_ID("rclk", &r_clk),
@@ -231,25 +229,14 @@
CLKDEV_CON_ID("rwdt0", &mstp_clks[MSTP013]),
CLKDEV_CON_ID("mfi0", &mstp_clks[MSTP011]),
CLKDEV_CON_ID("flctl0", &mstp_clks[MSTP010]),
- {
- /* SCIF0 */
- .dev_id = "sh-sci.0",
- .con_id = "sci_fck",
- .clk = &mstp_clks[MSTP007],
- }, {
- /* SCIF1 */
- .dev_id = "sh-sci.1",
- .con_id = "sci_fck",
- .clk = &mstp_clks[MSTP006],
- }, {
- /* SCIF2 */
- .dev_id = "sh-sci.2",
- .con_id = "sci_fck",
- .clk = &mstp_clks[MSTP005],
- },
+
+ CLKDEV_ICK_ID("sci_fck", "sh-sci.0", &mstp_clks[MSTP007]),
+ CLKDEV_ICK_ID("sci_fck", "sh-sci.1", &mstp_clks[MSTP006]),
+ CLKDEV_ICK_ID("sci_fck", "sh-sci.2", &mstp_clks[MSTP005]),
+
CLKDEV_CON_ID("msiof0", &mstp_clks[MSTP002]),
CLKDEV_CON_ID("sbr0", &mstp_clks[MSTP001]),
- CLKDEV_CON_ID("i2c0", &mstp_clks[MSTP109]),
+ CLKDEV_DEV_ID("i2c-sh_mobile.0", &mstp_clks[MSTP109]),
CLKDEV_CON_ID("icb0", &mstp_clks[MSTP227]),
CLKDEV_CON_ID("meram0", &mstp_clks[MSTP226]),
CLKDEV_CON_ID("dacy1", &mstp_clks[MSTP224]),
diff --git a/arch/sh/kernel/cpu/sh4a/clock-sh7722.c b/arch/sh/kernel/cpu/sh4a/clock-sh7722.c
index 9d23a36..c9a4808 100644
--- a/arch/sh/kernel/cpu/sh4a/clock-sh7722.c
+++ b/arch/sh/kernel/cpu/sh4a/clock-sh7722.c
@@ -175,8 +175,6 @@
SH_HWBLK_CLK(HWBLK_LCDC, &div4_clks[DIV4_P], 0),
};
-#define CLKDEV_CON_ID(_id, _clk) { .con_id = _id, .clk = _clk }
-
static struct clk_lookup lookups[] = {
/* main clocks */
CLKDEV_CON_ID("rclk", &r_clk),
@@ -201,42 +199,20 @@
/* MSTP clocks */
CLKDEV_CON_ID("uram0", &mstp_clks[HWBLK_URAM]),
CLKDEV_CON_ID("xymem0", &mstp_clks[HWBLK_XYMEM]),
- {
- /* TMU0 */
- .dev_id = "sh_tmu.0",
- .con_id = "tmu_fck",
- .clk = &mstp_clks[HWBLK_TMU],
- }, {
- /* TMU1 */
- .dev_id = "sh_tmu.1",
- .con_id = "tmu_fck",
- .clk = &mstp_clks[HWBLK_TMU],
- }, {
- /* TMU2 */
- .dev_id = "sh_tmu.2",
- .con_id = "tmu_fck",
- .clk = &mstp_clks[HWBLK_TMU],
- },
+
+ CLKDEV_ICK_ID("tmu_fck", "sh_tmu.0", &mstp_clks[HWBLK_TMU]),
+ CLKDEV_ICK_ID("tmu_fck", "sh_tmu.1", &mstp_clks[HWBLK_TMU]),
+ CLKDEV_ICK_ID("tmu_fck", "sh_tmu.2", &mstp_clks[HWBLK_TMU]),
+
CLKDEV_CON_ID("cmt_fck", &mstp_clks[HWBLK_CMT]),
CLKDEV_CON_ID("rwdt0", &mstp_clks[HWBLK_RWDT]),
CLKDEV_CON_ID("flctl0", &mstp_clks[HWBLK_FLCTL]),
- {
- /* SCIF0 */
- .dev_id = "sh-sci.0",
- .con_id = "sci_fck",
- .clk = &mstp_clks[HWBLK_SCIF0],
- }, {
- /* SCIF1 */
- .dev_id = "sh-sci.1",
- .con_id = "sci_fck",
- .clk = &mstp_clks[HWBLK_SCIF1],
- }, {
- /* SCIF2 */
- .dev_id = "sh-sci.2",
- .con_id = "sci_fck",
- .clk = &mstp_clks[HWBLK_SCIF2],
- },
- CLKDEV_CON_ID("i2c0", &mstp_clks[HWBLK_IIC]),
+
+ CLKDEV_ICK_ID("sci_fck", "sh-sci.0", &mstp_clks[HWBLK_SCIF0]),
+ CLKDEV_ICK_ID("sci_fck", "sh-sci.1", &mstp_clks[HWBLK_SCIF1]),
+ CLKDEV_ICK_ID("sci_fck", "sh-sci.2", &mstp_clks[HWBLK_SCIF2]),
+
+ CLKDEV_DEV_ID("i2c-sh_mobile.0", &mstp_clks[HWBLK_IIC]),
CLKDEV_CON_ID("rtc0", &mstp_clks[HWBLK_RTC]),
CLKDEV_CON_ID("sdhi0", &mstp_clks[HWBLK_SDHI]),
CLKDEV_CON_ID("keysc0", &mstp_clks[HWBLK_KEYSC]),
diff --git a/arch/sh/kernel/cpu/sh4a/clock-sh7723.c b/arch/sh/kernel/cpu/sh4a/clock-sh7723.c
index 55493cd..3cc3827 100644
--- a/arch/sh/kernel/cpu/sh4a/clock-sh7723.c
+++ b/arch/sh/kernel/cpu/sh4a/clock-sh7723.c
@@ -200,8 +200,6 @@
SH_HWBLK_CLK(HWBLK_LCDC, &div4_clks[DIV4_B], 0),
};
-#define CLKDEV_CON_ID(_id, _clk) { .con_id = _id, .clk = _clk }
-
static struct clk_lookup lookups[] = {
/* main clocks */
CLKDEV_CON_ID("rclk", &r_clk),
@@ -305,7 +303,7 @@
CLKDEV_CON_ID("msiof0", &mstp_clks[HWBLK_MSIOF0]),
CLKDEV_CON_ID("msiof1", &mstp_clks[HWBLK_MSIOF1]),
CLKDEV_CON_ID("meram0", &mstp_clks[HWBLK_MERAM]),
- CLKDEV_CON_ID("i2c0", &mstp_clks[HWBLK_IIC]),
+ CLKDEV_DEV_ID("i2c-sh_mobile.0", &mstp_clks[HWBLK_IIC]),
CLKDEV_CON_ID("rtc0", &mstp_clks[HWBLK_RTC]),
CLKDEV_CON_ID("atapi0", &mstp_clks[HWBLK_ATAPI]),
CLKDEV_CON_ID("adc0", &mstp_clks[HWBLK_ADC]),
diff --git a/arch/sh/kernel/cpu/sh4a/clock-sh7724.c b/arch/sh/kernel/cpu/sh4a/clock-sh7724.c
index d08fa95..8668f55 100644
--- a/arch/sh/kernel/cpu/sh4a/clock-sh7724.c
+++ b/arch/sh/kernel/cpu/sh4a/clock-sh7724.c
@@ -252,8 +252,6 @@
SH_HWBLK_CLK(HWBLK_LCDC, &div4_clks[DIV4_B], 0),
};
-#define CLKDEV_CON_ID(_id, _clk) { .con_id = _id, .clk = _clk }
-
static struct clk_lookup lookups[] = {
/* main clocks */
CLKDEV_CON_ID("rclk", &r_clk),
@@ -289,77 +287,31 @@
CLKDEV_CON_ID("sh0", &mstp_clks[HWBLK_SHYWAY]),
CLKDEV_CON_ID("hudi0", &mstp_clks[HWBLK_HUDI]),
CLKDEV_CON_ID("ubc0", &mstp_clks[HWBLK_UBC]),
- {
- /* TMU0 */
- .dev_id = "sh_tmu.0",
- .con_id = "tmu_fck",
- .clk = &mstp_clks[HWBLK_TMU0],
- }, {
- /* TMU1 */
- .dev_id = "sh_tmu.1",
- .con_id = "tmu_fck",
- .clk = &mstp_clks[HWBLK_TMU0],
- }, {
- /* TMU2 */
- .dev_id = "sh_tmu.2",
- .con_id = "tmu_fck",
- .clk = &mstp_clks[HWBLK_TMU0],
- }, {
- /* TMU3 */
- .dev_id = "sh_tmu.3",
- .con_id = "tmu_fck",
- .clk = &mstp_clks[HWBLK_TMU1],
- },
+
+ CLKDEV_ICK_ID("tmu_fck", "sh_tmu.0", &mstp_clks[HWBLK_TMU0]),
+ CLKDEV_ICK_ID("tmu_fck", "sh_tmu.1", &mstp_clks[HWBLK_TMU0]),
+ CLKDEV_ICK_ID("tmu_fck", "sh_tmu.2", &mstp_clks[HWBLK_TMU0]),
+ CLKDEV_ICK_ID("tmu_fck", "sh_tmu.3", &mstp_clks[HWBLK_TMU1]),
+
CLKDEV_CON_ID("cmt_fck", &mstp_clks[HWBLK_CMT]),
CLKDEV_CON_ID("rwdt0", &mstp_clks[HWBLK_RWDT]),
CLKDEV_CON_ID("dmac1", &mstp_clks[HWBLK_DMAC1]),
- {
- /* TMU4 */
- .dev_id = "sh_tmu.4",
- .con_id = "tmu_fck",
- .clk = &mstp_clks[HWBLK_TMU1],
- }, {
- /* TMU5 */
- .dev_id = "sh_tmu.5",
- .con_id = "tmu_fck",
- .clk = &mstp_clks[HWBLK_TMU1],
- }, {
- /* SCIF0 */
- .dev_id = "sh-sci.0",
- .con_id = "sci_fck",
- .clk = &mstp_clks[HWBLK_SCIF0],
- }, {
- /* SCIF1 */
- .dev_id = "sh-sci.1",
- .con_id = "sci_fck",
- .clk = &mstp_clks[HWBLK_SCIF1],
- }, {
- /* SCIF2 */
- .dev_id = "sh-sci.2",
- .con_id = "sci_fck",
- .clk = &mstp_clks[HWBLK_SCIF2],
- }, {
- /* SCIF3 */
- .dev_id = "sh-sci.3",
- .con_id = "sci_fck",
- .clk = &mstp_clks[HWBLK_SCIF3],
- }, {
- /* SCIF4 */
- .dev_id = "sh-sci.4",
- .con_id = "sci_fck",
- .clk = &mstp_clks[HWBLK_SCIF4],
- }, {
- /* SCIF5 */
- .dev_id = "sh-sci.5",
- .con_id = "sci_fck",
- .clk = &mstp_clks[HWBLK_SCIF5],
- },
+
+ CLKDEV_ICK_ID("tmu_fck", "sh_tmu.4", &mstp_clks[HWBLK_TMU1]),
+ CLKDEV_ICK_ID("tmu_fck", "sh_tmu.5", &mstp_clks[HWBLK_TMU1]),
+ CLKDEV_ICK_ID("sci_fck", "sh-sci.0", &mstp_clks[HWBLK_SCIF0]),
+ CLKDEV_ICK_ID("sci_fck", "sh-sci.1", &mstp_clks[HWBLK_SCIF1]),
+ CLKDEV_ICK_ID("sci_fck", "sh-sci.2", &mstp_clks[HWBLK_SCIF2]),
+ CLKDEV_ICK_ID("sci_fck", "sh-sci.3", &mstp_clks[HWBLK_SCIF3]),
+ CLKDEV_ICK_ID("sci_fck", "sh-sci.4", &mstp_clks[HWBLK_SCIF4]),
+ CLKDEV_ICK_ID("sci_fck", "sh-sci.5", &mstp_clks[HWBLK_SCIF5]),
+
CLKDEV_CON_ID("msiof0", &mstp_clks[HWBLK_MSIOF0]),
CLKDEV_CON_ID("msiof1", &mstp_clks[HWBLK_MSIOF1]),
CLKDEV_CON_ID("keysc0", &mstp_clks[HWBLK_KEYSC]),
CLKDEV_CON_ID("rtc0", &mstp_clks[HWBLK_RTC]),
- CLKDEV_CON_ID("i2c0", &mstp_clks[HWBLK_IIC0]),
- CLKDEV_CON_ID("i2c1", &mstp_clks[HWBLK_IIC1]),
+ CLKDEV_DEV_ID("i2c-sh_mobile.0", &mstp_clks[HWBLK_IIC0]),
+ CLKDEV_DEV_ID("i2c-sh_mobile.1", &mstp_clks[HWBLK_IIC1]),
CLKDEV_CON_ID("mmc0", &mstp_clks[HWBLK_MMC]),
CLKDEV_CON_ID("eth0", &mstp_clks[HWBLK_ETHER]),
CLKDEV_CON_ID("atapi0", &mstp_clks[HWBLK_ATAPI]),
diff --git a/arch/sh/kernel/cpu/sh4a/clock-sh7757.c b/arch/sh/kernel/cpu/sh4a/clock-sh7757.c
index eedddad..3b097b0 100644
--- a/arch/sh/kernel/cpu/sh4a/clock-sh7757.c
+++ b/arch/sh/kernel/cpu/sh4a/clock-sh7757.c
@@ -101,8 +101,6 @@
[MSTP220] = SH_CLK_MSTP32(&div4_clks[DIV4_P], MSTPCR2, 20, 0),
};
-#define CLKDEV_CON_ID(_id, _clk) { .con_id = _id, .clk = _clk }
-
static struct clk_lookup lookups[] = {
/* main clocks */
CLKDEV_CON_ID("extal", &extal_clk),
@@ -116,33 +114,13 @@
/* MSTP32 clocks */
CLKDEV_CON_ID("sdhi0", &mstp_clks[MSTP004]),
CLKDEV_CON_ID("riic", &mstp_clks[MSTP000]),
- {
- /* TMU0 */
- .dev_id = "sh_tmu.0",
- .con_id = "tmu_fck",
- .clk = &mstp_clks[MSTP113],
- }, {
- /* TMU1 */
- .dev_id = "sh_tmu.1",
- .con_id = "tmu_fck",
- .clk = &mstp_clks[MSTP114],
- },
- {
- /* SCIF4 (But, ID is 2) */
- .dev_id = "sh-sci.2",
- .con_id = "sci_fck",
- .clk = &mstp_clks[MSTP112],
- }, {
- /* SCIF3 */
- .dev_id = "sh-sci.1",
- .con_id = "sci_fck",
- .clk = &mstp_clks[MSTP111],
- }, {
- /* SCIF2 */
- .dev_id = "sh-sci.0",
- .con_id = "sci_fck",
- .clk = &mstp_clks[MSTP110],
- },
+
+ CLKDEV_ICK_ID("tmu_fck", "sh_tmu.0", &mstp_clks[MSTP113]),
+ CLKDEV_ICK_ID("tmu_fck", "sh_tmu.1", &mstp_clks[MSTP114]),
+ CLKDEV_ICK_ID("sci_fck", "sh-sci.2", &mstp_clks[MSTP112]),
+ CLKDEV_ICK_ID("sci_fck", "sh-sci.1", &mstp_clks[MSTP111]),
+ CLKDEV_ICK_ID("sci_fck", "sh-sci.0", &mstp_clks[MSTP110]),
+
CLKDEV_CON_ID("usb0", &mstp_clks[MSTP102]),
CLKDEV_CON_ID("mmc0", &mstp_clks[MSTP220]),
};
diff --git a/arch/sh/kernel/cpu/sh4a/clock-sh7763.c b/arch/sh/kernel/cpu/sh4a/clock-sh7763.c
index 599630f..2d4c7fd 100644
--- a/arch/sh/kernel/cpu/sh4a/clock-sh7763.c
+++ b/arch/sh/kernel/cpu/sh4a/clock-sh7763.c
@@ -91,8 +91,6 @@
&sh7763_shyway_clk,
};
-#define CLKDEV_CON_ID(_id, _clk) { .con_id = _id, .clk = _clk }
-
static struct clk_lookup lookups[] = {
/* main clocks */
CLKDEV_CON_ID("shyway_clk", &sh7763_shyway_clk),
diff --git a/arch/sh/kernel/cpu/sh4a/clock-sh7780.c b/arch/sh/kernel/cpu/sh4a/clock-sh7780.c
index 8894926..3b53348 100644
--- a/arch/sh/kernel/cpu/sh4a/clock-sh7780.c
+++ b/arch/sh/kernel/cpu/sh4a/clock-sh7780.c
@@ -97,8 +97,6 @@
&sh7780_shyway_clk,
};
-#define CLKDEV_CON_ID(_id, _clk) { .con_id = _id, .clk = _clk }
-
static struct clk_lookup lookups[] = {
/* main clocks */
CLKDEV_CON_ID("shyway_clk", &sh7780_shyway_clk),
diff --git a/arch/sh/kernel/cpu/sh4a/clock-sh7785.c b/arch/sh/kernel/cpu/sh4a/clock-sh7785.c
index 2d96024..e5b420c 100644
--- a/arch/sh/kernel/cpu/sh4a/clock-sh7785.c
+++ b/arch/sh/kernel/cpu/sh4a/clock-sh7785.c
@@ -116,8 +116,6 @@
[MSTP100] = SH_CLK_MSTP32(NULL, MSTPCR1, 0, 0),
};
-#define CLKDEV_CON_ID(_id, _clk) { .con_id = _id, .clk = _clk }
-
static struct clk_lookup lookups[] = {
/* main clocks */
CLKDEV_CON_ID("extal", &extal_clk),
@@ -134,74 +132,27 @@
CLKDEV_CON_ID("cpu_clk", &div4_clks[DIV4_I]),
/* MSTP32 clocks */
- {
- /* SCIF5 */
- .dev_id = "sh-sci.5",
- .con_id = "sci_fck",
- .clk = &mstp_clks[MSTP029],
- }, {
- /* SCIF4 */
- .dev_id = "sh-sci.4",
- .con_id = "sci_fck",
- .clk = &mstp_clks[MSTP028],
- }, {
- /* SCIF3 */
- .dev_id = "sh-sci.3",
- .con_id = "sci_fck",
- .clk = &mstp_clks[MSTP027],
- }, {
- /* SCIF2 */
- .dev_id = "sh-sci.2",
- .con_id = "sci_fck",
- .clk = &mstp_clks[MSTP026],
- }, {
- /* SCIF1 */
- .dev_id = "sh-sci.1",
- .con_id = "sci_fck",
- .clk = &mstp_clks[MSTP025],
- }, {
- /* SCIF0 */
- .dev_id = "sh-sci.0",
- .con_id = "sci_fck",
- .clk = &mstp_clks[MSTP024],
- },
+ CLKDEV_ICK_ID("sci_fck", "sh-sci.5", &mstp_clks[MSTP029]),
+ CLKDEV_ICK_ID("sci_fck", "sh-sci.4", &mstp_clks[MSTP028]),
+ CLKDEV_ICK_ID("sci_fck", "sh-sci.3", &mstp_clks[MSTP027]),
+ CLKDEV_ICK_ID("sci_fck", "sh-sci.2", &mstp_clks[MSTP026]),
+ CLKDEV_ICK_ID("sci_fck", "sh-sci.1", &mstp_clks[MSTP025]),
+ CLKDEV_ICK_ID("sci_fck", "sh-sci.0", &mstp_clks[MSTP024]),
+
CLKDEV_CON_ID("ssi1_fck", &mstp_clks[MSTP021]),
CLKDEV_CON_ID("ssi0_fck", &mstp_clks[MSTP020]),
CLKDEV_CON_ID("hac1_fck", &mstp_clks[MSTP017]),
CLKDEV_CON_ID("hac0_fck", &mstp_clks[MSTP016]),
CLKDEV_CON_ID("mmcif_fck", &mstp_clks[MSTP013]),
CLKDEV_CON_ID("flctl_fck", &mstp_clks[MSTP012]),
- {
- /* TMU0 */
- .dev_id = "sh_tmu.0",
- .con_id = "tmu_fck",
- .clk = &mstp_clks[MSTP008],
- }, {
- /* TMU1 */
- .dev_id = "sh_tmu.1",
- .con_id = "tmu_fck",
- .clk = &mstp_clks[MSTP008],
- }, {
- /* TMU2 */
- .dev_id = "sh_tmu.2",
- .con_id = "tmu_fck",
- .clk = &mstp_clks[MSTP008],
- }, {
- /* TMU3 */
- .dev_id = "sh_tmu.3",
- .con_id = "tmu_fck",
- .clk = &mstp_clks[MSTP009],
- }, {
- /* TMU4 */
- .dev_id = "sh_tmu.4",
- .con_id = "tmu_fck",
- .clk = &mstp_clks[MSTP009],
- }, {
- /* TMU5 */
- .dev_id = "sh_tmu.5",
- .con_id = "tmu_fck",
- .clk = &mstp_clks[MSTP009],
- },
+
+ CLKDEV_ICK_ID("tmu_fck", "sh_tmu.0", &mstp_clks[MSTP008]),
+ CLKDEV_ICK_ID("tmu_fck", "sh_tmu.1", &mstp_clks[MSTP008]),
+ CLKDEV_ICK_ID("tmu_fck", "sh_tmu.2", &mstp_clks[MSTP008]),
+ CLKDEV_ICK_ID("tmu_fck", "sh_tmu.3", &mstp_clks[MSTP009]),
+ CLKDEV_ICK_ID("tmu_fck", "sh_tmu.4", &mstp_clks[MSTP009]),
+ CLKDEV_ICK_ID("tmu_fck", "sh_tmu.5", &mstp_clks[MSTP009]),
+
CLKDEV_CON_ID("siof_fck", &mstp_clks[MSTP003]),
CLKDEV_CON_ID("hspi_fck", &mstp_clks[MSTP002]),
CLKDEV_CON_ID("hudi_fck", &mstp_clks[MSTP119]),
diff --git a/arch/sh/kernel/cpu/sh4a/clock-sh7786.c b/arch/sh/kernel/cpu/sh4a/clock-sh7786.c
index 42e403b..f6c0c3d 100644
--- a/arch/sh/kernel/cpu/sh4a/clock-sh7786.c
+++ b/arch/sh/kernel/cpu/sh4a/clock-sh7786.c
@@ -125,8 +125,6 @@
[MSTP102] = SH_CLK_MSTP32(NULL, MSTPCR1, 2, 0),
};
-#define CLKDEV_CON_ID(_id, _clk) { .con_id = _id, .clk = _clk }
-
static struct clk_lookup lookups[] = {
/* main clocks */
CLKDEV_CON_ID("extal", &extal_clk),
@@ -141,37 +139,13 @@
CLKDEV_CON_ID("cpu_clk", &div4_clks[DIV4_I]),
/* MSTP32 clocks */
- {
- /* SCIF5 */
- .dev_id = "sh-sci.5",
- .con_id = "sci_fck",
- .clk = &mstp_clks[MSTP029],
- }, {
- /* SCIF4 */
- .dev_id = "sh-sci.4",
- .con_id = "sci_fck",
- .clk = &mstp_clks[MSTP028],
- }, {
- /* SCIF3 */
- .dev_id = "sh-sci.3",
- .con_id = "sci_fck",
- .clk = &mstp_clks[MSTP027],
- }, {
- /* SCIF2 */
- .dev_id = "sh-sci.2",
- .con_id = "sci_fck",
- .clk = &mstp_clks[MSTP026],
- }, {
- /* SCIF1 */
- .dev_id = "sh-sci.1",
- .con_id = "sci_fck",
- .clk = &mstp_clks[MSTP025],
- }, {
- /* SCIF0 */
- .dev_id = "sh-sci.0",
- .con_id = "sci_fck",
- .clk = &mstp_clks[MSTP024],
- },
+ CLKDEV_ICK_ID("sci_fck", "sh-sci.5", &mstp_clks[MSTP029]),
+ CLKDEV_ICK_ID("sci_fck", "sh-sci.4", &mstp_clks[MSTP028]),
+ CLKDEV_ICK_ID("sci_fck", "sh-sci.3", &mstp_clks[MSTP027]),
+ CLKDEV_ICK_ID("sci_fck", "sh-sci.2", &mstp_clks[MSTP026]),
+ CLKDEV_ICK_ID("sci_fck", "sh-sci.1", &mstp_clks[MSTP025]),
+ CLKDEV_ICK_ID("sci_fck", "sh-sci.0", &mstp_clks[MSTP024]),
+
CLKDEV_CON_ID("ssi3_fck", &mstp_clks[MSTP023]),
CLKDEV_CON_ID("ssi2_fck", &mstp_clks[MSTP022]),
CLKDEV_CON_ID("ssi1_fck", &mstp_clks[MSTP021]),
@@ -180,67 +154,20 @@
CLKDEV_CON_ID("hac0_fck", &mstp_clks[MSTP016]),
CLKDEV_CON_ID("i2c1_fck", &mstp_clks[MSTP015]),
CLKDEV_CON_ID("i2c0_fck", &mstp_clks[MSTP014]),
- {
- /* TMU0 */
- .dev_id = "sh_tmu.0",
- .con_id = "tmu_fck",
- .clk = &mstp_clks[MSTP008],
- }, {
- /* TMU1 */
- .dev_id = "sh_tmu.1",
- .con_id = "tmu_fck",
- .clk = &mstp_clks[MSTP008],
- }, {
- /* TMU2 */
- .dev_id = "sh_tmu.2",
- .con_id = "tmu_fck",
- .clk = &mstp_clks[MSTP008],
- }, {
- /* TMU3 */
- .dev_id = "sh_tmu.3",
- .con_id = "tmu_fck",
- .clk = &mstp_clks[MSTP009],
- }, {
- /* TMU4 */
- .dev_id = "sh_tmu.4",
- .con_id = "tmu_fck",
- .clk = &mstp_clks[MSTP009],
- }, {
- /* TMU5 */
- .dev_id = "sh_tmu.5",
- .con_id = "tmu_fck",
- .clk = &mstp_clks[MSTP009],
- }, {
- /* TMU6 */
- .dev_id = "sh_tmu.6",
- .con_id = "tmu_fck",
- .clk = &mstp_clks[MSTP010],
- }, {
- /* TMU7 */
- .dev_id = "sh_tmu.7",
- .con_id = "tmu_fck",
- .clk = &mstp_clks[MSTP010],
- }, {
- /* TMU8 */
- .dev_id = "sh_tmu.8",
- .con_id = "tmu_fck",
- .clk = &mstp_clks[MSTP010],
- }, {
- /* TMU9 */
- .dev_id = "sh_tmu.9",
- .con_id = "tmu_fck",
- .clk = &mstp_clks[MSTP011],
- }, {
- /* TMU10 */
- .dev_id = "sh_tmu.10",
- .con_id = "tmu_fck",
- .clk = &mstp_clks[MSTP011],
- }, {
- /* TMU11 */
- .dev_id = "sh_tmu.11",
- .con_id = "tmu_fck",
- .clk = &mstp_clks[MSTP011],
- },
+
+ CLKDEV_ICK_ID("tmu_fck", "sh_tmu.0", &mstp_clks[MSTP008]),
+ CLKDEV_ICK_ID("tmu_fck", "sh_tmu.1", &mstp_clks[MSTP008]),
+ CLKDEV_ICK_ID("tmu_fck", "sh_tmu.2", &mstp_clks[MSTP008]),
+ CLKDEV_ICK_ID("tmu_fck", "sh_tmu.3", &mstp_clks[MSTP009]),
+ CLKDEV_ICK_ID("tmu_fck", "sh_tmu.4", &mstp_clks[MSTP009]),
+ CLKDEV_ICK_ID("tmu_fck", "sh_tmu.5", &mstp_clks[MSTP009]),
+ CLKDEV_ICK_ID("tmu_fck", "sh_tmu.6", &mstp_clks[MSTP010]),
+ CLKDEV_ICK_ID("tmu_fck", "sh_tmu.7", &mstp_clks[MSTP010]),
+ CLKDEV_ICK_ID("tmu_fck", "sh_tmu.8", &mstp_clks[MSTP010]),
+ CLKDEV_ICK_ID("tmu_fck", "sh_tmu.9", &mstp_clks[MSTP011]),
+ CLKDEV_ICK_ID("tmu_fck", "sh_tmu.10", &mstp_clks[MSTP011]),
+ CLKDEV_ICK_ID("tmu_fck", "sh_tmu.11", &mstp_clks[MSTP011]),
+
CLKDEV_CON_ID("sdif1_fck", &mstp_clks[MSTP005]),
CLKDEV_CON_ID("sdif0_fck", &mstp_clks[MSTP004]),
CLKDEV_CON_ID("hspi_fck", &mstp_clks[MSTP002]),
diff --git a/arch/sh/kernel/cpu/sh4a/clock-shx3.c b/arch/sh/kernel/cpu/sh4a/clock-shx3.c
index 1afdb93..bf2d00b 100644
--- a/arch/sh/kernel/cpu/sh4a/clock-shx3.c
+++ b/arch/sh/kernel/cpu/sh4a/clock-shx3.c
@@ -100,8 +100,6 @@
[MSTP104] = SH_CLK_MSTP32(NULL, MSTPCR1, 4, 0),
};
-#define CLKDEV_CON_ID(_id, _clk) { .con_id = _id, .clk = _clk }
-
static struct clk_lookup lookups[] = {
/* main clocks */
CLKDEV_CON_ID("extal", &extal_clk),
@@ -116,62 +114,23 @@
CLKDEV_CON_ID("cpu_clk", &div4_clks[DIV4_I]),
/* MSTP32 clocks */
- {
- /* SCIF3 */
- .dev_id = "sh-sci.3",
- .con_id = "sci_fck",
- .clk = &mstp_clks[MSTP027],
- }, {
- /* SCIF2 */
- .dev_id = "sh-sci.2",
- .con_id = "sci_fck",
- .clk = &mstp_clks[MSTP026],
- }, {
- /* SCIF1 */
- .dev_id = "sh-sci.1",
- .con_id = "sci_fck",
- .clk = &mstp_clks[MSTP025],
- }, {
- /* SCIF0 */
- .dev_id = "sh-sci.0",
- .con_id = "sci_fck",
- .clk = &mstp_clks[MSTP024],
- },
+ CLKDEV_ICK_ID("sci_fck", "sh-sci.3", &mstp_clks[MSTP027]),
+ CLKDEV_ICK_ID("sci_fck", "sh-sci.2", &mstp_clks[MSTP026]),
+ CLKDEV_ICK_ID("sci_fck", "sh-sci.1", &mstp_clks[MSTP025]),
+ CLKDEV_ICK_ID("sci_fck", "sh-sci.0", &mstp_clks[MSTP024]),
+
CLKDEV_CON_ID("h8ex_fck", &mstp_clks[MSTP003]),
CLKDEV_CON_ID("csm_fck", &mstp_clks[MSTP002]),
CLKDEV_CON_ID("fe1_fck", &mstp_clks[MSTP001]),
CLKDEV_CON_ID("fe0_fck", &mstp_clks[MSTP000]),
- {
- /* TMU0 */
- .dev_id = "sh_tmu.0",
- .con_id = "tmu_fck",
- .clk = &mstp_clks[MSTP008],
- }, {
- /* TMU1 */
- .dev_id = "sh_tmu.1",
- .con_id = "tmu_fck",
- .clk = &mstp_clks[MSTP008],
- }, {
- /* TMU2 */
- .dev_id = "sh_tmu.2",
- .con_id = "tmu_fck",
- .clk = &mstp_clks[MSTP008],
- }, {
- /* TMU3 */
- .dev_id = "sh_tmu.3",
- .con_id = "tmu_fck",
- .clk = &mstp_clks[MSTP009],
- }, {
- /* TMU4 */
- .dev_id = "sh_tmu.4",
- .con_id = "tmu_fck",
- .clk = &mstp_clks[MSTP009],
- }, {
- /* TMU5 */
- .dev_id = "sh_tmu.5",
- .con_id = "tmu_fck",
- .clk = &mstp_clks[MSTP009],
- },
+
+ CLKDEV_ICK_ID("tmu_fck", "sh_tmu.0", &mstp_clks[MSTP008]),
+ CLKDEV_ICK_ID("tmu_fck", "sh_tmu.1", &mstp_clks[MSTP008]),
+ CLKDEV_ICK_ID("tmu_fck", "sh_tmu.2", &mstp_clks[MSTP008]),
+ CLKDEV_ICK_ID("tmu_fck", "sh_tmu.3", &mstp_clks[MSTP009]),
+ CLKDEV_ICK_ID("tmu_fck", "sh_tmu.4", &mstp_clks[MSTP009]),
+ CLKDEV_ICK_ID("tmu_fck", "sh_tmu.5", &mstp_clks[MSTP009]),
+
CLKDEV_CON_ID("hudi_fck", &mstp_clks[MSTP119]),
CLKDEV_CON_ID("dmac_11_6_fck", &mstp_clks[MSTP105]),
CLKDEV_CON_ID("dmac_5_0_fck", &mstp_clks[MSTP104]),
diff --git a/arch/sh/kernel/cpu/sh4a/serial-sh7722.c b/arch/sh/kernel/cpu/sh4a/serial-sh7722.c
new file mode 100644
index 0000000..59bc3a7
--- /dev/null
+++ b/arch/sh/kernel/cpu/sh4a/serial-sh7722.c
@@ -0,0 +1,23 @@
+#include <linux/serial_sci.h>
+#include <linux/serial_core.h>
+#include <linux/io.h>
+
+#define PSCR 0xA405011E
+
+static void sh7722_sci_init_pins(struct uart_port *port, unsigned int cflag)
+{
+ unsigned short data;
+
+ if (port->mapbase == 0xffe00000) {
+ data = __raw_readw(PSCR);
+ data &= ~0x03cf;
+ if (!(cflag & CRTSCTS))
+ data |= 0x0340;
+
+ __raw_writew(data, PSCR);
+ }
+}
+
+struct plat_sci_port_ops sh7722_sci_port_ops = {
+ .init_pins = sh7722_sci_init_pins,
+};
diff --git a/arch/sh/kernel/cpu/sh4a/setup-sh7366.c b/arch/sh/kernel/cpu/sh4a/setup-sh7366.c
index 82616af..8777386 100644
--- a/arch/sh/kernel/cpu/sh4a/setup-sh7366.c
+++ b/arch/sh/kernel/cpu/sh4a/setup-sh7366.c
@@ -20,6 +20,7 @@
static struct plat_sci_port scif0_platform_data = {
.mapbase = 0xffe00000,
+ .port_reg = 0xa405013e,
.flags = UPF_BOOT_AUTOCONF,
.scscr = SCSCR_RE | SCSCR_TE | SCSCR_REIE,
.scbrr_algo_id = SCBRR_ALGO_2,
diff --git a/arch/sh/kernel/cpu/sh4a/setup-sh7722.c b/arch/sh/kernel/cpu/sh4a/setup-sh7722.c
index 5813d80..278a0e5 100644
--- a/arch/sh/kernel/cpu/sh4a/setup-sh7722.c
+++ b/arch/sh/kernel/cpu/sh4a/setup-sh7722.c
@@ -22,6 +22,7 @@
#include <cpu/dma-register.h>
#include <cpu/sh7722.h>
+#include <cpu/serial.h>
static const struct sh_dmae_slave_config sh7722_dmae_slaves[] = {
{
@@ -185,6 +186,8 @@
.scbrr_algo_id = SCBRR_ALGO_2,
.type = PORT_SCIF,
.irqs = { 80, 80, 80, 80 },
+ .ops = &sh7722_sci_port_ops,
+ .regtype = SCIx_SH4_SCIF_NO_SCSPTR_REGTYPE,
};
static struct platform_device scif0_device = {
@@ -202,6 +205,8 @@
.scbrr_algo_id = SCBRR_ALGO_2,
.type = PORT_SCIF,
.irqs = { 81, 81, 81, 81 },
+ .ops = &sh7722_sci_port_ops,
+ .regtype = SCIx_SH4_SCIF_NO_SCSPTR_REGTYPE,
};
static struct platform_device scif1_device = {
@@ -219,6 +224,8 @@
.scbrr_algo_id = SCBRR_ALGO_2,
.type = PORT_SCIF,
.irqs = { 82, 82, 82, 82 },
+ .ops = &sh7722_sci_port_ops,
+ .regtype = SCIx_SH4_SCIF_NO_SCSPTR_REGTYPE,
};
static struct platform_device scif2_device = {
diff --git a/arch/sh/kernel/cpu/sh4a/setup-sh7723.c b/arch/sh/kernel/cpu/sh4a/setup-sh7723.c
index 0723822..3c2810d 100644
--- a/arch/sh/kernel/cpu/sh4a/setup-sh7723.c
+++ b/arch/sh/kernel/cpu/sh4a/setup-sh7723.c
@@ -23,11 +23,13 @@
/* Serial */
static struct plat_sci_port scif0_platform_data = {
.mapbase = 0xffe00000,
+ .port_reg = 0xa4050160,
.flags = UPF_BOOT_AUTOCONF,
.scscr = SCSCR_RE | SCSCR_TE | SCSCR_REIE,
.scbrr_algo_id = SCBRR_ALGO_2,
.type = PORT_SCIF,
.irqs = { 80, 80, 80, 80 },
+ .regtype = SCIx_SH4_SCIF_NO_SCSPTR_REGTYPE,
};
static struct platform_device scif0_device = {
@@ -40,11 +42,13 @@
static struct plat_sci_port scif1_platform_data = {
.mapbase = 0xffe10000,
+ .port_reg = SCIx_NOT_SUPPORTED,
.flags = UPF_BOOT_AUTOCONF,
.scscr = SCSCR_RE | SCSCR_TE | SCSCR_REIE,
.scbrr_algo_id = SCBRR_ALGO_2,
.type = PORT_SCIF,
.irqs = { 81, 81, 81, 81 },
+ .regtype = SCIx_SH4_SCIF_NO_SCSPTR_REGTYPE,
};
static struct platform_device scif1_device = {
@@ -57,11 +61,13 @@
static struct plat_sci_port scif2_platform_data = {
.mapbase = 0xffe20000,
+ .port_reg = SCIx_NOT_SUPPORTED,
.flags = UPF_BOOT_AUTOCONF,
.scscr = SCSCR_RE | SCSCR_TE | SCSCR_REIE,
.scbrr_algo_id = SCBRR_ALGO_2,
.type = PORT_SCIF,
.irqs = { 82, 82, 82, 82 },
+ .regtype = SCIx_SH4_SCIF_NO_SCSPTR_REGTYPE,
};
static struct platform_device scif2_device = {
@@ -75,6 +81,7 @@
static struct plat_sci_port scif3_platform_data = {
.mapbase = 0xa4e30000,
.flags = UPF_BOOT_AUTOCONF,
+ .port_reg = SCIx_NOT_SUPPORTED,
.scscr = SCSCR_RE | SCSCR_TE | SCSCR_REIE,
.scbrr_algo_id = SCBRR_ALGO_3,
.type = PORT_SCIFA,
@@ -91,6 +98,7 @@
static struct plat_sci_port scif4_platform_data = {
.mapbase = 0xa4e40000,
+ .port_reg = SCIx_NOT_SUPPORTED,
.flags = UPF_BOOT_AUTOCONF,
.scscr = SCSCR_RE | SCSCR_TE | SCSCR_REIE,
.scbrr_algo_id = SCBRR_ALGO_3,
@@ -108,6 +116,7 @@
static struct plat_sci_port scif5_platform_data = {
.mapbase = 0xa4e50000,
+ .port_reg = SCIx_NOT_SUPPORTED,
.flags = UPF_BOOT_AUTOCONF,
.scscr = SCSCR_RE | SCSCR_TE | SCSCR_REIE,
.scbrr_algo_id = SCBRR_ALGO_3,
diff --git a/arch/sh/kernel/cpu/sh4a/setup-sh7724.c b/arch/sh/kernel/cpu/sh4a/setup-sh7724.c
index 134a397..a37dd72 100644
--- a/arch/sh/kernel/cpu/sh4a/setup-sh7724.c
+++ b/arch/sh/kernel/cpu/sh4a/setup-sh7724.c
@@ -296,11 +296,13 @@
/* Serial */
static struct plat_sci_port scif0_platform_data = {
.mapbase = 0xffe00000,
+ .port_reg = SCIx_NOT_SUPPORTED,
.flags = UPF_BOOT_AUTOCONF,
.scscr = SCSCR_RE | SCSCR_TE | SCSCR_REIE,
.scbrr_algo_id = SCBRR_ALGO_2,
.type = PORT_SCIF,
.irqs = { 80, 80, 80, 80 },
+ .regtype = SCIx_SH4_SCIF_NO_SCSPTR_REGTYPE,
};
static struct platform_device scif0_device = {
@@ -313,11 +315,13 @@
static struct plat_sci_port scif1_platform_data = {
.mapbase = 0xffe10000,
+ .port_reg = SCIx_NOT_SUPPORTED,
.flags = UPF_BOOT_AUTOCONF,
.scscr = SCSCR_RE | SCSCR_TE | SCSCR_REIE,
.scbrr_algo_id = SCBRR_ALGO_2,
.type = PORT_SCIF,
.irqs = { 81, 81, 81, 81 },
+ .regtype = SCIx_SH4_SCIF_NO_SCSPTR_REGTYPE,
};
static struct platform_device scif1_device = {
@@ -330,11 +334,13 @@
static struct plat_sci_port scif2_platform_data = {
.mapbase = 0xffe20000,
+ .port_reg = SCIx_NOT_SUPPORTED,
.flags = UPF_BOOT_AUTOCONF,
.scscr = SCSCR_RE | SCSCR_TE | SCSCR_REIE,
.scbrr_algo_id = SCBRR_ALGO_2,
.type = PORT_SCIF,
.irqs = { 82, 82, 82, 82 },
+ .regtype = SCIx_SH4_SCIF_NO_SCSPTR_REGTYPE,
};
static struct platform_device scif2_device = {
@@ -347,6 +353,7 @@
static struct plat_sci_port scif3_platform_data = {
.mapbase = 0xa4e30000,
+ .port_reg = SCIx_NOT_SUPPORTED,
.flags = UPF_BOOT_AUTOCONF,
.scscr = SCSCR_RE | SCSCR_TE,
.scbrr_algo_id = SCBRR_ALGO_3,
@@ -364,6 +371,7 @@
static struct plat_sci_port scif4_platform_data = {
.mapbase = 0xa4e40000,
+ .port_reg = SCIx_NOT_SUPPORTED,
.flags = UPF_BOOT_AUTOCONF,
.scscr = SCSCR_RE | SCSCR_TE,
.scbrr_algo_id = SCBRR_ALGO_3,
@@ -381,6 +389,7 @@
static struct plat_sci_port scif5_platform_data = {
.mapbase = 0xa4e50000,
+ .port_reg = SCIx_NOT_SUPPORTED,
.flags = UPF_BOOT_AUTOCONF,
.scscr = SCSCR_RE | SCSCR_TE,
.scbrr_algo_id = SCBRR_ALGO_3,
diff --git a/arch/sh/kernel/cpu/sh4a/setup-sh7763.c b/arch/sh/kernel/cpu/sh4a/setup-sh7763.c
index 593eca6..0011351 100644
--- a/arch/sh/kernel/cpu/sh4a/setup-sh7763.c
+++ b/arch/sh/kernel/cpu/sh4a/setup-sh7763.c
@@ -23,6 +23,7 @@
.scbrr_algo_id = SCBRR_ALGO_2,
.type = PORT_SCIF,
.irqs = { 40, 40, 40, 40 },
+ .regtype = SCIx_SH4_SCIF_FIFODATA_REGTYPE,
};
static struct platform_device scif0_device = {
@@ -40,6 +41,7 @@
.scbrr_algo_id = SCBRR_ALGO_2,
.type = PORT_SCIF,
.irqs = { 76, 76, 76, 76 },
+ .regtype = SCIx_SH4_SCIF_FIFODATA_REGTYPE,
};
static struct platform_device scif1_device = {
@@ -57,6 +59,7 @@
.scbrr_algo_id = SCBRR_ALGO_2,
.type = PORT_SCIF,
.irqs = { 104, 104, 104, 104 },
+ .regtype = SCIx_SH4_SCIF_FIFODATA_REGTYPE,
};
static struct platform_device scif2_device = {
diff --git a/arch/sh/kernel/cpu/sh4a/setup-sh7780.c b/arch/sh/kernel/cpu/sh4a/setup-sh7780.c
index 08add7f..3d4d207 100644
--- a/arch/sh/kernel/cpu/sh4a/setup-sh7780.c
+++ b/arch/sh/kernel/cpu/sh4a/setup-sh7780.c
@@ -14,7 +14,6 @@
#include <linux/serial_sci.h>
#include <linux/sh_dma.h>
#include <linux/sh_timer.h>
-
#include <cpu/dma-register.h>
static struct plat_sci_port scif0_platform_data = {
@@ -24,6 +23,7 @@
.scbrr_algo_id = SCBRR_ALGO_1,
.type = PORT_SCIF,
.irqs = { 40, 40, 40, 40 },
+ .regtype = SCIx_SH4_SCIF_FIFODATA_REGTYPE,
};
static struct platform_device scif0_device = {
@@ -41,6 +41,7 @@
.scbrr_algo_id = SCBRR_ALGO_1,
.type = PORT_SCIF,
.irqs = { 76, 76, 76, 76 },
+ .regtype = SCIx_SH4_SCIF_FIFODATA_REGTYPE,
};
static struct platform_device scif1_device = {
diff --git a/arch/sh/kernel/cpu/sh4a/setup-sh7785.c b/arch/sh/kernel/cpu/sh4a/setup-sh7785.c
index 18d8fc1..b29e634 100644
--- a/arch/sh/kernel/cpu/sh4a/setup-sh7785.c
+++ b/arch/sh/kernel/cpu/sh4a/setup-sh7785.c
@@ -15,9 +15,7 @@
#include <linux/mm.h>
#include <linux/sh_dma.h>
#include <linux/sh_timer.h>
-
#include <asm/mmzone.h>
-
#include <cpu/dma-register.h>
static struct plat_sci_port scif0_platform_data = {
@@ -27,6 +25,7 @@
.scbrr_algo_id = SCBRR_ALGO_1,
.type = PORT_SCIF,
.irqs = { 40, 40, 40, 40 },
+ .regtype = SCIx_SH4_SCIF_FIFODATA_REGTYPE,
};
static struct platform_device scif0_device = {
@@ -44,6 +43,7 @@
.scbrr_algo_id = SCBRR_ALGO_1,
.type = PORT_SCIF,
.irqs = { 44, 44, 44, 44 },
+ .regtype = SCIx_SH4_SCIF_FIFODATA_REGTYPE,
};
static struct platform_device scif1_device = {
@@ -61,6 +61,7 @@
.scbrr_algo_id = SCBRR_ALGO_1,
.type = PORT_SCIF,
.irqs = { 60, 60, 60, 60 },
+ .regtype = SCIx_SH4_SCIF_FIFODATA_REGTYPE,
};
static struct platform_device scif2_device = {
@@ -78,6 +79,7 @@
.scbrr_algo_id = SCBRR_ALGO_1,
.type = PORT_SCIF,
.irqs = { 61, 61, 61, 61 },
+ .regtype = SCIx_SH4_SCIF_FIFODATA_REGTYPE,
};
static struct platform_device scif3_device = {
@@ -95,6 +97,7 @@
.scbrr_algo_id = SCBRR_ALGO_1,
.type = PORT_SCIF,
.irqs = { 62, 62, 62, 62 },
+ .regtype = SCIx_SH4_SCIF_FIFODATA_REGTYPE,
};
static struct platform_device scif4_device = {
@@ -112,6 +115,7 @@
.scbrr_algo_id = SCBRR_ALGO_1,
.type = PORT_SCIF,
.irqs = { 63, 63, 63, 63 },
+ .regtype = SCIx_SH4_SCIF_FIFODATA_REGTYPE,
};
static struct platform_device scif5_device = {
diff --git a/arch/sh/kernel/cpu/sh4a/setup-sh7786.c b/arch/sh/kernel/cpu/sh4a/setup-sh7786.c
index beba32b..dd5e709 100644
--- a/arch/sh/kernel/cpu/sh4a/setup-sh7786.c
+++ b/arch/sh/kernel/cpu/sh4a/setup-sh7786.c
@@ -1,7 +1,7 @@
/*
* SH7786 Setup
*
- * Copyright (C) 2009 - 2010 Renesas Solutions Corp.
+ * Copyright (C) 2009 - 2011 Renesas Solutions Corp.
* Kuninori Morimoto <morimoto.kuninori@renesas.com>
* Paul Mundt <paul.mundt@renesas.com>
*
@@ -33,6 +33,7 @@
.scbrr_algo_id = SCBRR_ALGO_1,
.type = PORT_SCIF,
.irqs = { 40, 41, 43, 42 },
+ .regtype = SCIx_SH4_SCIF_FIFODATA_REGTYPE,
};
static struct platform_device scif0_device = {
@@ -53,6 +54,7 @@
.scbrr_algo_id = SCBRR_ALGO_1,
.type = PORT_SCIF,
.irqs = { 44, 44, 44, 44 },
+ .regtype = SCIx_SH4_SCIF_FIFODATA_REGTYPE,
};
static struct platform_device scif1_device = {
@@ -70,6 +72,7 @@
.scbrr_algo_id = SCBRR_ALGO_1,
.type = PORT_SCIF,
.irqs = { 50, 50, 50, 50 },
+ .regtype = SCIx_SH4_SCIF_FIFODATA_REGTYPE,
};
static struct platform_device scif2_device = {
@@ -87,6 +90,7 @@
.scbrr_algo_id = SCBRR_ALGO_1,
.type = PORT_SCIF,
.irqs = { 51, 51, 51, 51 },
+ .regtype = SCIx_SH4_SCIF_FIFODATA_REGTYPE,
};
static struct platform_device scif3_device = {
@@ -104,6 +108,7 @@
.scbrr_algo_id = SCBRR_ALGO_1,
.type = PORT_SCIF,
.irqs = { 52, 52, 52, 52 },
+ .regtype = SCIx_SH4_SCIF_FIFODATA_REGTYPE,
};
static struct platform_device scif4_device = {
@@ -121,6 +126,7 @@
.scbrr_algo_id = SCBRR_ALGO_1,
.type = PORT_SCIF,
.irqs = { 53, 53, 53, 53 },
+ .regtype = SCIx_SH4_SCIF_FIFODATA_REGTYPE,
};
static struct platform_device scif5_device = {
diff --git a/arch/sh/kernel/idle.c b/arch/sh/kernel/idle.c
index 84db0d6..32114e0 100644
--- a/arch/sh/kernel/idle.c
+++ b/arch/sh/kernel/idle.c
@@ -16,12 +16,13 @@
#include <linux/thread_info.h>
#include <linux/irqflags.h>
#include <linux/smp.h>
+#include <linux/cpuidle.h>
#include <asm/pgalloc.h>
#include <asm/system.h>
#include <linux/atomic.h>
#include <asm/smp.h>
-void (*pm_idle)(void) = NULL;
+static void (*pm_idle)(void);
static int hlt_counter;
@@ -100,7 +101,8 @@
local_irq_disable();
/* Don't trace irqs off for idle */
stop_critical_timings();
- pm_idle();
+ if (cpuidle_idle_call())
+ pm_idle();
/*
* Sanity check to ensure that pm_idle() returns
* with IRQs enabled
diff --git a/arch/sparc/Kconfig b/arch/sparc/Kconfig
index 1074ddd..1a6f20d 100644
--- a/arch/sparc/Kconfig
+++ b/arch/sparc/Kconfig
@@ -54,6 +54,8 @@
select HAVE_PERF_EVENTS
select PERF_USE_VMALLOC
select IRQ_PREFLOW_FASTEOI
+ select ARCH_HAVE_NMI_SAFE_CMPXCHG
+ select HAVE_C_RECORDMCOUNT
config ARCH_DEFCONFIG
string
diff --git a/arch/sparc/include/asm/Kbuild b/arch/sparc/include/asm/Kbuild
index 3c93f08..2c2e388 100644
--- a/arch/sparc/include/asm/Kbuild
+++ b/arch/sparc/include/asm/Kbuild
@@ -16,3 +16,8 @@
header-y += uctx.h
header-y += utrap.h
header-y += watchdog.h
+
+generic-y += div64.h
+generic-y += local64.h
+generic-y += irq_regs.h
+generic-y += local.h
diff --git a/arch/sparc/include/asm/bitops_64.h b/arch/sparc/include/asm/bitops_64.h
index 325e295..29011cc 100644
--- a/arch/sparc/include/asm/bitops_64.h
+++ b/arch/sparc/include/asm/bitops_64.h
@@ -26,61 +26,28 @@
#define smp_mb__before_clear_bit() barrier()
#define smp_mb__after_clear_bit() barrier()
-#include <asm-generic/bitops/ffz.h>
-#include <asm-generic/bitops/__ffs.h>
#include <asm-generic/bitops/fls.h>
#include <asm-generic/bitops/__fls.h>
#include <asm-generic/bitops/fls64.h>
#ifdef __KERNEL__
+extern int ffs(int x);
+extern unsigned long __ffs(unsigned long);
+
+#include <asm-generic/bitops/ffz.h>
#include <asm-generic/bitops/sched.h>
-#include <asm-generic/bitops/ffs.h>
/*
* hweightN: returns the hamming weight (i.e. the number
* of bits set) of a N-bit word
*/
-#ifdef ULTRA_HAS_POPULATION_COUNT
+extern unsigned long __arch_hweight64(__u64 w);
+extern unsigned int __arch_hweight32(unsigned int w);
+extern unsigned int __arch_hweight16(unsigned int w);
+extern unsigned int __arch_hweight8(unsigned int w);
-static inline unsigned int __arch_hweight64(unsigned long w)
-{
- unsigned int res;
-
- __asm__ ("popc %1,%0" : "=r" (res) : "r" (w));
- return res;
-}
-
-static inline unsigned int __arch_hweight32(unsigned int w)
-{
- unsigned int res;
-
- __asm__ ("popc %1,%0" : "=r" (res) : "r" (w & 0xffffffff));
- return res;
-}
-
-static inline unsigned int __arch_hweight16(unsigned int w)
-{
- unsigned int res;
-
- __asm__ ("popc %1,%0" : "=r" (res) : "r" (w & 0xffff));
- return res;
-}
-
-static inline unsigned int __arch_hweight8(unsigned int w)
-{
- unsigned int res;
-
- __asm__ ("popc %1,%0" : "=r" (res) : "r" (w & 0xff));
- return res;
-}
-
-#else
-
-#include <asm-generic/bitops/arch_hweight.h>
-
-#endif
#include <asm-generic/bitops/const_hweight.h>
#include <asm-generic/bitops/lock.h>
#endif /* __KERNEL__ */
diff --git a/arch/sparc/include/asm/div64.h b/arch/sparc/include/asm/div64.h
deleted file mode 100644
index 6cd978c..0000000
--- a/arch/sparc/include/asm/div64.h
+++ /dev/null
@@ -1 +0,0 @@
-#include <asm-generic/div64.h>
diff --git a/arch/sparc/include/asm/elf_64.h b/arch/sparc/include/asm/elf_64.h
index 64f7a00..7df8b7f 100644
--- a/arch/sparc/include/asm/elf_64.h
+++ b/arch/sparc/include/asm/elf_64.h
@@ -59,15 +59,33 @@
#define R_SPARC_6 45
/* Bits present in AT_HWCAP, primarily for Sparc32. */
+#define HWCAP_SPARC_FLUSH 0x00000001
+#define HWCAP_SPARC_STBAR 0x00000002
+#define HWCAP_SPARC_SWAP 0x00000004
+#define HWCAP_SPARC_MULDIV 0x00000008
+#define HWCAP_SPARC_V9 0x00000010
+#define HWCAP_SPARC_ULTRA3 0x00000020
+#define HWCAP_SPARC_BLKINIT 0x00000040
+#define HWCAP_SPARC_N2 0x00000080
-#define HWCAP_SPARC_FLUSH 1 /* CPU supports flush instruction. */
-#define HWCAP_SPARC_STBAR 2
-#define HWCAP_SPARC_SWAP 4
-#define HWCAP_SPARC_MULDIV 8
-#define HWCAP_SPARC_V9 16
-#define HWCAP_SPARC_ULTRA3 32
-#define HWCAP_SPARC_BLKINIT 64
-#define HWCAP_SPARC_N2 128
+/* Solaris compatible AT_HWCAP bits. */
+#define AV_SPARC_MUL32 0x00000100 /* 32x32 multiply is efficient */
+#define AV_SPARC_DIV32 0x00000200 /* 32x32 divide is efficient */
+#define AV_SPARC_FSMULD 0x00000400 /* 'fsmuld' is efficient */
+#define AV_SPARC_V8PLUS 0x00000800 /* v9 insn available to 32bit */
+#define AV_SPARC_POPC 0x00001000 /* 'popc' is efficient */
+#define AV_SPARC_VIS 0x00002000 /* VIS insns available */
+#define AV_SPARC_VIS2 0x00004000 /* VIS2 insns available */
+#define AV_SPARC_ASI_BLK_INIT 0x00008000 /* block init ASIs available */
+#define AV_SPARC_FMAF 0x00010000 /* fused multiply-add */
+#define AV_SPARC_VIS3 0x00020000 /* VIS3 insns available */
+#define AV_SPARC_HPC 0x00040000 /* HPC insns available */
+#define AV_SPARC_RANDOM 0x00080000 /* 'random' insn available */
+#define AV_SPARC_TRANS 0x00100000 /* transaction insns available */
+#define AV_SPARC_FJFMAU 0x00200000 /* unfused multiply-add */
+#define AV_SPARC_IMA 0x00400000 /* integer multiply-add */
+#define AV_SPARC_ASI_CACHE_SPARING \
+ 0x00800000 /* cache sparing ASIs available */
#define CORE_DUMP_USE_REGSET
@@ -162,33 +180,8 @@
#define ELF_ET_DYN_BASE 0x0000010000000000UL
#define COMPAT_ELF_ET_DYN_BASE 0x0000000070000000UL
-
-/* This yields a mask that user programs can use to figure out what
- instruction set this cpu supports. */
-
-/* On Ultra, we support all of the v8 capabilities. */
-static inline unsigned int sparc64_elf_hwcap(void)
-{
- unsigned int cap = (HWCAP_SPARC_FLUSH | HWCAP_SPARC_STBAR |
- HWCAP_SPARC_SWAP | HWCAP_SPARC_MULDIV |
- HWCAP_SPARC_V9);
-
- if (tlb_type == cheetah || tlb_type == cheetah_plus)
- cap |= HWCAP_SPARC_ULTRA3;
- else if (tlb_type == hypervisor) {
- if (sun4v_chip_type == SUN4V_CHIP_NIAGARA1 ||
- sun4v_chip_type == SUN4V_CHIP_NIAGARA2 ||
- sun4v_chip_type == SUN4V_CHIP_NIAGARA3)
- cap |= HWCAP_SPARC_BLKINIT;
- if (sun4v_chip_type == SUN4V_CHIP_NIAGARA2 ||
- sun4v_chip_type == SUN4V_CHIP_NIAGARA3)
- cap |= HWCAP_SPARC_N2;
- }
-
- return cap;
-}
-
-#define ELF_HWCAP sparc64_elf_hwcap()
+extern unsigned long sparc64_elf_hwcap;
+#define ELF_HWCAP sparc64_elf_hwcap
/* This yields a string that ld.so will use to load implementation
specific libraries for optimization. This is more specific in
diff --git a/arch/sparc/include/asm/hypervisor.h b/arch/sparc/include/asm/hypervisor.h
index 7a5f80d..015a761 100644
--- a/arch/sparc/include/asm/hypervisor.h
+++ b/arch/sparc/include/asm/hypervisor.h
@@ -2927,6 +2927,13 @@
#define HV_FAST_FIRE_GET_PERFREG 0x120
#define HV_FAST_FIRE_SET_PERFREG 0x121
+#define HV_FAST_REBOOT_DATA_SET 0x172
+
+#ifndef __ASSEMBLY__
+extern unsigned long sun4v_reboot_data_set(unsigned long ra,
+ unsigned long len);
+#endif
+
/* Function numbers for HV_CORE_TRAP. */
#define HV_CORE_SET_VER 0x00
#define HV_CORE_PUTCHAR 0x01
@@ -2940,11 +2947,17 @@
#define HV_GRP_CORE 0x0001
#define HV_GRP_INTR 0x0002
#define HV_GRP_SOFT_STATE 0x0003
+#define HV_GRP_TM 0x0080
#define HV_GRP_PCI 0x0100
#define HV_GRP_LDOM 0x0101
#define HV_GRP_SVC_CHAN 0x0102
#define HV_GRP_NCS 0x0103
#define HV_GRP_RNG 0x0104
+#define HV_GRP_PBOOT 0x0105
+#define HV_GRP_TPM 0x0107
+#define HV_GRP_SDIO 0x0108
+#define HV_GRP_SDIO_ERR 0x0109
+#define HV_GRP_REBOOT_DATA 0x0110
#define HV_GRP_NIAG_PERF 0x0200
#define HV_GRP_FIRE_PERF 0x0201
#define HV_GRP_N2_CPU 0x0202
diff --git a/arch/sparc/include/asm/irq_regs.h b/arch/sparc/include/asm/irq_regs.h
deleted file mode 100644
index 3dd9c0b..0000000
--- a/arch/sparc/include/asm/irq_regs.h
+++ /dev/null
@@ -1 +0,0 @@
-#include <asm-generic/irq_regs.h>
diff --git a/arch/sparc/include/asm/local.h b/arch/sparc/include/asm/local.h
deleted file mode 100644
index bc80815..0000000
--- a/arch/sparc/include/asm/local.h
+++ /dev/null
@@ -1,6 +0,0 @@
-#ifndef _SPARC_LOCAL_H
-#define _SPARC_LOCAL_H
-
-#include <asm-generic/local.h>
-
-#endif
diff --git a/arch/sparc/include/asm/local64.h b/arch/sparc/include/asm/local64.h
deleted file mode 100644
index 36c93b5..0000000
--- a/arch/sparc/include/asm/local64.h
+++ /dev/null
@@ -1 +0,0 @@
-#include <asm-generic/local64.h>
diff --git a/arch/sparc/include/asm/spinlock_32.h b/arch/sparc/include/asm/spinlock_32.h
index 5f5b8bf..bcc98fc 100644
--- a/arch/sparc/include/asm/spinlock_32.h
+++ b/arch/sparc/include/asm/spinlock_32.h
@@ -131,6 +131,15 @@
*(volatile __u32 *)&lp->lock = ~0U;
}
+static void inline arch_write_unlock(arch_rwlock_t *lock)
+{
+ __asm__ __volatile__(
+" st %%g0, [%0]"
+ : /* no outputs */
+ : "r" (lock)
+ : "memory");
+}
+
static inline int arch_write_trylock(arch_rwlock_t *rw)
{
unsigned int val;
@@ -175,8 +184,6 @@
res; \
})
-#define arch_write_unlock(rw) do { (rw)->lock = 0; } while(0)
-
#define arch_spin_lock_flags(lock, flags) arch_spin_lock(lock)
#define arch_read_lock_flags(rw, flags) arch_read_lock(rw)
#define arch_write_lock_flags(rw, flags) arch_write_lock(rw)
diff --git a/arch/sparc/include/asm/spinlock_64.h b/arch/sparc/include/asm/spinlock_64.h
index 073936a..9689176 100644
--- a/arch/sparc/include/asm/spinlock_64.h
+++ b/arch/sparc/include/asm/spinlock_64.h
@@ -210,14 +210,8 @@
return result;
}
-#define arch_read_lock(p) arch_read_lock(p)
#define arch_read_lock_flags(p, f) arch_read_lock(p)
-#define arch_read_trylock(p) arch_read_trylock(p)
-#define arch_read_unlock(p) arch_read_unlock(p)
-#define arch_write_lock(p) arch_write_lock(p)
#define arch_write_lock_flags(p, f) arch_write_lock(p)
-#define arch_write_unlock(p) arch_write_unlock(p)
-#define arch_write_trylock(p) arch_write_trylock(p)
#define arch_read_can_lock(rw) (!((rw)->lock & 0x80000000UL))
#define arch_write_can_lock(rw) (!(rw)->lock)
diff --git a/arch/sparc/include/asm/tsb.h b/arch/sparc/include/asm/tsb.h
index 83c571d..1a8afd1 100644
--- a/arch/sparc/include/asm/tsb.h
+++ b/arch/sparc/include/asm/tsb.h
@@ -133,29 +133,6 @@
sub TSB, 0x8, TSB; \
TSB_STORE(TSB, TAG);
-#define KTSB_LOAD_QUAD(TSB, REG) \
- ldda [TSB] ASI_NUCLEUS_QUAD_LDD, REG;
-
-#define KTSB_STORE(ADDR, VAL) \
- stxa VAL, [ADDR] ASI_N;
-
-#define KTSB_LOCK_TAG(TSB, REG1, REG2) \
-99: lduwa [TSB] ASI_N, REG1; \
- sethi %hi(TSB_TAG_LOCK_HIGH), REG2;\
- andcc REG1, REG2, %g0; \
- bne,pn %icc, 99b; \
- nop; \
- casa [TSB] ASI_N, REG1, REG2;\
- cmp REG1, REG2; \
- bne,pn %icc, 99b; \
- nop; \
-
-#define KTSB_WRITE(TSB, TTE, TAG) \
- add TSB, 0x8, TSB; \
- stxa TTE, [TSB] ASI_N; \
- sub TSB, 0x8, TSB; \
- stxa TAG, [TSB] ASI_N;
-
/* Do a kernel page table walk. Leaves physical PTE pointer in
* REG1. Jumps to FAIL_LABEL on early page table walk termination.
* VADDR will not be clobbered, but REG2 will.
@@ -239,6 +216,8 @@
(KERNEL_TSB_SIZE_BYTES / 16)
#define KERNEL_TSB4M_NENTRIES 4096
+#define KTSB_PHYS_SHIFT 15
+
/* Do a kernel TSB lookup at tl>0 on VADDR+TAG, branch to OK_LABEL
* on TSB hit. REG1, REG2, REG3, and REG4 are used as temporaries
* and the found TTE will be left in REG1. REG3 and REG4 must
@@ -247,13 +226,22 @@
* VADDR and TAG will be preserved and not clobbered by this macro.
*/
#define KERN_TSB_LOOKUP_TL1(VADDR, TAG, REG1, REG2, REG3, REG4, OK_LABEL) \
- sethi %hi(swapper_tsb), REG1; \
+661: sethi %hi(swapper_tsb), REG1; \
or REG1, %lo(swapper_tsb), REG1; \
+ .section .swapper_tsb_phys_patch, "ax"; \
+ .word 661b; \
+ .previous; \
+661: nop; \
+ .section .tsb_ldquad_phys_patch, "ax"; \
+ .word 661b; \
+ sllx REG1, KTSB_PHYS_SHIFT, REG1; \
+ sllx REG1, KTSB_PHYS_SHIFT, REG1; \
+ .previous; \
srlx VADDR, PAGE_SHIFT, REG2; \
and REG2, (KERNEL_TSB_NENTRIES - 1), REG2; \
sllx REG2, 4, REG2; \
add REG1, REG2, REG2; \
- KTSB_LOAD_QUAD(REG2, REG3); \
+ TSB_LOAD_QUAD(REG2, REG3); \
cmp REG3, TAG; \
be,a,pt %xcc, OK_LABEL; \
mov REG4, REG1;
@@ -263,12 +251,21 @@
* we can make use of that for the index computation.
*/
#define KERN_TSB4M_LOOKUP_TL1(TAG, REG1, REG2, REG3, REG4, OK_LABEL) \
- sethi %hi(swapper_4m_tsb), REG1; \
+661: sethi %hi(swapper_4m_tsb), REG1; \
or REG1, %lo(swapper_4m_tsb), REG1; \
+ .section .swapper_4m_tsb_phys_patch, "ax"; \
+ .word 661b; \
+ .previous; \
+661: nop; \
+ .section .tsb_ldquad_phys_patch, "ax"; \
+ .word 661b; \
+ sllx REG1, KTSB_PHYS_SHIFT, REG1; \
+ sllx REG1, KTSB_PHYS_SHIFT, REG1; \
+ .previous; \
and TAG, (KERNEL_TSB4M_NENTRIES - 1), REG2; \
sllx REG2, 4, REG2; \
add REG1, REG2, REG2; \
- KTSB_LOAD_QUAD(REG2, REG3); \
+ TSB_LOAD_QUAD(REG2, REG3); \
cmp REG3, TAG; \
be,a,pt %xcc, OK_LABEL; \
mov REG4, REG1;
diff --git a/arch/sparc/kernel/cpu.c b/arch/sparc/kernel/cpu.c
index 17cf290..9810fd8 100644
--- a/arch/sparc/kernel/cpu.c
+++ b/arch/sparc/kernel/cpu.c
@@ -396,6 +396,7 @@
, cpu_data(0).clock_tick
#endif
);
+ cpucap_info(m);
#ifdef CONFIG_SMP
smp_bogo(m);
#endif
diff --git a/arch/sparc/kernel/ds.c b/arch/sparc/kernel/ds.c
index dd1342c..7429b47 100644
--- a/arch/sparc/kernel/ds.c
+++ b/arch/sparc/kernel/ds.c
@@ -15,12 +15,15 @@
#include <linux/reboot.h>
#include <linux/cpu.h>
+#include <asm/hypervisor.h>
#include <asm/ldc.h>
#include <asm/vio.h>
#include <asm/mdesc.h>
#include <asm/head.h>
#include <asm/irq.h>
+#include "kernel.h"
+
#define DRV_MODULE_NAME "ds"
#define PFX DRV_MODULE_NAME ": "
#define DRV_MODULE_VERSION "1.0"
@@ -828,18 +831,32 @@
}
}
+static char full_boot_str[256] __attribute__((aligned(32)));
+static int reboot_data_supported;
+
void ldom_reboot(const char *boot_command)
{
/* Don't bother with any of this if the boot_command
* is empty.
*/
if (boot_command && strlen(boot_command)) {
- char full_boot_str[256];
+ unsigned long len;
strcpy(full_boot_str, "boot ");
strcpy(full_boot_str + strlen("boot "), boot_command);
+ len = strlen(full_boot_str);
- ldom_set_var("reboot-command", full_boot_str);
+ if (reboot_data_supported) {
+ unsigned long ra = kimage_addr_to_ra(full_boot_str);
+ unsigned long hv_ret;
+
+ hv_ret = sun4v_reboot_data_set(ra, len);
+ if (hv_ret != HV_EOK)
+ pr_err("SUN4V: Unable to set reboot data "
+ "hv_ret=%lu\n", hv_ret);
+ } else {
+ ldom_set_var("reboot-command", full_boot_str);
+ }
}
sun4v_mach_sir();
}
@@ -1237,6 +1254,16 @@
static int __init ds_init(void)
{
+ unsigned long hv_ret, major, minor;
+
+ if (tlb_type == hypervisor) {
+ hv_ret = sun4v_get_version(HV_GRP_REBOOT_DATA, &major, &minor);
+ if (hv_ret == HV_EOK) {
+ pr_info("SUN4V: Reboot data supported (maj=%lu,min=%lu).\n",
+ major, minor);
+ reboot_data_supported = 1;
+ }
+ }
kthread_run(ds_thread, NULL, "kldomd");
return vio_register_driver(&ds_driver);
diff --git a/arch/sparc/kernel/entry.h b/arch/sparc/kernel/entry.h
index d1f1361..e27f8ea 100644
--- a/arch/sparc/kernel/entry.h
+++ b/arch/sparc/kernel/entry.h
@@ -42,6 +42,20 @@
extern void fpload(unsigned long *fpregs, unsigned long *fsr);
#else /* CONFIG_SPARC32 */
+struct popc_3insn_patch_entry {
+ unsigned int addr;
+ unsigned int insns[3];
+};
+extern struct popc_3insn_patch_entry __popc_3insn_patch,
+ __popc_3insn_patch_end;
+
+struct popc_6insn_patch_entry {
+ unsigned int addr;
+ unsigned int insns[6];
+};
+extern struct popc_6insn_patch_entry __popc_6insn_patch,
+ __popc_6insn_patch_end;
+
extern void __init per_cpu_patch(void);
extern void __init sun4v_patch(void);
extern void __init boot_cpu_id_too_large(int cpu);
diff --git a/arch/sparc/kernel/head_64.S b/arch/sparc/kernel/head_64.S
index c752603..0eac1b2 100644
--- a/arch/sparc/kernel/head_64.S
+++ b/arch/sparc/kernel/head_64.S
@@ -559,7 +559,7 @@
nop
call niagara_patch_bzero
nop
- call niagara2_patch_pageops
+ call niagara_patch_pageops
nop
ba,a,pt %xcc, 80f
diff --git a/arch/sparc/kernel/hvapi.c b/arch/sparc/kernel/hvapi.c
index d306e64..c2d055d 100644
--- a/arch/sparc/kernel/hvapi.c
+++ b/arch/sparc/kernel/hvapi.c
@@ -28,11 +28,17 @@
{ .group = HV_GRP_CORE, .flags = FLAG_PRE_API },
{ .group = HV_GRP_INTR, },
{ .group = HV_GRP_SOFT_STATE, },
+ { .group = HV_GRP_TM, },
{ .group = HV_GRP_PCI, .flags = FLAG_PRE_API },
{ .group = HV_GRP_LDOM, },
{ .group = HV_GRP_SVC_CHAN, .flags = FLAG_PRE_API },
{ .group = HV_GRP_NCS, .flags = FLAG_PRE_API },
{ .group = HV_GRP_RNG, },
+ { .group = HV_GRP_PBOOT, },
+ { .group = HV_GRP_TPM, },
+ { .group = HV_GRP_SDIO, },
+ { .group = HV_GRP_SDIO_ERR, },
+ { .group = HV_GRP_REBOOT_DATA, },
{ .group = HV_GRP_NIAG_PERF, .flags = FLAG_PRE_API },
{ .group = HV_GRP_FIRE_PERF, },
{ .group = HV_GRP_N2_CPU, },
diff --git a/arch/sparc/kernel/hvcalls.S b/arch/sparc/kernel/hvcalls.S
index 8a5f35f..58d60de 100644
--- a/arch/sparc/kernel/hvcalls.S
+++ b/arch/sparc/kernel/hvcalls.S
@@ -798,3 +798,10 @@
retl
nop
ENDPROC(sun4v_niagara2_setperf)
+
+ENTRY(sun4v_reboot_data_set)
+ mov HV_FAST_REBOOT_DATA_SET, %o5
+ ta HV_FAST_TRAP
+ retl
+ nop
+ENDPROC(sun4v_reboot_data_set)
diff --git a/arch/sparc/kernel/ioport.c b/arch/sparc/kernel/ioport.c
index 6ffccd6..d0479e2 100644
--- a/arch/sparc/kernel/ioport.c
+++ b/arch/sparc/kernel/ioport.c
@@ -65,9 +65,6 @@
}
#endif
-static struct resource *_sparc_find_resource(struct resource *r,
- unsigned long);
-
static void __iomem *_sparc_ioremap(struct resource *res, u32 bus, u32 pa, int sz);
static void __iomem *_sparc_alloc_io(unsigned int busno, unsigned long phys,
unsigned long size, char *name);
@@ -143,7 +140,11 @@
unsigned long vaddr = (unsigned long) virtual & PAGE_MASK;
struct resource *res;
- if ((res = _sparc_find_resource(&sparc_iomap, vaddr)) == NULL) {
+ /*
+ * XXX Too slow. Can have 8192 DVMA pages on sun4m in the worst case.
+ * This probably warrants some sort of hashing.
+ */
+ if ((res = lookup_resource(&sparc_iomap, vaddr)) == NULL) {
printk("free_io/iounmap: cannot free %lx\n", vaddr);
return;
}
@@ -319,7 +320,7 @@
struct resource *res;
struct page *pgv;
- if ((res = _sparc_find_resource(&_sparc_dvma,
+ if ((res = lookup_resource(&_sparc_dvma,
(unsigned long)p)) == NULL) {
printk("sbus_free_consistent: cannot free %p\n", p);
return;
@@ -492,7 +493,7 @@
{
struct resource *res;
- if ((res = _sparc_find_resource(&_sparc_dvma,
+ if ((res = lookup_resource(&_sparc_dvma,
(unsigned long)p)) == NULL) {
printk("pci_free_consistent: cannot free %p\n", p);
return;
@@ -715,25 +716,6 @@
};
#endif /* CONFIG_PROC_FS */
-/*
- * This is a version of find_resource and it belongs to kernel/resource.c.
- * Until we have agreement with Linus and Martin, it lingers here.
- *
- * XXX Too slow. Can have 8192 DVMA pages on sun4m in the worst case.
- * This probably warrants some sort of hashing.
- */
-static struct resource *_sparc_find_resource(struct resource *root,
- unsigned long hit)
-{
- struct resource *tmp;
-
- for (tmp = root->child; tmp != 0; tmp = tmp->sibling) {
- if (tmp->start <= hit && tmp->end >= hit)
- return tmp;
- }
- return NULL;
-}
-
static void register_proc_sparc_ioport(void)
{
#ifdef CONFIG_PROC_FS
diff --git a/arch/sparc/kernel/kernel.h b/arch/sparc/kernel/kernel.h
index 6f6544c..fd6c36b 100644
--- a/arch/sparc/kernel/kernel.h
+++ b/arch/sparc/kernel/kernel.h
@@ -4,12 +4,27 @@
#include <linux/interrupt.h>
#include <asm/traps.h>
+#include <asm/head.h>
+#include <asm/io.h>
/* cpu.c */
extern const char *sparc_pmu_type;
extern unsigned int fsr_storage;
extern int ncpus_probed;
+#ifdef CONFIG_SPARC64
+/* setup_64.c */
+struct seq_file;
+extern void cpucap_info(struct seq_file *);
+
+static inline unsigned long kimage_addr_to_ra(const char *p)
+{
+ unsigned long val = (unsigned long) p;
+
+ return kern_base + (val - KERNBASE);
+}
+#endif
+
#ifdef CONFIG_SPARC32
/* cpu.c */
extern void cpu_probe(void);
diff --git a/arch/sparc/kernel/ktlb.S b/arch/sparc/kernel/ktlb.S
index 1d36147..79f3103 100644
--- a/arch/sparc/kernel/ktlb.S
+++ b/arch/sparc/kernel/ktlb.S
@@ -47,16 +47,16 @@
kvmap_itlb_vmalloc_addr:
KERN_PGTABLE_WALK(%g4, %g5, %g2, kvmap_itlb_longpath)
- KTSB_LOCK_TAG(%g1, %g2, %g7)
+ TSB_LOCK_TAG(%g1, %g2, %g7)
/* Load and check PTE. */
ldxa [%g5] ASI_PHYS_USE_EC, %g5
mov 1, %g7
sllx %g7, TSB_TAG_INVALID_BIT, %g7
brgez,a,pn %g5, kvmap_itlb_longpath
- KTSB_STORE(%g1, %g7)
+ TSB_STORE(%g1, %g7)
- KTSB_WRITE(%g1, %g5, %g6)
+ TSB_WRITE(%g1, %g5, %g6)
/* fallthrough to TLB load */
@@ -102,9 +102,9 @@
kvmap_itlb_obp:
OBP_TRANS_LOOKUP(%g4, %g5, %g2, %g3, kvmap_itlb_longpath)
- KTSB_LOCK_TAG(%g1, %g2, %g7)
+ TSB_LOCK_TAG(%g1, %g2, %g7)
- KTSB_WRITE(%g1, %g5, %g6)
+ TSB_WRITE(%g1, %g5, %g6)
ba,pt %xcc, kvmap_itlb_load
nop
@@ -112,17 +112,17 @@
kvmap_dtlb_obp:
OBP_TRANS_LOOKUP(%g4, %g5, %g2, %g3, kvmap_dtlb_longpath)
- KTSB_LOCK_TAG(%g1, %g2, %g7)
+ TSB_LOCK_TAG(%g1, %g2, %g7)
- KTSB_WRITE(%g1, %g5, %g6)
+ TSB_WRITE(%g1, %g5, %g6)
ba,pt %xcc, kvmap_dtlb_load
nop
.align 32
kvmap_dtlb_tsb4m_load:
- KTSB_LOCK_TAG(%g1, %g2, %g7)
- KTSB_WRITE(%g1, %g5, %g6)
+ TSB_LOCK_TAG(%g1, %g2, %g7)
+ TSB_WRITE(%g1, %g5, %g6)
ba,pt %xcc, kvmap_dtlb_load
nop
@@ -222,16 +222,16 @@
kvmap_dtlb_vmalloc_addr:
KERN_PGTABLE_WALK(%g4, %g5, %g2, kvmap_dtlb_longpath)
- KTSB_LOCK_TAG(%g1, %g2, %g7)
+ TSB_LOCK_TAG(%g1, %g2, %g7)
/* Load and check PTE. */
ldxa [%g5] ASI_PHYS_USE_EC, %g5
mov 1, %g7
sllx %g7, TSB_TAG_INVALID_BIT, %g7
brgez,a,pn %g5, kvmap_dtlb_longpath
- KTSB_STORE(%g1, %g7)
+ TSB_STORE(%g1, %g7)
- KTSB_WRITE(%g1, %g5, %g6)
+ TSB_WRITE(%g1, %g5, %g6)
/* fallthrough to TLB load */
diff --git a/arch/sparc/kernel/mdesc.c b/arch/sparc/kernel/mdesc.c
index 42f28c7..acaebb6 100644
--- a/arch/sparc/kernel/mdesc.c
+++ b/arch/sparc/kernel/mdesc.c
@@ -508,6 +508,8 @@
}
EXPORT_SYMBOL(mdesc_node_name);
+static u64 max_cpus = 64;
+
static void __init report_platform_properties(void)
{
struct mdesc_handle *hp = mdesc_grab();
@@ -543,8 +545,10 @@
if (v)
printk("PLATFORM: watchdog-max-timeout [%llu ms]\n", *v);
v = mdesc_get_property(hp, pn, "max-cpus", NULL);
- if (v)
- printk("PLATFORM: max-cpus [%llu]\n", *v);
+ if (v) {
+ max_cpus = *v;
+ printk("PLATFORM: max-cpus [%llu]\n", max_cpus);
+ }
#ifdef CONFIG_SMP
{
@@ -715,7 +719,7 @@
}
static void __cpuinit get_one_mondo_bits(const u64 *p, unsigned int *mask,
- unsigned char def)
+ unsigned long def, unsigned long max)
{
u64 val;
@@ -726,6 +730,9 @@
if (!val || val >= 64)
goto use_default;
+ if (val > max)
+ val = max;
+
*mask = ((1U << val) * 64U) - 1U;
return;
@@ -736,19 +743,28 @@
static void __cpuinit get_mondo_data(struct mdesc_handle *hp, u64 mp,
struct trap_per_cpu *tb)
{
+ static int printed;
const u64 *val;
val = mdesc_get_property(hp, mp, "q-cpu-mondo-#bits", NULL);
- get_one_mondo_bits(val, &tb->cpu_mondo_qmask, 7);
+ get_one_mondo_bits(val, &tb->cpu_mondo_qmask, 7, ilog2(max_cpus * 2));
val = mdesc_get_property(hp, mp, "q-dev-mondo-#bits", NULL);
- get_one_mondo_bits(val, &tb->dev_mondo_qmask, 7);
+ get_one_mondo_bits(val, &tb->dev_mondo_qmask, 7, 8);
val = mdesc_get_property(hp, mp, "q-resumable-#bits", NULL);
- get_one_mondo_bits(val, &tb->resum_qmask, 6);
+ get_one_mondo_bits(val, &tb->resum_qmask, 6, 7);
val = mdesc_get_property(hp, mp, "q-nonresumable-#bits", NULL);
- get_one_mondo_bits(val, &tb->nonresum_qmask, 2);
+ get_one_mondo_bits(val, &tb->nonresum_qmask, 2, 2);
+ if (!printed++) {
+ pr_info("SUN4V: Mondo queue sizes "
+ "[cpu(%u) dev(%u) r(%u) nr(%u)]\n",
+ tb->cpu_mondo_qmask + 1,
+ tb->dev_mondo_qmask + 1,
+ tb->resum_qmask + 1,
+ tb->nonresum_qmask + 1);
+ }
}
static void * __cpuinit mdesc_iterate_over_cpus(void *(*func)(struct mdesc_handle *, u64, int, void *), void *arg, cpumask_t *mask)
diff --git a/arch/sparc/kernel/pcic.c b/arch/sparc/kernel/pcic.c
index a19f041..1aaf8c1 100644
--- a/arch/sparc/kernel/pcic.c
+++ b/arch/sparc/kernel/pcic.c
@@ -352,8 +352,8 @@
strcpy(pbm->prom_name, namebuf);
{
- extern volatile int t_nmi[1];
- extern int pcic_nmi_trap_patch[1];
+ extern volatile int t_nmi[4];
+ extern int pcic_nmi_trap_patch[4];
t_nmi[0] = pcic_nmi_trap_patch[0];
t_nmi[1] = pcic_nmi_trap_patch[1];
diff --git a/arch/sparc/kernel/setup_64.c b/arch/sparc/kernel/setup_64.c
index c4dd099..3e9daea 100644
--- a/arch/sparc/kernel/setup_64.c
+++ b/arch/sparc/kernel/setup_64.c
@@ -29,6 +29,7 @@
#include <linux/interrupt.h>
#include <linux/cpu.h>
#include <linux/initrd.h>
+#include <linux/module.h>
#include <asm/system.h>
#include <asm/io.h>
@@ -46,6 +47,8 @@
#include <asm/mmu.h>
#include <asm/ns87303.h>
#include <asm/btext.h>
+#include <asm/elf.h>
+#include <asm/mdesc.h>
#ifdef CONFIG_IP_PNP
#include <net/ipconfig.h>
@@ -269,6 +272,40 @@
sun4v_hvapi_init();
}
+static void __init popc_patch(void)
+{
+ struct popc_3insn_patch_entry *p3;
+ struct popc_6insn_patch_entry *p6;
+
+ p3 = &__popc_3insn_patch;
+ while (p3 < &__popc_3insn_patch_end) {
+ unsigned long i, addr = p3->addr;
+
+ for (i = 0; i < 3; i++) {
+ *(unsigned int *) (addr + (i * 4)) = p3->insns[i];
+ wmb();
+ __asm__ __volatile__("flush %0"
+ : : "r" (addr + (i * 4)));
+ }
+
+ p3++;
+ }
+
+ p6 = &__popc_6insn_patch;
+ while (p6 < &__popc_6insn_patch_end) {
+ unsigned long i, addr = p6->addr;
+
+ for (i = 0; i < 6; i++) {
+ *(unsigned int *) (addr + (i * 4)) = p6->insns[i];
+ wmb();
+ __asm__ __volatile__("flush %0"
+ : : "r" (addr + (i * 4)));
+ }
+
+ p6++;
+ }
+}
+
#ifdef CONFIG_SMP
void __init boot_cpu_id_too_large(int cpu)
{
@@ -278,6 +315,154 @@
}
#endif
+/* On Ultra, we support all of the v8 capabilities. */
+unsigned long sparc64_elf_hwcap = (HWCAP_SPARC_FLUSH | HWCAP_SPARC_STBAR |
+ HWCAP_SPARC_SWAP | HWCAP_SPARC_MULDIV |
+ HWCAP_SPARC_V9);
+EXPORT_SYMBOL(sparc64_elf_hwcap);
+
+static const char *hwcaps[] = {
+ "flush", "stbar", "swap", "muldiv", "v9",
+ "ultra3", "blkinit", "n2",
+
+ /* These strings are as they appear in the machine description
+ * 'hwcap-list' property for cpu nodes.
+ */
+ "mul32", "div32", "fsmuld", "v8plus", "popc", "vis", "vis2",
+ "ASIBlkInit", "fmaf", "vis3", "hpc", "random", "trans", "fjfmau",
+ "ima", "cspare",
+};
+
+void cpucap_info(struct seq_file *m)
+{
+ unsigned long caps = sparc64_elf_hwcap;
+ int i, printed = 0;
+
+ seq_puts(m, "cpucaps\t\t: ");
+ for (i = 0; i < ARRAY_SIZE(hwcaps); i++) {
+ unsigned long bit = 1UL << i;
+ if (caps & bit) {
+ seq_printf(m, "%s%s",
+ printed ? "," : "", hwcaps[i]);
+ printed++;
+ }
+ }
+ seq_putc(m, '\n');
+}
+
+static void __init report_hwcaps(unsigned long caps)
+{
+ int i, printed = 0;
+
+ printk(KERN_INFO "CPU CAPS: [");
+ for (i = 0; i < ARRAY_SIZE(hwcaps); i++) {
+ unsigned long bit = 1UL << i;
+ if (caps & bit) {
+ printk(KERN_CONT "%s%s",
+ printed ? "," : "", hwcaps[i]);
+ if (++printed == 8) {
+ printk(KERN_CONT "]\n");
+ printk(KERN_INFO "CPU CAPS: [");
+ printed = 0;
+ }
+ }
+ }
+ printk(KERN_CONT "]\n");
+}
+
+static unsigned long __init mdesc_cpu_hwcap_list(void)
+{
+ struct mdesc_handle *hp;
+ unsigned long caps = 0;
+ const char *prop;
+ int len;
+ u64 pn;
+
+ hp = mdesc_grab();
+ if (!hp)
+ return 0;
+
+ pn = mdesc_node_by_name(hp, MDESC_NODE_NULL, "cpu");
+ if (pn == MDESC_NODE_NULL)
+ goto out;
+
+ prop = mdesc_get_property(hp, pn, "hwcap-list", &len);
+ if (!prop)
+ goto out;
+
+ while (len) {
+ int i, plen;
+
+ for (i = 0; i < ARRAY_SIZE(hwcaps); i++) {
+ unsigned long bit = 1UL << i;
+
+ if (!strcmp(prop, hwcaps[i])) {
+ caps |= bit;
+ break;
+ }
+ }
+
+ plen = strlen(prop) + 1;
+ prop += plen;
+ len -= plen;
+ }
+
+out:
+ mdesc_release(hp);
+ return caps;
+}
+
+/* This yields a mask that user programs can use to figure out what
+ * instruction set this cpu supports.
+ */
+static void __init init_sparc64_elf_hwcap(void)
+{
+ unsigned long cap = sparc64_elf_hwcap;
+ unsigned long mdesc_caps;
+
+ if (tlb_type == cheetah || tlb_type == cheetah_plus)
+ cap |= HWCAP_SPARC_ULTRA3;
+ else if (tlb_type == hypervisor) {
+ if (sun4v_chip_type == SUN4V_CHIP_NIAGARA1 ||
+ sun4v_chip_type == SUN4V_CHIP_NIAGARA2 ||
+ sun4v_chip_type == SUN4V_CHIP_NIAGARA3)
+ cap |= HWCAP_SPARC_BLKINIT;
+ if (sun4v_chip_type == SUN4V_CHIP_NIAGARA2 ||
+ sun4v_chip_type == SUN4V_CHIP_NIAGARA3)
+ cap |= HWCAP_SPARC_N2;
+ }
+
+ cap |= (AV_SPARC_MUL32 | AV_SPARC_DIV32 | AV_SPARC_V8PLUS);
+
+ mdesc_caps = mdesc_cpu_hwcap_list();
+ if (!mdesc_caps) {
+ if (tlb_type == spitfire)
+ cap |= AV_SPARC_VIS;
+ if (tlb_type == cheetah || tlb_type == cheetah_plus)
+ cap |= AV_SPARC_VIS | AV_SPARC_VIS2;
+ if (tlb_type == cheetah_plus)
+ cap |= AV_SPARC_POPC;
+ if (tlb_type == hypervisor) {
+ if (sun4v_chip_type == SUN4V_CHIP_NIAGARA1)
+ cap |= AV_SPARC_ASI_BLK_INIT;
+ if (sun4v_chip_type == SUN4V_CHIP_NIAGARA2 ||
+ sun4v_chip_type == SUN4V_CHIP_NIAGARA3)
+ cap |= (AV_SPARC_VIS | AV_SPARC_VIS2 |
+ AV_SPARC_ASI_BLK_INIT |
+ AV_SPARC_POPC);
+ if (sun4v_chip_type == SUN4V_CHIP_NIAGARA3)
+ cap |= (AV_SPARC_VIS3 | AV_SPARC_HPC |
+ AV_SPARC_FMAF);
+ }
+ }
+ sparc64_elf_hwcap = cap | mdesc_caps;
+
+ report_hwcaps(sparc64_elf_hwcap);
+
+ if (sparc64_elf_hwcap & AV_SPARC_POPC)
+ popc_patch();
+}
+
void __init setup_arch(char **cmdline_p)
{
/* Initialize PROM console and command line. */
@@ -337,6 +522,7 @@
init_cur_cpu_trap(current_thread_info());
paging_init();
+ init_sparc64_elf_hwcap();
}
extern int stop_a_enabled;
diff --git a/arch/sparc/kernel/sparc_ksyms_64.c b/arch/sparc/kernel/sparc_ksyms_64.c
index 372ad59..83b47ab 100644
--- a/arch/sparc/kernel/sparc_ksyms_64.c
+++ b/arch/sparc/kernel/sparc_ksyms_64.c
@@ -8,6 +8,7 @@
#include <linux/module.h>
#include <linux/pci.h>
#include <linux/init.h>
+#include <linux/bitops.h>
#include <asm/system.h>
#include <asm/cpudata.h>
@@ -38,5 +39,15 @@
EXPORT_SYMBOL(sun4v_niagara2_getperf);
EXPORT_SYMBOL(sun4v_niagara2_setperf);
+/* from hweight.S */
+EXPORT_SYMBOL(__arch_hweight8);
+EXPORT_SYMBOL(__arch_hweight16);
+EXPORT_SYMBOL(__arch_hweight32);
+EXPORT_SYMBOL(__arch_hweight64);
+
+/* from ffs_ffz.S */
+EXPORT_SYMBOL(ffs);
+EXPORT_SYMBOL(__ffs);
+
/* Exporting a symbol from /init/main.c */
EXPORT_SYMBOL(saved_command_line);
diff --git a/arch/sparc/kernel/sstate.c b/arch/sparc/kernel/sstate.c
index 8cdbe59..c59af54 100644
--- a/arch/sparc/kernel/sstate.c
+++ b/arch/sparc/kernel/sstate.c
@@ -14,15 +14,10 @@
#include <asm/head.h>
#include <asm/io.h>
+#include "kernel.h"
+
static int hv_supports_soft_state;
-static unsigned long kimage_addr_to_ra(const char *p)
-{
- unsigned long val = (unsigned long) p;
-
- return kern_base + (val - KERNBASE);
-}
-
static void do_set_sstate(unsigned long state, const char *msg)
{
unsigned long err;
diff --git a/arch/sparc/kernel/unaligned_64.c b/arch/sparc/kernel/unaligned_64.c
index 35cff16..76e4ac1 100644
--- a/arch/sparc/kernel/unaligned_64.c
+++ b/arch/sparc/kernel/unaligned_64.c
@@ -22,6 +22,7 @@
#include <linux/bitops.h>
#include <linux/perf_event.h>
#include <linux/ratelimit.h>
+#include <linux/bitops.h>
#include <asm/fpumacro.h>
enum direction {
@@ -373,16 +374,11 @@
}
}
-static char popc_helper[] = {
-0, 1, 1, 2, 1, 2, 2, 3,
-1, 2, 2, 3, 2, 3, 3, 4,
-};
-
int handle_popc(u32 insn, struct pt_regs *regs)
{
- u64 value;
- int ret, i, rd = ((insn >> 25) & 0x1f);
int from_kernel = (regs->tstate & TSTATE_PRIV) != 0;
+ int ret, rd = ((insn >> 25) & 0x1f);
+ u64 value;
perf_sw_event(PERF_COUNT_SW_EMULATION_FAULTS, 1, regs, 0);
if (insn & 0x2000) {
@@ -392,10 +388,7 @@
maybe_flush_windows(0, insn & 0x1f, rd, from_kernel);
value = fetch_reg(insn & 0x1f, regs);
}
- for (ret = 0, i = 0; i < 16; i++) {
- ret += popc_helper[value & 0xf];
- value >>= 4;
- }
+ ret = hweight64(value);
if (rd < 16) {
if (rd)
regs->u_regs[rd] = ret;
diff --git a/arch/sparc/kernel/vmlinux.lds.S b/arch/sparc/kernel/vmlinux.lds.S
index c022075..0e16056 100644
--- a/arch/sparc/kernel/vmlinux.lds.S
+++ b/arch/sparc/kernel/vmlinux.lds.S
@@ -107,7 +107,26 @@
*(.sun4v_2insn_patch)
__sun4v_2insn_patch_end = .;
}
-
+ .swapper_tsb_phys_patch : {
+ __swapper_tsb_phys_patch = .;
+ *(.swapper_tsb_phys_patch)
+ __swapper_tsb_phys_patch_end = .;
+ }
+ .swapper_4m_tsb_phys_patch : {
+ __swapper_4m_tsb_phys_patch = .;
+ *(.swapper_4m_tsb_phys_patch)
+ __swapper_4m_tsb_phys_patch_end = .;
+ }
+ .popc_3insn_patch : {
+ __popc_3insn_patch = .;
+ *(.popc_3insn_patch)
+ __popc_3insn_patch_end = .;
+ }
+ .popc_6insn_patch : {
+ __popc_6insn_patch = .;
+ *(.popc_6insn_patch)
+ __popc_6insn_patch_end = .;
+ }
PERCPU_SECTION(SMP_CACHE_BYTES)
. = ALIGN(PAGE_SIZE);
diff --git a/arch/sparc/lib/Makefile b/arch/sparc/lib/Makefile
index 7f01b8f..a3fc437 100644
--- a/arch/sparc/lib/Makefile
+++ b/arch/sparc/lib/Makefile
@@ -31,13 +31,13 @@
lib-$(CONFIG_SPARC64) += NGpatch.o NGpage.o NGbzero.o
lib-$(CONFIG_SPARC64) += NG2memcpy.o NG2copy_from_user.o NG2copy_to_user.o
-lib-$(CONFIG_SPARC64) += NG2patch.o NG2page.o
+lib-$(CONFIG_SPARC64) += NG2patch.o
lib-$(CONFIG_SPARC64) += GENmemcpy.o GENcopy_from_user.o GENcopy_to_user.o
lib-$(CONFIG_SPARC64) += GENpatch.o GENpage.o GENbzero.o
lib-$(CONFIG_SPARC64) += copy_in_user.o user_fixup.o memmove.o
-lib-$(CONFIG_SPARC64) += mcount.o ipcsum.o xor.o
+lib-$(CONFIG_SPARC64) += mcount.o ipcsum.o xor.o hweight.o ffs.o
obj-y += iomap.o
obj-$(CONFIG_SPARC32) += atomic32.o
diff --git a/arch/sparc/lib/NG2page.S b/arch/sparc/lib/NG2page.S
deleted file mode 100644
index 73b6b7c..0000000
--- a/arch/sparc/lib/NG2page.S
+++ /dev/null
@@ -1,61 +0,0 @@
-/* NG2page.S: Niagara-2 optimized clear and copy page.
- *
- * Copyright (C) 2007 (davem@davemloft.net)
- */
-
-#include <asm/asi.h>
-#include <asm/page.h>
-#include <asm/visasm.h>
-
- .text
- .align 32
-
- /* This is heavily simplified from the sun4u variants
- * because Niagara-2 does not have any D-cache aliasing issues.
- */
-NG2copy_user_page: /* %o0=dest, %o1=src, %o2=vaddr */
- prefetch [%o1 + 0x00], #one_read
- prefetch [%o1 + 0x40], #one_read
- VISEntryHalf
- set PAGE_SIZE, %g7
- sub %o0, %o1, %g3
-1: stxa %g0, [%o1 + %g3] ASI_BLK_INIT_QUAD_LDD_P
- subcc %g7, 64, %g7
- ldda [%o1] ASI_BLK_P, %f0
- stda %f0, [%o1 + %g3] ASI_BLK_P
- add %o1, 64, %o1
- bne,pt %xcc, 1b
- prefetch [%o1 + 0x40], #one_read
- membar #Sync
- VISExitHalf
- retl
- nop
-
-#define BRANCH_ALWAYS 0x10680000
-#define NOP 0x01000000
-#define NG_DO_PATCH(OLD, NEW) \
- sethi %hi(NEW), %g1; \
- or %g1, %lo(NEW), %g1; \
- sethi %hi(OLD), %g2; \
- or %g2, %lo(OLD), %g2; \
- sub %g1, %g2, %g1; \
- sethi %hi(BRANCH_ALWAYS), %g3; \
- sll %g1, 11, %g1; \
- srl %g1, 11 + 2, %g1; \
- or %g3, %lo(BRANCH_ALWAYS), %g3; \
- or %g3, %g1, %g3; \
- stw %g3, [%g2]; \
- sethi %hi(NOP), %g3; \
- or %g3, %lo(NOP), %g3; \
- stw %g3, [%g2 + 0x4]; \
- flush %g2;
-
- .globl niagara2_patch_pageops
- .type niagara2_patch_pageops,#function
-niagara2_patch_pageops:
- NG_DO_PATCH(copy_user_page, NG2copy_user_page)
- NG_DO_PATCH(_clear_page, NGclear_page)
- NG_DO_PATCH(clear_user_page, NGclear_user_page)
- retl
- nop
- .size niagara2_patch_pageops,.-niagara2_patch_pageops
diff --git a/arch/sparc/lib/NGpage.S b/arch/sparc/lib/NGpage.S
index 428920d..b9e790b 100644
--- a/arch/sparc/lib/NGpage.S
+++ b/arch/sparc/lib/NGpage.S
@@ -16,55 +16,91 @@
*/
NGcopy_user_page: /* %o0=dest, %o1=src, %o2=vaddr */
- prefetch [%o1 + 0x00], #one_read
- mov 8, %g1
- mov 16, %g2
- mov 24, %g3
+ save %sp, -192, %sp
+ rd %asi, %g3
+ wr %g0, ASI_BLK_INIT_QUAD_LDD_P, %asi
set PAGE_SIZE, %g7
+ prefetch [%i1 + 0x00], #one_read
+ prefetch [%i1 + 0x40], #one_read
-1: ldda [%o1 + %g0] ASI_BLK_INIT_QUAD_LDD_P, %o2
- ldda [%o1 + %g2] ASI_BLK_INIT_QUAD_LDD_P, %o4
- prefetch [%o1 + 0x40], #one_read
- add %o1, 32, %o1
- stxa %o2, [%o0 + %g0] ASI_BLK_INIT_QUAD_LDD_P
- stxa %o3, [%o0 + %g1] ASI_BLK_INIT_QUAD_LDD_P
- ldda [%o1 + %g0] ASI_BLK_INIT_QUAD_LDD_P, %o2
- stxa %o4, [%o0 + %g2] ASI_BLK_INIT_QUAD_LDD_P
- stxa %o5, [%o0 + %g3] ASI_BLK_INIT_QUAD_LDD_P
- ldda [%o1 + %g2] ASI_BLK_INIT_QUAD_LDD_P, %o4
- add %o1, 32, %o1
- add %o0, 32, %o0
- stxa %o2, [%o0 + %g0] ASI_BLK_INIT_QUAD_LDD_P
- stxa %o3, [%o0 + %g1] ASI_BLK_INIT_QUAD_LDD_P
- stxa %o4, [%o0 + %g2] ASI_BLK_INIT_QUAD_LDD_P
- stxa %o5, [%o0 + %g3] ASI_BLK_INIT_QUAD_LDD_P
- subcc %g7, 64, %g7
+1: prefetch [%i1 + 0x80], #one_read
+ prefetch [%i1 + 0xc0], #one_read
+ ldda [%i1 + 0x00] %asi, %o2
+ ldda [%i1 + 0x10] %asi, %o4
+ ldda [%i1 + 0x20] %asi, %l2
+ ldda [%i1 + 0x30] %asi, %l4
+ stxa %o2, [%i0 + 0x00] %asi
+ stxa %o3, [%i0 + 0x08] %asi
+ stxa %o4, [%i0 + 0x10] %asi
+ stxa %o5, [%i0 + 0x18] %asi
+ stxa %l2, [%i0 + 0x20] %asi
+ stxa %l3, [%i0 + 0x28] %asi
+ stxa %l4, [%i0 + 0x30] %asi
+ stxa %l5, [%i0 + 0x38] %asi
+ ldda [%i1 + 0x40] %asi, %o2
+ ldda [%i1 + 0x50] %asi, %o4
+ ldda [%i1 + 0x60] %asi, %l2
+ ldda [%i1 + 0x70] %asi, %l4
+ stxa %o2, [%i0 + 0x40] %asi
+ stxa %o3, [%i0 + 0x48] %asi
+ stxa %o4, [%i0 + 0x50] %asi
+ stxa %o5, [%i0 + 0x58] %asi
+ stxa %l2, [%i0 + 0x60] %asi
+ stxa %l3, [%i0 + 0x68] %asi
+ stxa %l4, [%i0 + 0x70] %asi
+ stxa %l5, [%i0 + 0x78] %asi
+ add %i1, 128, %i1
+ subcc %g7, 128, %g7
bne,pt %xcc, 1b
- add %o0, 32, %o0
+ add %i0, 128, %i0
+ wr %g3, 0x0, %asi
membar #Sync
- retl
- nop
+ ret
+ restore
- .globl NGclear_page, NGclear_user_page
+ .align 32
NGclear_page: /* %o0=dest */
NGclear_user_page: /* %o0=dest, %o1=vaddr */
- mov 8, %g1
- mov 16, %g2
- mov 24, %g3
+ rd %asi, %g3
+ wr %g0, ASI_BLK_INIT_QUAD_LDD_P, %asi
set PAGE_SIZE, %g7
-1: stxa %g0, [%o0 + %g0] ASI_BLK_INIT_QUAD_LDD_P
- stxa %g0, [%o0 + %g1] ASI_BLK_INIT_QUAD_LDD_P
- stxa %g0, [%o0 + %g2] ASI_BLK_INIT_QUAD_LDD_P
- stxa %g0, [%o0 + %g3] ASI_BLK_INIT_QUAD_LDD_P
- add %o0, 32, %o0
- stxa %g0, [%o0 + %g0] ASI_BLK_INIT_QUAD_LDD_P
- stxa %g0, [%o0 + %g1] ASI_BLK_INIT_QUAD_LDD_P
- stxa %g0, [%o0 + %g2] ASI_BLK_INIT_QUAD_LDD_P
- stxa %g0, [%o0 + %g3] ASI_BLK_INIT_QUAD_LDD_P
- subcc %g7, 64, %g7
+1: stxa %g0, [%o0 + 0x00] %asi
+ stxa %g0, [%o0 + 0x08] %asi
+ stxa %g0, [%o0 + 0x10] %asi
+ stxa %g0, [%o0 + 0x18] %asi
+ stxa %g0, [%o0 + 0x20] %asi
+ stxa %g0, [%o0 + 0x28] %asi
+ stxa %g0, [%o0 + 0x30] %asi
+ stxa %g0, [%o0 + 0x38] %asi
+ stxa %g0, [%o0 + 0x40] %asi
+ stxa %g0, [%o0 + 0x48] %asi
+ stxa %g0, [%o0 + 0x50] %asi
+ stxa %g0, [%o0 + 0x58] %asi
+ stxa %g0, [%o0 + 0x60] %asi
+ stxa %g0, [%o0 + 0x68] %asi
+ stxa %g0, [%o0 + 0x70] %asi
+ stxa %g0, [%o0 + 0x78] %asi
+ stxa %g0, [%o0 + 0x80] %asi
+ stxa %g0, [%o0 + 0x88] %asi
+ stxa %g0, [%o0 + 0x90] %asi
+ stxa %g0, [%o0 + 0x98] %asi
+ stxa %g0, [%o0 + 0xa0] %asi
+ stxa %g0, [%o0 + 0xa8] %asi
+ stxa %g0, [%o0 + 0xb0] %asi
+ stxa %g0, [%o0 + 0xb8] %asi
+ stxa %g0, [%o0 + 0xc0] %asi
+ stxa %g0, [%o0 + 0xc8] %asi
+ stxa %g0, [%o0 + 0xd0] %asi
+ stxa %g0, [%o0 + 0xd8] %asi
+ stxa %g0, [%o0 + 0xe0] %asi
+ stxa %g0, [%o0 + 0xe8] %asi
+ stxa %g0, [%o0 + 0xf0] %asi
+ stxa %g0, [%o0 + 0xf8] %asi
+ subcc %g7, 256, %g7
bne,pt %xcc, 1b
- add %o0, 32, %o0
+ add %o0, 256, %o0
+ wr %g3, 0x0, %asi
membar #Sync
retl
nop
diff --git a/arch/sparc/lib/atomic32.c b/arch/sparc/lib/atomic32.c
index 8600eb2..1d32b54 100644
--- a/arch/sparc/lib/atomic32.c
+++ b/arch/sparc/lib/atomic32.c
@@ -65,7 +65,7 @@
if (ret != u)
v->counter += a;
spin_unlock_irqrestore(ATOMIC_HASH(v), flags);
- return ret != u;
+ return ret;
}
EXPORT_SYMBOL(__atomic_add_unless);
diff --git a/arch/sparc/lib/ffs.S b/arch/sparc/lib/ffs.S
new file mode 100644
index 0000000..b39389f
--- /dev/null
+++ b/arch/sparc/lib/ffs.S
@@ -0,0 +1,84 @@
+#include <linux/linkage.h>
+
+ .register %g2,#scratch
+
+ .text
+ .align 32
+
+ENTRY(ffs)
+ brnz,pt %o0, 1f
+ mov 1, %o1
+ retl
+ clr %o0
+ nop
+ nop
+ENTRY(__ffs)
+ sllx %o0, 32, %g1 /* 1 */
+ srlx %o0, 32, %g2
+
+ clr %o1 /* 2 */
+ movrz %g1, %g2, %o0
+
+ movrz %g1, 32, %o1 /* 3 */
+1: clr %o2
+
+ sllx %o0, (64 - 16), %g1 /* 4 */
+ srlx %o0, 16, %g2
+
+ movrz %g1, %g2, %o0 /* 5 */
+ clr %o3
+
+ movrz %g1, 16, %o2 /* 6 */
+ clr %o4
+
+ and %o0, 0xff, %g1 /* 7 */
+ srlx %o0, 8, %g2
+
+ movrz %g1, %g2, %o0 /* 8 */
+ clr %o5
+
+ movrz %g1, 8, %o3 /* 9 */
+ add %o2, %o1, %o2
+
+ and %o0, 0xf, %g1 /* 10 */
+ srlx %o0, 4, %g2
+
+ movrz %g1, %g2, %o0 /* 11 */
+ add %o2, %o3, %o2
+
+ movrz %g1, 4, %o4 /* 12 */
+
+ and %o0, 0x3, %g1 /* 13 */
+ srlx %o0, 2, %g2
+
+ movrz %g1, %g2, %o0 /* 14 */
+ add %o2, %o4, %o2
+
+ movrz %g1, 2, %o5 /* 15 */
+
+ and %o0, 0x1, %g1 /* 16 */
+
+ add %o2, %o5, %o2 /* 17 */
+ xor %g1, 0x1, %g1
+
+ retl /* 18 */
+ add %o2, %g1, %o0
+ENDPROC(ffs)
+ENDPROC(__ffs)
+
+ .section .popc_6insn_patch, "ax"
+ .word ffs
+ brz,pn %o0, 98f
+ neg %o0, %g1
+ xnor %o0, %g1, %o1
+ popc %o1, %o0
+98: retl
+ nop
+ .word __ffs
+ neg %o0, %g1
+ xnor %o0, %g1, %o1
+ popc %o1, %o0
+ retl
+ sub %o0, 1, %o0
+ nop
+ .previous
diff --git a/arch/sparc/lib/hweight.S b/arch/sparc/lib/hweight.S
new file mode 100644
index 0000000..95414e0
--- /dev/null
+++ b/arch/sparc/lib/hweight.S
@@ -0,0 +1,51 @@
+#include <linux/linkage.h>
+
+ .text
+ .align 32
+ENTRY(__arch_hweight8)
+ ba,pt %xcc, __sw_hweight8
+ nop
+ nop
+ENDPROC(__arch_hweight8)
+ .section .popc_3insn_patch, "ax"
+ .word __arch_hweight8
+ sllx %o0, 64-8, %g1
+ retl
+ popc %g1, %o0
+ .previous
+
+ENTRY(__arch_hweight16)
+ ba,pt %xcc, __sw_hweight16
+ nop
+ nop
+ENDPROC(__arch_hweight16)
+ .section .popc_3insn_patch, "ax"
+ .word __arch_hweight16
+ sllx %o0, 64-16, %g1
+ retl
+ popc %g1, %o0
+ .previous
+
+ENTRY(__arch_hweight32)
+ ba,pt %xcc, __sw_hweight32
+ nop
+ nop
+ENDPROC(__arch_hweight32)
+ .section .popc_3insn_patch, "ax"
+ .word __arch_hweight32
+ sllx %o0, 64-32, %g1
+ retl
+ popc %g1, %o0
+ .previous
+
+ENTRY(__arch_hweight64)
+ ba,pt %xcc, __sw_hweight64
+ nop
+ nop
+ENDPROC(__arch_hweight64)
+ .section .popc_3insn_patch, "ax"
+ .word __arch_hweight64
+ retl
+ popc %o0, %o0
+ nop
+ .previous
diff --git a/arch/sparc/mm/init_64.c b/arch/sparc/mm/init_64.c
index 3fd8e18..581531d 100644
--- a/arch/sparc/mm/init_64.c
+++ b/arch/sparc/mm/init_64.c
@@ -1597,6 +1597,44 @@
static struct hv_tsb_descr ktsb_descr[NUM_KTSB_DESCR];
extern struct tsb swapper_tsb[KERNEL_TSB_NENTRIES];
+static void patch_one_ktsb_phys(unsigned int *start, unsigned int *end, unsigned long pa)
+{
+ pa >>= KTSB_PHYS_SHIFT;
+
+ while (start < end) {
+ unsigned int *ia = (unsigned int *)(unsigned long)*start;
+
+ ia[0] = (ia[0] & ~0x3fffff) | (pa >> 10);
+ __asm__ __volatile__("flush %0" : : "r" (ia));
+
+ ia[1] = (ia[1] & ~0x3ff) | (pa & 0x3ff);
+ __asm__ __volatile__("flush %0" : : "r" (ia + 1));
+
+ start++;
+ }
+}
+
+static void ktsb_phys_patch(void)
+{
+ extern unsigned int __swapper_tsb_phys_patch;
+ extern unsigned int __swapper_tsb_phys_patch_end;
+ unsigned long ktsb_pa;
+
+ ktsb_pa = kern_base + ((unsigned long)&swapper_tsb[0] - KERNBASE);
+ patch_one_ktsb_phys(&__swapper_tsb_phys_patch,
+ &__swapper_tsb_phys_patch_end, ktsb_pa);
+#ifndef CONFIG_DEBUG_PAGEALLOC
+ {
+ extern unsigned int __swapper_4m_tsb_phys_patch;
+ extern unsigned int __swapper_4m_tsb_phys_patch_end;
+ ktsb_pa = (kern_base +
+ ((unsigned long)&swapper_4m_tsb[0] - KERNBASE));
+ patch_one_ktsb_phys(&__swapper_4m_tsb_phys_patch,
+ &__swapper_4m_tsb_phys_patch_end, ktsb_pa);
+ }
+#endif
+}
+
static void __init sun4v_ktsb_init(void)
{
unsigned long ktsb_pa;
@@ -1716,8 +1754,10 @@
sun4u_pgprot_init();
if (tlb_type == cheetah_plus ||
- tlb_type == hypervisor)
+ tlb_type == hypervisor) {
tsb_phys_patch();
+ ktsb_phys_patch();
+ }
if (tlb_type == hypervisor) {
sun4v_patch_tlb_handlers();
diff --git a/arch/tile/Kconfig b/arch/tile/Kconfig
index 0249b8b..b30f71a 100644
--- a/arch/tile/Kconfig
+++ b/arch/tile/Kconfig
@@ -12,6 +12,7 @@
select GENERIC_PENDING_IRQ if SMP
select GENERIC_IRQ_SHOW
select SYS_HYPERVISOR
+ select ARCH_HAVE_NMI_SAFE_CMPXCHG if !M386
# FIXME: investigate whether we need/want these options.
# select HAVE_IOREMAP_PROT
diff --git a/arch/tile/include/asm/Kbuild b/arch/tile/include/asm/Kbuild
index 849ab2f..aec60dc 100644
--- a/arch/tile/include/asm/Kbuild
+++ b/arch/tile/include/asm/Kbuild
@@ -2,3 +2,41 @@
header-y += ucontext.h
header-y += hardwall.h
+
+generic-y += bug.h
+generic-y += bugs.h
+generic-y += cputime.h
+generic-y += device.h
+generic-y += div64.h
+generic-y += emergency-restart.h
+generic-y += errno.h
+generic-y += fb.h
+generic-y += fcntl.h
+generic-y += ioctl.h
+generic-y += ioctls.h
+generic-y += ipc.h
+generic-y += ipcbuf.h
+generic-y += irq_regs.h
+generic-y += kdebug.h
+generic-y += local.h
+generic-y += module.h
+generic-y += msgbuf.h
+generic-y += mutex.h
+generic-y += param.h
+generic-y += parport.h
+generic-y += poll.h
+generic-y += posix_types.h
+generic-y += resource.h
+generic-y += scatterlist.h
+generic-y += sembuf.h
+generic-y += serial.h
+generic-y += shmbuf.h
+generic-y += shmparam.h
+generic-y += socket.h
+generic-y += sockios.h
+generic-y += statfs.h
+generic-y += termbits.h
+generic-y += termios.h
+generic-y += types.h
+generic-y += ucontext.h
+generic-y += xor.h
diff --git a/arch/tile/include/asm/bug.h b/arch/tile/include/asm/bug.h
deleted file mode 100644
index b12fd89..0000000
--- a/arch/tile/include/asm/bug.h
+++ /dev/null
@@ -1 +0,0 @@
-#include <asm-generic/bug.h>
diff --git a/arch/tile/include/asm/bugs.h b/arch/tile/include/asm/bugs.h
deleted file mode 100644
index 61791e1..0000000
--- a/arch/tile/include/asm/bugs.h
+++ /dev/null
@@ -1 +0,0 @@
-#include <asm-generic/bugs.h>
diff --git a/arch/tile/include/asm/cputime.h b/arch/tile/include/asm/cputime.h
deleted file mode 100644
index 6d68ad7..0000000
--- a/arch/tile/include/asm/cputime.h
+++ /dev/null
@@ -1 +0,0 @@
-#include <asm-generic/cputime.h>
diff --git a/arch/tile/include/asm/device.h b/arch/tile/include/asm/device.h
deleted file mode 100644
index f0a4c25..0000000
--- a/arch/tile/include/asm/device.h
+++ /dev/null
@@ -1 +0,0 @@
-#include <asm-generic/device.h>
diff --git a/arch/tile/include/asm/div64.h b/arch/tile/include/asm/div64.h
deleted file mode 100644
index 6cd978c..0000000
--- a/arch/tile/include/asm/div64.h
+++ /dev/null
@@ -1 +0,0 @@
-#include <asm-generic/div64.h>
diff --git a/arch/tile/include/asm/emergency-restart.h b/arch/tile/include/asm/emergency-restart.h
deleted file mode 100644
index 3711bd9..0000000
--- a/arch/tile/include/asm/emergency-restart.h
+++ /dev/null
@@ -1 +0,0 @@
-#include <asm-generic/emergency-restart.h>
diff --git a/arch/tile/include/asm/errno.h b/arch/tile/include/asm/errno.h
deleted file mode 100644
index 4c82b50..0000000
--- a/arch/tile/include/asm/errno.h
+++ /dev/null
@@ -1 +0,0 @@
-#include <asm-generic/errno.h>
diff --git a/arch/tile/include/asm/fb.h b/arch/tile/include/asm/fb.h
deleted file mode 100644
index 3a4988e..0000000
--- a/arch/tile/include/asm/fb.h
+++ /dev/null
@@ -1 +0,0 @@
-#include <asm-generic/fb.h>
diff --git a/arch/tile/include/asm/fcntl.h b/arch/tile/include/asm/fcntl.h
deleted file mode 100644
index 46ab12d..0000000
--- a/arch/tile/include/asm/fcntl.h
+++ /dev/null
@@ -1 +0,0 @@
-#include <asm-generic/fcntl.h>
diff --git a/arch/tile/include/asm/fixmap.h b/arch/tile/include/asm/fixmap.h
index 51537ff..c66f793 100644
--- a/arch/tile/include/asm/fixmap.h
+++ b/arch/tile/include/asm/fixmap.h
@@ -75,12 +75,6 @@
#define set_fixmap(idx, phys) \
__set_fixmap(idx, phys, PAGE_KERNEL)
-/*
- * Some hardware wants to get fixmapped without caching.
- */
-#define set_fixmap_nocache(idx, phys) \
- __set_fixmap(idx, phys, PAGE_KERNEL_NOCACHE)
-
#define clear_fixmap(idx) \
__set_fixmap(idx, 0, __pgprot(0))
diff --git a/arch/tile/include/asm/ioctl.h b/arch/tile/include/asm/ioctl.h
deleted file mode 100644
index b279fe0..0000000
--- a/arch/tile/include/asm/ioctl.h
+++ /dev/null
@@ -1 +0,0 @@
-#include <asm-generic/ioctl.h>
diff --git a/arch/tile/include/asm/ioctls.h b/arch/tile/include/asm/ioctls.h
deleted file mode 100644
index ec34c76..0000000
--- a/arch/tile/include/asm/ioctls.h
+++ /dev/null
@@ -1 +0,0 @@
-#include <asm-generic/ioctls.h>
diff --git a/arch/tile/include/asm/ipc.h b/arch/tile/include/asm/ipc.h
deleted file mode 100644
index a46e3d9..0000000
--- a/arch/tile/include/asm/ipc.h
+++ /dev/null
@@ -1 +0,0 @@
-#include <asm-generic/ipc.h>
diff --git a/arch/tile/include/asm/ipcbuf.h b/arch/tile/include/asm/ipcbuf.h
deleted file mode 100644
index 84c7e51..0000000
--- a/arch/tile/include/asm/ipcbuf.h
+++ /dev/null
@@ -1 +0,0 @@
-#include <asm-generic/ipcbuf.h>
diff --git a/arch/tile/include/asm/irq_regs.h b/arch/tile/include/asm/irq_regs.h
deleted file mode 100644
index 3dd9c0b..0000000
--- a/arch/tile/include/asm/irq_regs.h
+++ /dev/null
@@ -1 +0,0 @@
-#include <asm-generic/irq_regs.h>
diff --git a/arch/tile/include/asm/kdebug.h b/arch/tile/include/asm/kdebug.h
deleted file mode 100644
index 6ece1b0..0000000
--- a/arch/tile/include/asm/kdebug.h
+++ /dev/null
@@ -1 +0,0 @@
-#include <asm-generic/kdebug.h>
diff --git a/arch/tile/include/asm/local.h b/arch/tile/include/asm/local.h
deleted file mode 100644
index c11c530..0000000
--- a/arch/tile/include/asm/local.h
+++ /dev/null
@@ -1 +0,0 @@
-#include <asm-generic/local.h>
diff --git a/arch/tile/include/asm/module.h b/arch/tile/include/asm/module.h
deleted file mode 100644
index 1e4b79f..0000000
--- a/arch/tile/include/asm/module.h
+++ /dev/null
@@ -1 +0,0 @@
-#include <asm-generic/module.h>
diff --git a/arch/tile/include/asm/msgbuf.h b/arch/tile/include/asm/msgbuf.h
deleted file mode 100644
index 809134c..0000000
--- a/arch/tile/include/asm/msgbuf.h
+++ /dev/null
@@ -1 +0,0 @@
-#include <asm-generic/msgbuf.h>
diff --git a/arch/tile/include/asm/mutex.h b/arch/tile/include/asm/mutex.h
deleted file mode 100644
index ff6101a..0000000
--- a/arch/tile/include/asm/mutex.h
+++ /dev/null
@@ -1 +0,0 @@
-#include <asm-generic/mutex-dec.h>
diff --git a/arch/tile/include/asm/param.h b/arch/tile/include/asm/param.h
deleted file mode 100644
index 965d454..0000000
--- a/arch/tile/include/asm/param.h
+++ /dev/null
@@ -1 +0,0 @@
-#include <asm-generic/param.h>
diff --git a/arch/tile/include/asm/parport.h b/arch/tile/include/asm/parport.h
deleted file mode 100644
index cf252af..0000000
--- a/arch/tile/include/asm/parport.h
+++ /dev/null
@@ -1 +0,0 @@
-#include <asm-generic/parport.h>
diff --git a/arch/tile/include/asm/poll.h b/arch/tile/include/asm/poll.h
deleted file mode 100644
index c98509d..0000000
--- a/arch/tile/include/asm/poll.h
+++ /dev/null
@@ -1 +0,0 @@
-#include <asm-generic/poll.h>
diff --git a/arch/tile/include/asm/posix_types.h b/arch/tile/include/asm/posix_types.h
deleted file mode 100644
index 22cae62..0000000
--- a/arch/tile/include/asm/posix_types.h
+++ /dev/null
@@ -1 +0,0 @@
-#include <asm-generic/posix_types.h>
diff --git a/arch/tile/include/asm/resource.h b/arch/tile/include/asm/resource.h
deleted file mode 100644
index 04bc4db..0000000
--- a/arch/tile/include/asm/resource.h
+++ /dev/null
@@ -1 +0,0 @@
-#include <asm-generic/resource.h>
diff --git a/arch/tile/include/asm/scatterlist.h b/arch/tile/include/asm/scatterlist.h
deleted file mode 100644
index 35d786f..0000000
--- a/arch/tile/include/asm/scatterlist.h
+++ /dev/null
@@ -1 +0,0 @@
-#include <asm-generic/scatterlist.h>
diff --git a/arch/tile/include/asm/sembuf.h b/arch/tile/include/asm/sembuf.h
deleted file mode 100644
index 7673b83..0000000
--- a/arch/tile/include/asm/sembuf.h
+++ /dev/null
@@ -1 +0,0 @@
-#include <asm-generic/sembuf.h>
diff --git a/arch/tile/include/asm/serial.h b/arch/tile/include/asm/serial.h
deleted file mode 100644
index a0cb0ca..0000000
--- a/arch/tile/include/asm/serial.h
+++ /dev/null
@@ -1 +0,0 @@
-#include <asm-generic/serial.h>
diff --git a/arch/tile/include/asm/shmbuf.h b/arch/tile/include/asm/shmbuf.h
deleted file mode 100644
index 83c05fc..0000000
--- a/arch/tile/include/asm/shmbuf.h
+++ /dev/null
@@ -1 +0,0 @@
-#include <asm-generic/shmbuf.h>
diff --git a/arch/tile/include/asm/shmparam.h b/arch/tile/include/asm/shmparam.h
deleted file mode 100644
index 93f30de..0000000
--- a/arch/tile/include/asm/shmparam.h
+++ /dev/null
@@ -1 +0,0 @@
-#include <asm-generic/shmparam.h>
diff --git a/arch/tile/include/asm/socket.h b/arch/tile/include/asm/socket.h
deleted file mode 100644
index 6b71384..0000000
--- a/arch/tile/include/asm/socket.h
+++ /dev/null
@@ -1 +0,0 @@
-#include <asm-generic/socket.h>
diff --git a/arch/tile/include/asm/sockios.h b/arch/tile/include/asm/sockios.h
deleted file mode 100644
index def6d47..0000000
--- a/arch/tile/include/asm/sockios.h
+++ /dev/null
@@ -1 +0,0 @@
-#include <asm-generic/sockios.h>
diff --git a/arch/tile/include/asm/statfs.h b/arch/tile/include/asm/statfs.h
deleted file mode 100644
index 0b91fe1..0000000
--- a/arch/tile/include/asm/statfs.h
+++ /dev/null
@@ -1 +0,0 @@
-#include <asm-generic/statfs.h>
diff --git a/arch/tile/include/asm/termbits.h b/arch/tile/include/asm/termbits.h
deleted file mode 100644
index 3935b10..0000000
--- a/arch/tile/include/asm/termbits.h
+++ /dev/null
@@ -1 +0,0 @@
-#include <asm-generic/termbits.h>
diff --git a/arch/tile/include/asm/termios.h b/arch/tile/include/asm/termios.h
deleted file mode 100644
index 280d78a..0000000
--- a/arch/tile/include/asm/termios.h
+++ /dev/null
@@ -1 +0,0 @@
-#include <asm-generic/termios.h>
diff --git a/arch/tile/include/asm/types.h b/arch/tile/include/asm/types.h
deleted file mode 100644
index b9e79bc..0000000
--- a/arch/tile/include/asm/types.h
+++ /dev/null
@@ -1 +0,0 @@
-#include <asm-generic/types.h>
diff --git a/arch/tile/include/asm/ucontext.h b/arch/tile/include/asm/ucontext.h
deleted file mode 100644
index 9bc07b9..0000000
--- a/arch/tile/include/asm/ucontext.h
+++ /dev/null
@@ -1 +0,0 @@
-#include <asm-generic/ucontext.h>
diff --git a/arch/tile/include/asm/xor.h b/arch/tile/include/asm/xor.h
deleted file mode 100644
index c82eb12..0000000
--- a/arch/tile/include/asm/xor.h
+++ /dev/null
@@ -1 +0,0 @@
-#include <asm-generic/xor.h>
diff --git a/arch/tile/include/hv/drv_srom_intf.h b/arch/tile/include/hv/drv_srom_intf.h
new file mode 100644
index 0000000..6395faa
--- /dev/null
+++ b/arch/tile/include/hv/drv_srom_intf.h
@@ -0,0 +1,41 @@
+/*
+ * Copyright 2011 Tilera Corporation. All Rights Reserved.
+ *
+ * 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 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, GOOD TITLE or
+ * NON INFRINGEMENT. See the GNU General Public License for
+ * more details.
+ */
+
+/**
+ * @file drv_srom_intf.h
+ * Interface definitions for the SPI Flash ROM driver.
+ */
+
+#ifndef _SYS_HV_INCLUDE_DRV_SROM_INTF_H
+#define _SYS_HV_INCLUDE_DRV_SROM_INTF_H
+
+/** Read this offset to get the total device size. */
+#define SROM_TOTAL_SIZE_OFF 0xF0000000
+
+/** Read this offset to get the device sector size. */
+#define SROM_SECTOR_SIZE_OFF 0xF0000004
+
+/** Read this offset to get the device page size. */
+#define SROM_PAGE_SIZE_OFF 0xF0000008
+
+/** Write this offset to flush any pending writes. */
+#define SROM_FLUSH_OFF 0xF1000000
+
+/** Write this offset, plus the byte offset of the start of a sector, to
+ * erase a sector. Any write data is ignored, but there must be at least
+ * one byte of write data. Only applies when the driver is in MTD mode.
+ */
+#define SROM_ERASE_OFF 0xF2000000
+
+#endif /* _SYS_HV_INCLUDE_DRV_SROM_INTF_H */
diff --git a/arch/tile/kernel/time.c b/arch/tile/kernel/time.c
index c4be58c..f6f50f2a 100644
--- a/arch/tile/kernel/time.c
+++ b/arch/tile/kernel/time.c
@@ -78,7 +78,6 @@
.rating = 300,
.read = clocksource_get_cycles,
.mask = CLOCKSOURCE_MASK(64),
- .shift = 22, /* typical value, e.g. x86 tsc uses this */
.flags = CLOCK_SOURCE_IS_CONTINUOUS,
};
@@ -91,8 +90,6 @@
cycles_per_sec = hv_sysconf(HV_SYSCONF_CPU_SPEED);
sched_clock_mult =
clocksource_hz2mult(cycles_per_sec, SCHED_CLOCK_SHIFT);
- cycle_counter_cs.mult =
- clocksource_hz2mult(cycles_per_sec, cycle_counter_cs.shift);
}
void __init calibrate_delay(void)
@@ -107,7 +104,7 @@
void __init time_init(void)
{
/* Initialize and register the clock source. */
- clocksource_register(&cycle_counter_cs);
+ clocksource_register_hz(&cycle_counter_cs, cycles_per_sec);
/* Start up the tile-timer interrupt source on the boot cpu. */
setup_tile_timer();
diff --git a/arch/tile/mm/init.c b/arch/tile/mm/init.c
index 4e10c40..7309988 100644
--- a/arch/tile/mm/init.c
+++ b/arch/tile/mm/init.c
@@ -836,8 +836,7 @@
#endif
#ifdef CONFIG_FLATMEM
- if (!mem_map)
- BUG();
+ BUG_ON(!mem_map);
#endif
#ifdef CONFIG_HIGHMEM
diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig
index 7cf916f..6a47bb2 100644
--- a/arch/x86/Kconfig
+++ b/arch/x86/Kconfig
@@ -72,6 +72,7 @@
select USE_GENERIC_SMP_HELPERS if SMP
select HAVE_BPF_JIT if (X86_64 && NET)
select CLKEVT_I8253
+ select ARCH_HAVE_NMI_SAFE_CMPXCHG
config INSTRUCTION_DECODER
def_bool (KPROBES || PERF_EVENTS)
diff --git a/arch/x86/include/asm/desc.h b/arch/x86/include/asm/desc.h
index 7b439d9..41935fa 100644
--- a/arch/x86/include/asm/desc.h
+++ b/arch/x86/include/asm/desc.h
@@ -27,8 +27,8 @@
desc->base2 = (info->base_addr & 0xff000000) >> 24;
/*
- * Don't allow setting of the lm bit. It is useless anyway
- * because 64bit system calls require __USER_CS:
+ * Don't allow setting of the lm bit. It would confuse
+ * user_64bit_mode and would get overridden by sysret anyway.
*/
desc->l = 0;
}
diff --git a/arch/x86/include/asm/io.h b/arch/x86/include/asm/io.h
index d02804d..d8e8eef 100644
--- a/arch/x86/include/asm/io.h
+++ b/arch/x86/include/asm/io.h
@@ -40,8 +40,6 @@
#include <linux/compiler.h>
#include <asm/page.h>
-#include <xen/xen.h>
-
#define build_mmio_read(name, size, type, reg, barrier) \
static inline type name(const volatile void __iomem *addr) \
{ type ret; asm volatile("mov" size " %1,%0":reg (ret) \
@@ -334,6 +332,7 @@
extern bool is_early_ioremap_ptep(pte_t *ptep);
#ifdef CONFIG_XEN
+#include <xen/xen.h>
struct bio_vec;
extern bool xen_biovec_phys_mergeable(const struct bio_vec *vec1,
diff --git a/arch/x86/include/asm/irq_vectors.h b/arch/x86/include/asm/irq_vectors.h
index f9a3209..7e50f06 100644
--- a/arch/x86/include/asm/irq_vectors.h
+++ b/arch/x86/include/asm/irq_vectors.h
@@ -17,7 +17,6 @@
* Vectors 0 ... 31 : system traps and exceptions - hardcoded events
* Vectors 32 ... 127 : device interrupts
* Vector 128 : legacy int80 syscall interface
- * Vector 204 : legacy x86_64 vsyscall emulation
* Vectors 129 ... INVALIDATE_TLB_VECTOR_START-1 except 204 : device interrupts
* Vectors INVALIDATE_TLB_VECTOR_START ... 255 : special interrupts
*
@@ -51,9 +50,6 @@
#ifdef CONFIG_X86_32
# define SYSCALL_VECTOR 0x80
#endif
-#ifdef CONFIG_X86_64
-# define VSYSCALL_EMU_VECTOR 0xcc
-#endif
/*
* Vectors 0x30-0x3f are used for ISA interrupts.
diff --git a/arch/x86/include/asm/paravirt_types.h b/arch/x86/include/asm/paravirt_types.h
index 2c76521..8e8b9a4 100644
--- a/arch/x86/include/asm/paravirt_types.h
+++ b/arch/x86/include/asm/paravirt_types.h
@@ -41,6 +41,7 @@
#include <asm/desc_defs.h>
#include <asm/kmap_types.h>
+#include <asm/pgtable_types.h>
struct page;
struct thread_struct;
@@ -63,6 +64,11 @@
struct pv_info {
unsigned int kernel_rpl;
int shared_kernel_pmd;
+
+#ifdef CONFIG_X86_64
+ u16 extra_user_64bit_cs; /* __USER_CS if none */
+#endif
+
int paravirt_enabled;
const char *name;
};
diff --git a/arch/x86/include/asm/processor.h b/arch/x86/include/asm/processor.h
index 2193715..0d1171c 100644
--- a/arch/x86/include/asm/processor.h
+++ b/arch/x86/include/asm/processor.h
@@ -751,8 +751,6 @@
:: "a" (eax), "c" (ecx));
}
-extern void mwait_idle_with_hints(unsigned long eax, unsigned long ecx);
-
extern void select_idle_routine(const struct cpuinfo_x86 *c);
extern void init_amd_e400_c1e_mask(void);
diff --git a/arch/x86/include/asm/ptrace.h b/arch/x86/include/asm/ptrace.h
index 94e7618..3566454 100644
--- a/arch/x86/include/asm/ptrace.h
+++ b/arch/x86/include/asm/ptrace.h
@@ -131,6 +131,9 @@
#ifdef __KERNEL__
#include <linux/init.h>
+#ifdef CONFIG_PARAVIRT
+#include <asm/paravirt_types.h>
+#endif
struct cpuinfo_x86;
struct task_struct;
@@ -187,6 +190,22 @@
#endif
}
+#ifdef CONFIG_X86_64
+static inline bool user_64bit_mode(struct pt_regs *regs)
+{
+#ifndef CONFIG_PARAVIRT
+ /*
+ * On non-paravirt systems, this is the only long mode CPL 3
+ * selector. We do not allow long mode selectors in the LDT.
+ */
+ return regs->cs == __USER_CS;
+#else
+ /* Headers are too twisted for this to go in paravirt.h. */
+ return regs->cs == __USER_CS || regs->cs == pv_info.extra_user_64bit_cs;
+#endif
+}
+#endif
+
/*
* X86_32 CPUs don't save ss and esp if the CPU is already in kernel mode
* when it traps. The previous stack will be directly underneath the saved
diff --git a/arch/x86/include/asm/traps.h b/arch/x86/include/asm/traps.h
index 2bae0a5..0012d09 100644
--- a/arch/x86/include/asm/traps.h
+++ b/arch/x86/include/asm/traps.h
@@ -40,7 +40,6 @@
asmlinkage void machine_check(void);
#endif /* CONFIG_X86_MCE */
asmlinkage void simd_coprocessor_error(void);
-asmlinkage void emulate_vsyscall(void);
dotraplinkage void do_divide_error(struct pt_regs *, long);
dotraplinkage void do_debug(struct pt_regs *, long);
@@ -67,7 +66,6 @@
dotraplinkage void do_machine_check(struct pt_regs *, long);
#endif
dotraplinkage void do_simd_coprocessor_error(struct pt_regs *, long);
-dotraplinkage void do_emulate_vsyscall(struct pt_regs *, long);
#ifdef CONFIG_X86_32
dotraplinkage void do_iret_error(struct pt_regs *, long);
#endif
diff --git a/arch/x86/include/asm/unistd_64.h b/arch/x86/include/asm/unistd_64.h
index 705bf13..d92641c 100644
--- a/arch/x86/include/asm/unistd_64.h
+++ b/arch/x86/include/asm/unistd_64.h
@@ -681,6 +681,8 @@
__SYSCALL(__NR_sendmmsg, sys_sendmmsg)
#define __NR_setns 308
__SYSCALL(__NR_setns, sys_setns)
+#define __NR_getcpu 309
+__SYSCALL(__NR_getcpu, sys_getcpu)
#ifndef __NO_STUBS
#define __ARCH_WANT_OLD_READDIR
diff --git a/arch/x86/include/asm/vsyscall.h b/arch/x86/include/asm/vsyscall.h
index 6010707..eaea1d3 100644
--- a/arch/x86/include/asm/vsyscall.h
+++ b/arch/x86/include/asm/vsyscall.h
@@ -27,6 +27,12 @@
extern void map_vsyscall(void);
+/*
+ * Called on instruction fetch fault in vsyscall page.
+ * Returns true if handled.
+ */
+extern bool emulate_vsyscall(struct pt_regs *regs, unsigned long address);
+
#endif /* __KERNEL__ */
#endif /* _ASM_X86_VSYSCALL_H */
diff --git a/arch/x86/kernel/Makefile b/arch/x86/kernel/Makefile
index 0410557..82f2912 100644
--- a/arch/x86/kernel/Makefile
+++ b/arch/x86/kernel/Makefile
@@ -17,19 +17,6 @@
CFLAGS_REMOVE_early_printk.o = -pg
endif
-#
-# vsyscalls (which work on the user stack) should have
-# no stack-protector checks:
-#
-nostackp := $(call cc-option, -fno-stack-protector)
-CFLAGS_vsyscall_64.o := $(PROFILING) -g0 $(nostackp)
-CFLAGS_hpet.o := $(nostackp)
-CFLAGS_paravirt.o := $(nostackp)
-GCOV_PROFILE_vsyscall_64.o := n
-GCOV_PROFILE_hpet.o := n
-GCOV_PROFILE_tsc.o := n
-GCOV_PROFILE_paravirt.o := n
-
obj-y := process_$(BITS).o signal.o entry_$(BITS).o
obj-y += traps.o irq.o irq_$(BITS).o dumpstack_$(BITS).o
obj-y += time.o ioport.o ldt.o dumpstack.o
diff --git a/arch/x86/kernel/acpi/cstate.c b/arch/x86/kernel/acpi/cstate.c
index 5812404..f50e7fb 100644
--- a/arch/x86/kernel/acpi/cstate.c
+++ b/arch/x86/kernel/acpi/cstate.c
@@ -149,6 +149,29 @@
}
EXPORT_SYMBOL_GPL(acpi_processor_ffh_cstate_probe);
+/*
+ * This uses new MONITOR/MWAIT instructions on P4 processors with PNI,
+ * which can obviate IPI to trigger checking of need_resched.
+ * We execute MONITOR against need_resched and enter optimized wait state
+ * through MWAIT. Whenever someone changes need_resched, we would be woken
+ * up from MWAIT (without an IPI).
+ *
+ * New with Core Duo processors, MWAIT can take some hints based on CPU
+ * capability.
+ */
+void mwait_idle_with_hints(unsigned long ax, unsigned long cx)
+{
+ if (!need_resched()) {
+ if (this_cpu_has(X86_FEATURE_CLFLUSH_MONITOR))
+ clflush((void *)¤t_thread_info()->flags);
+
+ __monitor((void *)¤t_thread_info()->flags, 0, 0);
+ smp_mb();
+ if (!need_resched())
+ __mwait(ax, cx);
+ }
+}
+
void acpi_processor_ffh_cstate_enter(struct acpi_processor_cx *cx)
{
unsigned int cpu = smp_processor_id();
diff --git a/arch/x86/kernel/cpu/perf_event_intel.c b/arch/x86/kernel/cpu/perf_event_intel.c
index 45fbb8f..f88af2c 100644
--- a/arch/x86/kernel/cpu/perf_event_intel.c
+++ b/arch/x86/kernel/cpu/perf_event_intel.c
@@ -1590,6 +1590,7 @@
break;
case 42: /* SandyBridge */
+ case 45: /* SandyBridge, "Romely-EP" */
memcpy(hw_cache_event_ids, snb_hw_cache_event_ids,
sizeof(hw_cache_event_ids));
diff --git a/arch/x86/kernel/entry_64.S b/arch/x86/kernel/entry_64.S
index e13329d..6419bb0 100644
--- a/arch/x86/kernel/entry_64.S
+++ b/arch/x86/kernel/entry_64.S
@@ -1111,7 +1111,6 @@
zeroentry coprocessor_error do_coprocessor_error
errorentry alignment_check do_alignment_check
zeroentry simd_coprocessor_error do_simd_coprocessor_error
-zeroentry emulate_vsyscall do_emulate_vsyscall
/* Reload gs selector with exception handling */
diff --git a/arch/x86/kernel/paravirt.c b/arch/x86/kernel/paravirt.c
index 613a793..d90272e 100644
--- a/arch/x86/kernel/paravirt.c
+++ b/arch/x86/kernel/paravirt.c
@@ -307,6 +307,10 @@
.paravirt_enabled = 0,
.kernel_rpl = 0,
.shared_kernel_pmd = 1, /* Only used when CONFIG_X86_PAE is set */
+
+#ifdef CONFIG_X86_64
+ .extra_user_64bit_cs = __USER_CS,
+#endif
};
struct pv_init_ops pv_init_ops = {
diff --git a/arch/x86/kernel/process.c b/arch/x86/kernel/process.c
index e1ba8cb..e7e3b01 100644
--- a/arch/x86/kernel/process.c
+++ b/arch/x86/kernel/process.c
@@ -438,29 +438,6 @@
}
EXPORT_SYMBOL_GPL(cpu_idle_wait);
-/*
- * This uses new MONITOR/MWAIT instructions on P4 processors with PNI,
- * which can obviate IPI to trigger checking of need_resched.
- * We execute MONITOR against need_resched and enter optimized wait state
- * through MWAIT. Whenever someone changes need_resched, we would be woken
- * up from MWAIT (without an IPI).
- *
- * New with Core Duo processors, MWAIT can take some hints based on CPU
- * capability.
- */
-void mwait_idle_with_hints(unsigned long ax, unsigned long cx)
-{
- if (!need_resched()) {
- if (this_cpu_has(X86_FEATURE_CLFLUSH_MONITOR))
- clflush((void *)¤t_thread_info()->flags);
-
- __monitor((void *)¤t_thread_info()->flags, 0, 0);
- smp_mb();
- if (!need_resched())
- __mwait(ax, cx);
- }
-}
-
/* Default MONITOR/MWAIT with no hints, used for default C1 state */
static void mwait_idle(void)
{
diff --git a/arch/x86/kernel/process_32.c b/arch/x86/kernel/process_32.c
index a3d0dc5..7a3b651 100644
--- a/arch/x86/kernel/process_32.c
+++ b/arch/x86/kernel/process_32.c
@@ -38,6 +38,7 @@
#include <linux/uaccess.h>
#include <linux/io.h>
#include <linux/kdebug.h>
+#include <linux/cpuidle.h>
#include <asm/pgtable.h>
#include <asm/system.h>
@@ -109,7 +110,8 @@
local_irq_disable();
/* Don't trace irqs off for idle */
stop_critical_timings();
- pm_idle();
+ if (cpuidle_idle_call())
+ pm_idle();
start_critical_timings();
}
tick_nohz_restart_sched_tick();
diff --git a/arch/x86/kernel/process_64.c b/arch/x86/kernel/process_64.c
index ca6f7ab..f693e44 100644
--- a/arch/x86/kernel/process_64.c
+++ b/arch/x86/kernel/process_64.c
@@ -37,6 +37,7 @@
#include <linux/uaccess.h>
#include <linux/io.h>
#include <linux/ftrace.h>
+#include <linux/cpuidle.h>
#include <asm/pgtable.h>
#include <asm/system.h>
@@ -136,7 +137,8 @@
enter_idle();
/* Don't trace irqs off for idle */
stop_critical_timings();
- pm_idle();
+ if (cpuidle_idle_call())
+ pm_idle();
start_critical_timings();
/* In many cases the interrupt that ended idle
diff --git a/arch/x86/kernel/step.c b/arch/x86/kernel/step.c
index 7977f0c..c346d11 100644
--- a/arch/x86/kernel/step.c
+++ b/arch/x86/kernel/step.c
@@ -74,7 +74,7 @@
#ifdef CONFIG_X86_64
case 0x40 ... 0x4f:
- if (regs->cs != __USER_CS)
+ if (!user_64bit_mode(regs))
/* 32-bit mode: register increment */
return 0;
/* 64-bit mode: REX prefix */
diff --git a/arch/x86/kernel/traps.c b/arch/x86/kernel/traps.c
index 9682ec5..6913369 100644
--- a/arch/x86/kernel/traps.c
+++ b/arch/x86/kernel/traps.c
@@ -872,12 +872,6 @@
set_bit(SYSCALL_VECTOR, used_vectors);
#endif
-#ifdef CONFIG_X86_64
- BUG_ON(test_bit(VSYSCALL_EMU_VECTOR, used_vectors));
- set_system_intr_gate(VSYSCALL_EMU_VECTOR, &emulate_vsyscall);
- set_bit(VSYSCALL_EMU_VECTOR, used_vectors);
-#endif
-
/*
* Should be a barrier for any external CPU state:
*/
diff --git a/arch/x86/kernel/vmlinux.lds.S b/arch/x86/kernel/vmlinux.lds.S
index 4aa9c54..0f703f1 100644
--- a/arch/x86/kernel/vmlinux.lds.S
+++ b/arch/x86/kernel/vmlinux.lds.S
@@ -71,7 +71,6 @@
text PT_LOAD FLAGS(5); /* R_E */
data PT_LOAD FLAGS(6); /* RW_ */
#ifdef CONFIG_X86_64
- user PT_LOAD FLAGS(5); /* R_E */
#ifdef CONFIG_SMP
percpu PT_LOAD FLAGS(6); /* RW_ */
#endif
@@ -154,44 +153,16 @@
#ifdef CONFIG_X86_64
-#define VSYSCALL_ADDR (-10*1024*1024)
-
-#define VLOAD_OFFSET (VSYSCALL_ADDR - __vsyscall_0 + LOAD_OFFSET)
-#define VLOAD(x) (ADDR(x) - VLOAD_OFFSET)
-
-#define VVIRT_OFFSET (VSYSCALL_ADDR - __vsyscall_0)
-#define VVIRT(x) (ADDR(x) - VVIRT_OFFSET)
-
- . = ALIGN(4096);
- __vsyscall_0 = .;
-
- . = VSYSCALL_ADDR;
- .vsyscall : AT(VLOAD(.vsyscall)) {
- *(.vsyscall_0)
-
- . = 1024;
- *(.vsyscall_1)
-
- . = 2048;
- *(.vsyscall_2)
-
- . = 4096; /* Pad the whole page. */
- } :user =0xcc
- . = ALIGN(__vsyscall_0 + PAGE_SIZE, PAGE_SIZE);
-
-#undef VSYSCALL_ADDR
-#undef VLOAD_OFFSET
-#undef VLOAD
-#undef VVIRT_OFFSET
-#undef VVIRT
-
+ . = ALIGN(PAGE_SIZE);
__vvar_page = .;
.vvar : AT(ADDR(.vvar) - LOAD_OFFSET) {
+ /* work around gold bug 13023 */
+ __vvar_beginning_hack = .;
- /* Place all vvars at the offsets in asm/vvar.h. */
-#define EMIT_VVAR(name, offset) \
- . = offset; \
+ /* Place all vvars at the offsets in asm/vvar.h. */
+#define EMIT_VVAR(name, offset) \
+ . = __vvar_beginning_hack + offset; \
*(.vvar_ ## name)
#define __VVAR_KERNEL_LDS
#include <asm/vvar.h>
diff --git a/arch/x86/kernel/vsyscall_64.c b/arch/x86/kernel/vsyscall_64.c
index dda7dff..18ae83d 100644
--- a/arch/x86/kernel/vsyscall_64.c
+++ b/arch/x86/kernel/vsyscall_64.c
@@ -18,9 +18,6 @@
* use the vDSO.
*/
-/* Disable profiling for userspace code: */
-#define DISABLE_BRANCH_PROFILING
-
#include <linux/time.h>
#include <linux/init.h>
#include <linux/kernel.h>
@@ -50,12 +47,36 @@
#include <asm/vgtod.h>
#include <asm/traps.h>
+#define CREATE_TRACE_POINTS
+#include "vsyscall_trace.h"
+
DEFINE_VVAR(int, vgetcpu_mode);
DEFINE_VVAR(struct vsyscall_gtod_data, vsyscall_gtod_data) =
{
.lock = __SEQLOCK_UNLOCKED(__vsyscall_gtod_data.lock),
};
+static enum { EMULATE, NATIVE, NONE } vsyscall_mode = EMULATE;
+
+static int __init vsyscall_setup(char *str)
+{
+ if (str) {
+ if (!strcmp("emulate", str))
+ vsyscall_mode = EMULATE;
+ else if (!strcmp("native", str))
+ vsyscall_mode = NATIVE;
+ else if (!strcmp("none", str))
+ vsyscall_mode = NONE;
+ else
+ return -EINVAL;
+
+ return 0;
+ }
+
+ return -EINVAL;
+}
+early_param("vsyscall", vsyscall_setup);
+
void update_vsyscall_tz(void)
{
unsigned long flags;
@@ -100,7 +121,7 @@
printk("%s%s[%d] %s ip:%lx cs:%lx sp:%lx ax:%lx si:%lx di:%lx\n",
level, tsk->comm, task_pid_nr(tsk),
- message, regs->ip - 2, regs->cs,
+ message, regs->ip, regs->cs,
regs->sp, regs->ax, regs->si, regs->di);
}
@@ -118,46 +139,39 @@
return nr;
}
-void dotraplinkage do_emulate_vsyscall(struct pt_regs *regs, long error_code)
+bool emulate_vsyscall(struct pt_regs *regs, unsigned long address)
{
struct task_struct *tsk;
unsigned long caller;
int vsyscall_nr;
long ret;
- local_irq_enable();
-
/*
- * Real 64-bit user mode code has cs == __USER_CS. Anything else
- * is bogus.
+ * No point in checking CS -- the only way to get here is a user mode
+ * trap to a high address, which means that we're in 64-bit user code.
*/
- if (regs->cs != __USER_CS) {
- /*
- * If we trapped from kernel mode, we might as well OOPS now
- * instead of returning to some random address and OOPSing
- * then.
- */
- BUG_ON(!user_mode(regs));
- /* Compat mode and non-compat 32-bit CS should both segfault. */
- warn_bad_vsyscall(KERN_WARNING, regs,
- "illegal int 0xcc from 32-bit mode");
- goto sigsegv;
+ WARN_ON_ONCE(address != regs->ip);
+
+ if (vsyscall_mode == NONE) {
+ warn_bad_vsyscall(KERN_INFO, regs,
+ "vsyscall attempted with vsyscall=none");
+ return false;
}
- /*
- * x86-ism here: regs->ip points to the instruction after the int 0xcc,
- * and int 0xcc is two bytes long.
- */
- vsyscall_nr = addr_to_vsyscall_nr(regs->ip - 2);
+ vsyscall_nr = addr_to_vsyscall_nr(address);
+
+ trace_emulate_vsyscall(vsyscall_nr);
+
if (vsyscall_nr < 0) {
warn_bad_vsyscall(KERN_WARNING, regs,
- "illegal int 0xcc (exploit attempt?)");
+ "misaligned vsyscall (exploit attempt or buggy program) -- look up the vsyscall kernel parameter if you need a workaround");
goto sigsegv;
}
if (get_user(caller, (unsigned long __user *)regs->sp) != 0) {
- warn_bad_vsyscall(KERN_WARNING, regs, "int 0xcc with bad stack (exploit attempt?)");
+ warn_bad_vsyscall(KERN_WARNING, regs,
+ "vsyscall with bad stack (exploit attempt?)");
goto sigsegv;
}
@@ -202,13 +216,11 @@
regs->ip = caller;
regs->sp += 8;
- local_irq_disable();
- return;
+ return true;
sigsegv:
- regs->ip -= 2; /* The faulting instruction should be the int 0xcc. */
force_sig(SIGSEGV, current);
- local_irq_disable();
+ return true;
}
/*
@@ -256,15 +268,21 @@
void __init map_vsyscall(void)
{
- extern char __vsyscall_0;
- unsigned long physaddr_page0 = __pa_symbol(&__vsyscall_0);
+ extern char __vsyscall_page;
+ unsigned long physaddr_vsyscall = __pa_symbol(&__vsyscall_page);
extern char __vvar_page;
unsigned long physaddr_vvar_page = __pa_symbol(&__vvar_page);
- /* Note that VSYSCALL_MAPPED_PAGES must agree with the code below. */
- __set_fixmap(VSYSCALL_FIRST_PAGE, physaddr_page0, PAGE_KERNEL_VSYSCALL);
+ __set_fixmap(VSYSCALL_FIRST_PAGE, physaddr_vsyscall,
+ vsyscall_mode == NATIVE
+ ? PAGE_KERNEL_VSYSCALL
+ : PAGE_KERNEL_VVAR);
+ BUILD_BUG_ON((unsigned long)__fix_to_virt(VSYSCALL_FIRST_PAGE) !=
+ (unsigned long)VSYSCALL_START);
+
__set_fixmap(VVAR_PAGE, physaddr_vvar_page, PAGE_KERNEL_VVAR);
- BUILD_BUG_ON((unsigned long)__fix_to_virt(VVAR_PAGE) != (unsigned long)VVAR_ADDRESS);
+ BUILD_BUG_ON((unsigned long)__fix_to_virt(VVAR_PAGE) !=
+ (unsigned long)VVAR_ADDRESS);
}
static int __init vsyscall_init(void)
diff --git a/arch/x86/kernel/vsyscall_emu_64.S b/arch/x86/kernel/vsyscall_emu_64.S
index ffa845e..c9596a9 100644
--- a/arch/x86/kernel/vsyscall_emu_64.S
+++ b/arch/x86/kernel/vsyscall_emu_64.S
@@ -7,21 +7,31 @@
*/
#include <linux/linkage.h>
+
#include <asm/irq_vectors.h>
+#include <asm/page_types.h>
+#include <asm/unistd_64.h>
-/* The unused parts of the page are filled with 0xcc by the linker script. */
+__PAGE_ALIGNED_DATA
+ .globl __vsyscall_page
+ .balign PAGE_SIZE, 0xcc
+ .type __vsyscall_page, @object
+__vsyscall_page:
-.section .vsyscall_0, "a"
-ENTRY(vsyscall_0)
- int $VSYSCALL_EMU_VECTOR
-END(vsyscall_0)
+ mov $__NR_gettimeofday, %rax
+ syscall
+ ret
-.section .vsyscall_1, "a"
-ENTRY(vsyscall_1)
- int $VSYSCALL_EMU_VECTOR
-END(vsyscall_1)
+ .balign 1024, 0xcc
+ mov $__NR_time, %rax
+ syscall
+ ret
-.section .vsyscall_2, "a"
-ENTRY(vsyscall_2)
- int $VSYSCALL_EMU_VECTOR
-END(vsyscall_2)
+ .balign 1024, 0xcc
+ mov $__NR_getcpu, %rax
+ syscall
+ ret
+
+ .balign 4096, 0xcc
+
+ .size __vsyscall_page, 4096
diff --git a/arch/x86/kernel/vsyscall_trace.h b/arch/x86/kernel/vsyscall_trace.h
new file mode 100644
index 0000000..a8b2ede
--- /dev/null
+++ b/arch/x86/kernel/vsyscall_trace.h
@@ -0,0 +1,29 @@
+#undef TRACE_SYSTEM
+#define TRACE_SYSTEM vsyscall
+
+#if !defined(__VSYSCALL_TRACE_H) || defined(TRACE_HEADER_MULTI_READ)
+#define __VSYSCALL_TRACE_H
+
+#include <linux/tracepoint.h>
+
+TRACE_EVENT(emulate_vsyscall,
+
+ TP_PROTO(int nr),
+
+ TP_ARGS(nr),
+
+ TP_STRUCT__entry(__field(int, nr)),
+
+ TP_fast_assign(
+ __entry->nr = nr;
+ ),
+
+ TP_printk("nr = %d", __entry->nr)
+);
+
+#endif
+
+#undef TRACE_INCLUDE_PATH
+#define TRACE_INCLUDE_PATH ../../arch/x86/kernel
+#define TRACE_INCLUDE_FILE vsyscall_trace
+#include <trace/define_trace.h>
diff --git a/arch/x86/kvm/Kconfig b/arch/x86/kvm/Kconfig
index 988724b..ff5790d 100644
--- a/arch/x86/kvm/Kconfig
+++ b/arch/x86/kvm/Kconfig
@@ -22,6 +22,8 @@
depends on HAVE_KVM
# for device assignment:
depends on PCI
+ # for TASKSTATS/TASK_DELAY_ACCT:
+ depends on NET
select PREEMPT_NOTIFIERS
select MMU_NOTIFIER
select ANON_INODES
@@ -31,6 +33,7 @@
select KVM_ASYNC_PF
select USER_RETURN_NOTIFIER
select KVM_MMIO
+ select TASKSTATS
select TASK_DELAY_ACCT
---help---
Support hosting fully virtualized guest machines using hardware
diff --git a/arch/x86/mm/fault.c b/arch/x86/mm/fault.c
index 4d09df0..0d17c8c 100644
--- a/arch/x86/mm/fault.c
+++ b/arch/x86/mm/fault.c
@@ -17,6 +17,7 @@
#include <asm/traps.h> /* dotraplinkage, ... */
#include <asm/pgalloc.h> /* pgd_*(), ... */
#include <asm/kmemcheck.h> /* kmemcheck_*(), ... */
+#include <asm/vsyscall.h>
/*
* Page fault error code bits:
@@ -105,7 +106,7 @@
* but for now it's good enough to assume that long
* mode only uses well known segments or kernel.
*/
- return (!user_mode(regs)) || (regs->cs == __USER_CS);
+ return (!user_mode(regs) || user_64bit_mode(regs));
#endif
case 0x60:
/* 0x64 thru 0x67 are valid prefixes in all modes. */
@@ -720,6 +721,18 @@
if (is_errata100(regs, address))
return;
+#ifdef CONFIG_X86_64
+ /*
+ * Instruction fetch faults in the vsyscall page might need
+ * emulation.
+ */
+ if (unlikely((error_code & PF_INSTR) &&
+ ((address & ~0xfff) == VSYSCALL_START))) {
+ if (emulate_vsyscall(regs, address))
+ return;
+ }
+#endif
+
if (unlikely(show_unhandled_signals))
show_signal_msg(regs, error_code, address, tsk);
diff --git a/arch/x86/pci/acpi.c b/arch/x86/pci/acpi.c
index ae3cb23..c953302 100644
--- a/arch/x86/pci/acpi.c
+++ b/arch/x86/pci/acpi.c
@@ -360,6 +360,15 @@
}
}
+ /* After the PCI-E bus has been walked and all devices discovered,
+ * configure any settings of the fabric that might be necessary.
+ */
+ if (bus) {
+ struct pci_bus *child;
+ list_for_each_entry(child, &bus->children, node)
+ pcie_bus_configure_settings(child, child->self->pcie_mpss);
+ }
+
if (!bus)
kfree(sd);
diff --git a/arch/x86/platform/mrst/Makefile b/arch/x86/platform/mrst/Makefile
index f61ccdd..1ea3877 100644
--- a/arch/x86/platform/mrst/Makefile
+++ b/arch/x86/platform/mrst/Makefile
@@ -1,3 +1,4 @@
obj-$(CONFIG_X86_MRST) += mrst.o
obj-$(CONFIG_X86_MRST) += vrtc.o
obj-$(CONFIG_EARLY_PRINTK_MRST) += early_printk_mrst.o
+obj-$(CONFIG_X86_MRST) += pmu.o
diff --git a/arch/x86/platform/mrst/pmu.c b/arch/x86/platform/mrst/pmu.c
new file mode 100644
index 0000000..9281da7
--- /dev/null
+++ b/arch/x86/platform/mrst/pmu.c
@@ -0,0 +1,817 @@
+/*
+ * mrst/pmu.c - driver for MRST Power Management Unit
+ *
+ * Copyright (c) 2011, Intel Corporation.
+ *
+ * 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 <linux/cpuidle.h>
+#include <linux/debugfs.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/seq_file.h>
+#include <linux/sfi.h>
+#include <asm/intel_scu_ipc.h>
+#include "pmu.h"
+
+#define IPCMSG_FW_REVISION 0xF4
+
+struct mrst_device {
+ u16 pci_dev_num; /* DEBUG only */
+ u16 lss;
+ u16 latest_request;
+ unsigned int pci_state_counts[PCI_D3cold + 1]; /* DEBUG only */
+};
+
+/*
+ * comlete list of MRST PCI devices
+ */
+static struct mrst_device mrst_devs[] = {
+/* 0 */ { 0x0800, LSS_SPI0 }, /* Moorestown SPI Ctrl 0 */
+/* 1 */ { 0x0801, LSS_SPI1 }, /* Moorestown SPI Ctrl 1 */
+/* 2 */ { 0x0802, LSS_I2C0 }, /* Moorestown I2C 0 */
+/* 3 */ { 0x0803, LSS_I2C1 }, /* Moorestown I2C 1 */
+/* 4 */ { 0x0804, LSS_I2C2 }, /* Moorestown I2C 2 */
+/* 5 */ { 0x0805, LSS_KBD }, /* Moorestown Keyboard Ctrl */
+/* 6 */ { 0x0806, LSS_USB_HC }, /* Moorestown USB Ctrl */
+/* 7 */ { 0x0807, LSS_SD_HC0 }, /* Moorestown SD Host Ctrl 0 */
+/* 8 */ { 0x0808, LSS_SD_HC1 }, /* Moorestown SD Host Ctrl 1 */
+/* 9 */ { 0x0809, LSS_NAND }, /* Moorestown NAND Ctrl */
+/* 10 */ { 0x080a, LSS_AUDIO }, /* Moorestown Audio Ctrl */
+/* 11 */ { 0x080b, LSS_IMAGING }, /* Moorestown ISP */
+/* 12 */ { 0x080c, LSS_SECURITY }, /* Moorestown Security Controller */
+/* 13 */ { 0x080d, LSS_DISPLAY }, /* Moorestown External Displays */
+/* 14 */ { 0x080e, 0 }, /* Moorestown SCU IPC */
+/* 15 */ { 0x080f, LSS_GPIO }, /* Moorestown GPIO Controller */
+/* 16 */ { 0x0810, 0 }, /* Moorestown Power Management Unit */
+/* 17 */ { 0x0811, LSS_USB_OTG }, /* Moorestown OTG Ctrl */
+/* 18 */ { 0x0812, LSS_SPI2 }, /* Moorestown SPI Ctrl 2 */
+/* 19 */ { 0x0813, 0 }, /* Moorestown SC DMA */
+/* 20 */ { 0x0814, LSS_AUDIO_LPE }, /* Moorestown LPE DMA */
+/* 21 */ { 0x0815, LSS_AUDIO_SSP }, /* Moorestown SSP0 */
+
+/* 22 */ { 0x084F, LSS_SD_HC2 }, /* Moorestown SD Host Ctrl 2 */
+
+/* 23 */ { 0x4102, 0 }, /* Lincroft */
+/* 24 */ { 0x4110, 0 }, /* Lincroft */
+};
+
+/* n.b. We ignore PCI-id 0x815 in LSS9 b/c MeeGo has no driver for it */
+static u16 mrst_lss9_pci_ids[] = {0x080a, 0x0814, 0};
+static u16 mrst_lss10_pci_ids[] = {0x0800, 0x0801, 0x0802, 0x0803,
+ 0x0804, 0x0805, 0x080f, 0};
+
+/* handle concurrent SMP invokations of pmu_pci_set_power_state() */
+static spinlock_t mrst_pmu_power_state_lock;
+
+static unsigned int wake_counters[MRST_NUM_LSS]; /* DEBUG only */
+static unsigned int pmu_irq_stats[INT_INVALID + 1]; /* DEBUG only */
+
+static int graphics_is_off;
+static int lss_s0i3_enabled;
+static bool mrst_pmu_s0i3_enable;
+
+/* debug counters */
+static u32 pmu_wait_ready_calls;
+static u32 pmu_wait_ready_udelays;
+static u32 pmu_wait_ready_udelays_max;
+static u32 pmu_wait_done_calls;
+static u32 pmu_wait_done_udelays;
+static u32 pmu_wait_done_udelays_max;
+static u32 pmu_set_power_state_entry;
+static u32 pmu_set_power_state_send_cmd;
+
+static struct mrst_device *pci_id_2_mrst_dev(u16 pci_dev_num)
+{
+ int index = 0;
+
+ if ((pci_dev_num >= 0x0800) && (pci_dev_num <= 0x815))
+ index = pci_dev_num - 0x800;
+ else if (pci_dev_num == 0x084F)
+ index = 22;
+ else if (pci_dev_num == 0x4102)
+ index = 23;
+ else if (pci_dev_num == 0x4110)
+ index = 24;
+
+ if (pci_dev_num != mrst_devs[index].pci_dev_num) {
+ WARN_ONCE(1, FW_BUG "Unknown PCI device 0x%04X\n", pci_dev_num);
+ return 0;
+ }
+
+ return &mrst_devs[index];
+}
+
+/**
+ * mrst_pmu_validate_cstates
+ * @dev: cpuidle_device
+ *
+ * Certain states are not appropriate for governor to pick in some cases.
+ * This function will be called as cpuidle_device's prepare callback and
+ * thus tells governor to ignore such states when selecting the next state
+ * to enter.
+ */
+
+#define IDLE_STATE4_IS_C6 4
+#define IDLE_STATE5_IS_S0I3 5
+
+int mrst_pmu_invalid_cstates(void)
+{
+ int cpu = smp_processor_id();
+
+ /*
+ * Demote to C4 if the PMU is busy.
+ * Since LSS changes leave the busy bit clear...
+ * busy means either the PMU is waiting for an ACK-C6 that
+ * isn't coming due to an MWAIT that returned immediately;
+ * or we returned from S0i3 successfully, and the PMU
+ * is not done sending us interrupts.
+ */
+ if (pmu_read_busy_status())
+ return 1 << IDLE_STATE4_IS_C6 | 1 << IDLE_STATE5_IS_S0I3;
+
+ /*
+ * Disallow S0i3 if: PMU is not initialized, or CPU1 is active,
+ * or if device LSS is insufficient, or the GPU is active,
+ * or if it has been explicitly disabled.
+ */
+ if (!pmu_reg || !cpumask_equal(cpu_online_mask, cpumask_of(cpu)) ||
+ !lss_s0i3_enabled || !graphics_is_off || !mrst_pmu_s0i3_enable)
+ return 1 << IDLE_STATE5_IS_S0I3;
+ else
+ return 0;
+}
+
+/*
+ * pmu_update_wake_counters(): read PM_WKS, update wake_counters[]
+ * DEBUG only.
+ */
+static void pmu_update_wake_counters(void)
+{
+ int lss;
+ u32 wake_status;
+
+ wake_status = pmu_read_wks();
+
+ for (lss = 0; lss < MRST_NUM_LSS; ++lss) {
+ if (wake_status & (1 << lss))
+ wake_counters[lss]++;
+ }
+}
+
+int mrst_pmu_s0i3_entry(void)
+{
+ int status;
+
+ /* Clear any possible error conditions */
+ pmu_write_ics(0x300);
+
+ /* set wake control to current D-states */
+ pmu_write_wssc(S0I3_SSS_TARGET);
+
+ status = mrst_s0i3_entry(PM_S0I3_COMMAND, &pmu_reg->pm_cmd);
+ pmu_update_wake_counters();
+ return status;
+}
+
+/* poll for maximum of 5ms for busy bit to clear */
+static int pmu_wait_ready(void)
+{
+ int udelays;
+
+ pmu_wait_ready_calls++;
+
+ for (udelays = 0; udelays < 500; ++udelays) {
+ if (udelays > pmu_wait_ready_udelays_max)
+ pmu_wait_ready_udelays_max = udelays;
+
+ if (pmu_read_busy_status() == 0)
+ return 0;
+
+ udelay(10);
+ pmu_wait_ready_udelays++;
+ }
+
+ /*
+ * if this fires, observe
+ * /sys/kernel/debug/mrst_pmu_wait_ready_calls
+ * /sys/kernel/debug/mrst_pmu_wait_ready_udelays
+ */
+ WARN_ONCE(1, "SCU not ready for 5ms");
+ return -EBUSY;
+}
+/* poll for maximum of 50ms us for busy bit to clear */
+static int pmu_wait_done(void)
+{
+ int udelays;
+
+ pmu_wait_done_calls++;
+
+ for (udelays = 0; udelays < 500; ++udelays) {
+ if (udelays > pmu_wait_done_udelays_max)
+ pmu_wait_done_udelays_max = udelays;
+
+ if (pmu_read_busy_status() == 0)
+ return 0;
+
+ udelay(100);
+ pmu_wait_done_udelays++;
+ }
+
+ /*
+ * if this fires, observe
+ * /sys/kernel/debug/mrst_pmu_wait_done_calls
+ * /sys/kernel/debug/mrst_pmu_wait_done_udelays
+ */
+ WARN_ONCE(1, "SCU not done for 50ms");
+ return -EBUSY;
+}
+
+u32 mrst_pmu_msi_is_disabled(void)
+{
+ return pmu_msi_is_disabled();
+}
+
+void mrst_pmu_enable_msi(void)
+{
+ pmu_msi_enable();
+}
+
+/**
+ * pmu_irq - pmu driver interrupt handler
+ * Context: interrupt context
+ */
+static irqreturn_t pmu_irq(int irq, void *dummy)
+{
+ union pmu_pm_ics pmu_ics;
+
+ pmu_ics.value = pmu_read_ics();
+
+ if (!pmu_ics.bits.pending)
+ return IRQ_NONE;
+
+ switch (pmu_ics.bits.cause) {
+ case INT_SPURIOUS:
+ case INT_CMD_DONE:
+ case INT_CMD_ERR:
+ case INT_WAKE_RX:
+ case INT_SS_ERROR:
+ case INT_S0IX_MISS:
+ case INT_NO_ACKC6:
+ pmu_irq_stats[pmu_ics.bits.cause]++;
+ break;
+ default:
+ pmu_irq_stats[INT_INVALID]++;
+ }
+
+ pmu_write_ics(pmu_ics.value); /* Clear pending interrupt */
+
+ return IRQ_HANDLED;
+}
+
+/*
+ * Translate PCI power management to MRST LSS D-states
+ */
+static int pci_2_mrst_state(int lss, pci_power_t pci_state)
+{
+ switch (pci_state) {
+ case PCI_D0:
+ if (SSMSK(D0i1, lss) & D0I1_ACG_SSS_TARGET)
+ return D0i1;
+ else
+ return D0;
+ case PCI_D1:
+ return D0i1;
+ case PCI_D2:
+ return D0i2;
+ case PCI_D3hot:
+ case PCI_D3cold:
+ return D0i3;
+ default:
+ WARN(1, "pci_state %d\n", pci_state);
+ return 0;
+ }
+}
+
+static int pmu_issue_command(u32 pm_ssc)
+{
+ union pmu_pm_set_cfg_cmd_t command;
+
+ if (pmu_read_busy_status()) {
+ pr_debug("pmu is busy, Operation not permitted\n");
+ return -1;
+ }
+
+ /*
+ * enable interrupts in PMU so that interrupts are
+ * propagated when ioc bit for a particular set
+ * command is set
+ */
+
+ pmu_irq_enable();
+
+ /* Configure the sub systems for pmu2 */
+
+ pmu_write_ssc(pm_ssc);
+
+ /*
+ * Send the set config command for pmu its configured
+ * for mode CM_IMMEDIATE & hence with No Trigger
+ */
+
+ command.pmu2_params.d_param.cfg_mode = CM_IMMEDIATE;
+ command.pmu2_params.d_param.cfg_delay = 0;
+ command.pmu2_params.d_param.rsvd = 0;
+
+ /* construct the command to send SET_CFG to particular PMU */
+ command.pmu2_params.d_param.cmd = SET_CFG_CMD;
+ command.pmu2_params.d_param.ioc = 0;
+ command.pmu2_params.d_param.mode_id = 0;
+ command.pmu2_params.d_param.sys_state = SYS_STATE_S0I0;
+
+ /* write the value of PM_CMD into particular PMU */
+ pr_debug("pmu command being written %x\n",
+ command.pmu_pm_set_cfg_cmd_value);
+
+ pmu_write_cmd(command.pmu_pm_set_cfg_cmd_value);
+
+ return 0;
+}
+
+static u16 pmu_min_lss_pci_req(u16 *ids, u16 pci_state)
+{
+ u16 existing_request;
+ int i;
+
+ for (i = 0; ids[i]; ++i) {
+ struct mrst_device *mrst_dev;
+
+ mrst_dev = pci_id_2_mrst_dev(ids[i]);
+ if (unlikely(!mrst_dev))
+ continue;
+
+ existing_request = mrst_dev->latest_request;
+ if (existing_request < pci_state)
+ pci_state = existing_request;
+ }
+ return pci_state;
+}
+
+/**
+ * pmu_pci_set_power_state - Callback function is used by all the PCI devices
+ * for a platform specific device power on/shutdown.
+ */
+
+int pmu_pci_set_power_state(struct pci_dev *pdev, pci_power_t pci_state)
+{
+ u32 old_sss, new_sss;
+ int status = 0;
+ struct mrst_device *mrst_dev;
+
+ pmu_set_power_state_entry++;
+
+ BUG_ON(pdev->vendor != PCI_VENDOR_ID_INTEL);
+ BUG_ON(pci_state < PCI_D0 || pci_state > PCI_D3cold);
+
+ mrst_dev = pci_id_2_mrst_dev(pdev->device);
+ if (unlikely(!mrst_dev))
+ return -ENODEV;
+
+ mrst_dev->pci_state_counts[pci_state]++; /* count invocations */
+
+ /* PMU driver calls self as part of PCI initialization, ignore */
+ if (pdev->device == PCI_DEV_ID_MRST_PMU)
+ return 0;
+
+ BUG_ON(!pmu_reg); /* SW bug if called before initialized */
+
+ spin_lock(&mrst_pmu_power_state_lock);
+
+ if (pdev->d3_delay) {
+ dev_dbg(&pdev->dev, "d3_delay %d, should be 0\n",
+ pdev->d3_delay);
+ pdev->d3_delay = 0;
+ }
+ /*
+ * If Lincroft graphics, simply remember state
+ */
+ if ((pdev->class >> 16) == PCI_BASE_CLASS_DISPLAY
+ && !((pdev->class & PCI_SUB_CLASS_MASK) >> 8)) {
+ if (pci_state == PCI_D0)
+ graphics_is_off = 0;
+ else
+ graphics_is_off = 1;
+ goto ret;
+ }
+
+ if (!mrst_dev->lss)
+ goto ret; /* device with no LSS */
+
+ if (mrst_dev->latest_request == pci_state)
+ goto ret; /* no change */
+
+ mrst_dev->latest_request = pci_state; /* record latest request */
+
+ /*
+ * LSS9 and LSS10 contain multiple PCI devices.
+ * Use the lowest numbered (highest power) state in the LSS
+ */
+ if (mrst_dev->lss == 9)
+ pci_state = pmu_min_lss_pci_req(mrst_lss9_pci_ids, pci_state);
+ else if (mrst_dev->lss == 10)
+ pci_state = pmu_min_lss_pci_req(mrst_lss10_pci_ids, pci_state);
+
+ status = pmu_wait_ready();
+ if (status)
+ goto ret;
+
+ old_sss = pmu_read_sss();
+ new_sss = old_sss & ~SSMSK(3, mrst_dev->lss);
+ new_sss |= SSMSK(pci_2_mrst_state(mrst_dev->lss, pci_state),
+ mrst_dev->lss);
+
+ if (new_sss == old_sss)
+ goto ret; /* nothing to do */
+
+ pmu_set_power_state_send_cmd++;
+
+ status = pmu_issue_command(new_sss);
+
+ if (unlikely(status != 0)) {
+ dev_err(&pdev->dev, "Failed to Issue a PM command\n");
+ goto ret;
+ }
+
+ if (pmu_wait_done())
+ goto ret;
+
+ lss_s0i3_enabled =
+ ((pmu_read_sss() & S0I3_SSS_TARGET) == S0I3_SSS_TARGET);
+ret:
+ spin_unlock(&mrst_pmu_power_state_lock);
+ return status;
+}
+
+#ifdef CONFIG_DEBUG_FS
+static char *d0ix_names[] = {"D0", "D0i1", "D0i2", "D0i3"};
+
+static inline const char *d0ix_name(int state)
+{
+ return d0ix_names[(int) state];
+}
+
+static int debug_mrst_pmu_show(struct seq_file *s, void *unused)
+{
+ struct pci_dev *pdev = NULL;
+ u32 cur_pmsss;
+ int lss;
+
+ seq_printf(s, "0x%08X D0I1_ACG_SSS_TARGET\n", D0I1_ACG_SSS_TARGET);
+
+ cur_pmsss = pmu_read_sss();
+
+ seq_printf(s, "0x%08X S0I3_SSS_TARGET\n", S0I3_SSS_TARGET);
+
+ seq_printf(s, "0x%08X Current SSS ", cur_pmsss);
+ seq_printf(s, lss_s0i3_enabled ? "\n" : "[BLOCKS s0i3]\n");
+
+ if (cpumask_equal(cpu_online_mask, cpumask_of(0)))
+ seq_printf(s, "cpu0 is only cpu online\n");
+ else
+ seq_printf(s, "cpu0 is NOT only cpu online [BLOCKS S0i3]\n");
+
+ seq_printf(s, "GFX: %s\n", graphics_is_off ? "" : "[BLOCKS s0i3]");
+
+
+ for_each_pci_dev(pdev) {
+ int pos;
+ u16 pmcsr;
+ struct mrst_device *mrst_dev;
+ int i;
+
+ mrst_dev = pci_id_2_mrst_dev(pdev->device);
+
+ seq_printf(s, "%s %04x/%04X %-16.16s ",
+ dev_name(&pdev->dev),
+ pdev->vendor, pdev->device,
+ dev_driver_string(&pdev->dev));
+
+ if (unlikely (!mrst_dev)) {
+ seq_printf(s, " UNKNOWN\n");
+ continue;
+ }
+
+ if (mrst_dev->lss)
+ seq_printf(s, "LSS %2d %-4s ", mrst_dev->lss,
+ d0ix_name(((cur_pmsss >>
+ (mrst_dev->lss * 2)) & 0x3)));
+ else
+ seq_printf(s, " ");
+
+ /* PCI PM config space setting */
+ pos = pci_find_capability(pdev, PCI_CAP_ID_PM);
+ if (pos != 0) {
+ pci_read_config_word(pdev, pos + PCI_PM_CTRL, &pmcsr);
+ seq_printf(s, "PCI-%-4s",
+ pci_power_name(pmcsr & PCI_PM_CTRL_STATE_MASK));
+ } else {
+ seq_printf(s, " ");
+ }
+
+ seq_printf(s, " %s ", pci_power_name(mrst_dev->latest_request));
+ for (i = 0; i <= PCI_D3cold; ++i)
+ seq_printf(s, "%d ", mrst_dev->pci_state_counts[i]);
+
+ if (mrst_dev->lss) {
+ unsigned int lssmask;
+
+ lssmask = SSMSK(D0i3, mrst_dev->lss);
+
+ if ((lssmask & S0I3_SSS_TARGET) &&
+ ((lssmask & cur_pmsss) !=
+ (lssmask & S0I3_SSS_TARGET)))
+ seq_printf(s , "[BLOCKS s0i3]");
+ }
+
+ seq_printf(s, "\n");
+ }
+ seq_printf(s, "Wake Counters:\n");
+ for (lss = 0; lss < MRST_NUM_LSS; ++lss)
+ seq_printf(s, "LSS%d %d\n", lss, wake_counters[lss]);
+
+ seq_printf(s, "Interrupt Counters:\n");
+ seq_printf(s,
+ "INT_SPURIOUS \t%8u\n" "INT_CMD_DONE \t%8u\n"
+ "INT_CMD_ERR \t%8u\n" "INT_WAKE_RX \t%8u\n"
+ "INT_SS_ERROR \t%8u\n" "INT_S0IX_MISS\t%8u\n"
+ "INT_NO_ACKC6 \t%8u\n" "INT_INVALID \t%8u\n",
+ pmu_irq_stats[INT_SPURIOUS], pmu_irq_stats[INT_CMD_DONE],
+ pmu_irq_stats[INT_CMD_ERR], pmu_irq_stats[INT_WAKE_RX],
+ pmu_irq_stats[INT_SS_ERROR], pmu_irq_stats[INT_S0IX_MISS],
+ pmu_irq_stats[INT_NO_ACKC6], pmu_irq_stats[INT_INVALID]);
+
+ seq_printf(s, "mrst_pmu_wait_ready_calls %8d\n",
+ pmu_wait_ready_calls);
+ seq_printf(s, "mrst_pmu_wait_ready_udelays %8d\n",
+ pmu_wait_ready_udelays);
+ seq_printf(s, "mrst_pmu_wait_ready_udelays_max %8d\n",
+ pmu_wait_ready_udelays_max);
+ seq_printf(s, "mrst_pmu_wait_done_calls %8d\n",
+ pmu_wait_done_calls);
+ seq_printf(s, "mrst_pmu_wait_done_udelays %8d\n",
+ pmu_wait_done_udelays);
+ seq_printf(s, "mrst_pmu_wait_done_udelays_max %8d\n",
+ pmu_wait_done_udelays_max);
+ seq_printf(s, "mrst_pmu_set_power_state_entry %8d\n",
+ pmu_set_power_state_entry);
+ seq_printf(s, "mrst_pmu_set_power_state_send_cmd %8d\n",
+ pmu_set_power_state_send_cmd);
+ seq_printf(s, "SCU busy: %d\n", pmu_read_busy_status());
+
+ return 0;
+}
+
+static int debug_mrst_pmu_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, debug_mrst_pmu_show, NULL);
+}
+
+static const struct file_operations devices_state_operations = {
+ .open = debug_mrst_pmu_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+#endif /* DEBUG_FS */
+
+/*
+ * Validate SCU PCI shim PCI vendor capability byte
+ * against LSS hard-coded in mrst_devs[] above.
+ * DEBUG only.
+ */
+static void pmu_scu_firmware_debug(void)
+{
+ struct pci_dev *pdev = NULL;
+
+ for_each_pci_dev(pdev) {
+ struct mrst_device *mrst_dev;
+ u8 pci_config_lss;
+ int pos;
+
+ mrst_dev = pci_id_2_mrst_dev(pdev->device);
+ if (unlikely(!mrst_dev)) {
+ printk(KERN_ERR FW_BUG "pmu: Unknown "
+ "PCI device 0x%04X\n", pdev->device);
+ continue;
+ }
+
+ if (mrst_dev->lss == 0)
+ continue; /* no LSS in our table */
+
+ pos = pci_find_capability(pdev, PCI_CAP_ID_VNDR);
+ if (!pos != 0) {
+ printk(KERN_ERR FW_BUG "pmu: 0x%04X "
+ "missing PCI Vendor Capability\n",
+ pdev->device);
+ continue;
+ }
+ pci_read_config_byte(pdev, pos + 4, &pci_config_lss);
+ if (!(pci_config_lss & PCI_VENDOR_CAP_LOG_SS_MASK)) {
+ printk(KERN_ERR FW_BUG "pmu: 0x%04X "
+ "invalid PCI Vendor Capability 0x%x "
+ " expected LSS 0x%X\n",
+ pdev->device, pci_config_lss, mrst_dev->lss);
+ continue;
+ }
+ pci_config_lss &= PCI_VENDOR_CAP_LOG_ID_MASK;
+
+ if (mrst_dev->lss == pci_config_lss)
+ continue;
+
+ printk(KERN_ERR FW_BUG "pmu: 0x%04X LSS = %d, expected %d\n",
+ pdev->device, pci_config_lss, mrst_dev->lss);
+ }
+}
+
+/**
+ * pmu_probe
+ */
+static int __devinit pmu_probe(struct pci_dev *pdev,
+ const struct pci_device_id *pci_id)
+{
+ int ret;
+ struct mrst_pmu_reg *pmu;
+
+ /* Init the device */
+ ret = pci_enable_device(pdev);
+ if (ret) {
+ dev_err(&pdev->dev, "Unable to Enable PCI device\n");
+ return ret;
+ }
+
+ ret = pci_request_regions(pdev, MRST_PMU_DRV_NAME);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "Cannot obtain PCI resources, aborting\n");
+ goto out_err1;
+ }
+
+ /* Map the memory of PMU reg base */
+ pmu = pci_iomap(pdev, 0, 0);
+ if (!pmu) {
+ dev_err(&pdev->dev, "Unable to map the PMU address space\n");
+ ret = -ENOMEM;
+ goto out_err2;
+ }
+
+#ifdef CONFIG_DEBUG_FS
+ /* /sys/kernel/debug/mrst_pmu */
+ (void) debugfs_create_file("mrst_pmu", S_IFREG | S_IRUGO,
+ NULL, NULL, &devices_state_operations);
+#endif
+ pmu_reg = pmu; /* success */
+
+ if (request_irq(pdev->irq, pmu_irq, 0, MRST_PMU_DRV_NAME, NULL)) {
+ dev_err(&pdev->dev, "Registering isr has failed\n");
+ ret = -1;
+ goto out_err3;
+ }
+
+ pmu_scu_firmware_debug();
+
+ pmu_write_wkc(S0I3_WAKE_SOURCES); /* Enable S0i3 wakeup sources */
+
+ pmu_wait_ready();
+
+ pmu_write_ssc(D0I1_ACG_SSS_TARGET); /* Enable Auto-Clock_Gating */
+ pmu_write_cmd(0x201);
+
+ spin_lock_init(&mrst_pmu_power_state_lock);
+
+ /* Enable the hardware interrupt */
+ pmu_irq_enable();
+ return 0;
+
+out_err3:
+ free_irq(pdev->irq, NULL);
+ pci_iounmap(pdev, pmu_reg);
+ pmu_reg = NULL;
+out_err2:
+ pci_release_region(pdev, 0);
+out_err1:
+ pci_disable_device(pdev);
+ return ret;
+}
+
+static void __devexit pmu_remove(struct pci_dev *pdev)
+{
+ dev_err(&pdev->dev, "Mid PM pmu_remove called\n");
+
+ /* Freeing up the irq */
+ free_irq(pdev->irq, NULL);
+
+ pci_iounmap(pdev, pmu_reg);
+ pmu_reg = NULL;
+
+ /* disable the current PCI device */
+ pci_release_region(pdev, 0);
+ pci_disable_device(pdev);
+}
+
+static DEFINE_PCI_DEVICE_TABLE(pmu_pci_ids) = {
+ { PCI_VDEVICE(INTEL, PCI_DEV_ID_MRST_PMU), 0 },
+ { }
+};
+
+MODULE_DEVICE_TABLE(pci, pmu_pci_ids);
+
+static struct pci_driver driver = {
+ .name = MRST_PMU_DRV_NAME,
+ .id_table = pmu_pci_ids,
+ .probe = pmu_probe,
+ .remove = __devexit_p(pmu_remove),
+};
+
+/**
+ * pmu_pci_register - register the PMU driver as PCI device
+ */
+static int __init pmu_pci_register(void)
+{
+ return pci_register_driver(&driver);
+}
+
+/* Register and probe via fs_initcall() to preceed device_initcall() */
+fs_initcall(pmu_pci_register);
+
+static void __exit mid_pci_cleanup(void)
+{
+ pci_unregister_driver(&driver);
+}
+
+static int ia_major;
+static int ia_minor;
+
+static int pmu_sfi_parse_oem(struct sfi_table_header *table)
+{
+ struct sfi_table_simple *sb;
+
+ sb = (struct sfi_table_simple *)table;
+ ia_major = (sb->pentry[1] >> 0) & 0xFFFF;
+ ia_minor = (sb->pentry[1] >> 16) & 0xFFFF;
+ printk(KERN_INFO "mrst_pmu: IA FW version v%x.%x\n",
+ ia_major, ia_minor);
+
+ return 0;
+}
+
+static int __init scu_fw_check(void)
+{
+ int ret;
+ u32 fw_version;
+
+ if (!pmu_reg)
+ return 0; /* this driver didn't probe-out */
+
+ sfi_table_parse("OEMB", NULL, NULL, pmu_sfi_parse_oem);
+
+ if (ia_major < 0x6005 || ia_minor < 0x1525) {
+ WARN(1, "mrst_pmu: IA FW version too old\n");
+ return -1;
+ }
+
+ ret = intel_scu_ipc_command(IPCMSG_FW_REVISION, 0, NULL, 0,
+ &fw_version, 1);
+
+ if (ret) {
+ WARN(1, "mrst_pmu: IPC FW version? %d\n", ret);
+ } else {
+ int scu_major = (fw_version >> 8) & 0xFF;
+ int scu_minor = (fw_version >> 0) & 0xFF;
+
+ printk(KERN_INFO "mrst_pmu: firmware v%x\n", fw_version);
+
+ if ((scu_major >= 0xC0) && (scu_minor >= 0x49)) {
+ printk(KERN_INFO "mrst_pmu: enabling S0i3\n");
+ mrst_pmu_s0i3_enable = true;
+ } else {
+ WARN(1, "mrst_pmu: S0i3 disabled, old firmware %X.%X",
+ scu_major, scu_minor);
+ }
+ }
+ return 0;
+}
+late_initcall(scu_fw_check);
+module_exit(mid_pci_cleanup);
diff --git a/arch/x86/platform/mrst/pmu.h b/arch/x86/platform/mrst/pmu.h
new file mode 100644
index 0000000..bfbfe64
--- /dev/null
+++ b/arch/x86/platform/mrst/pmu.h
@@ -0,0 +1,234 @@
+/*
+ * mrst/pmu.h - private definitions for MRST Power Management Unit mrst/pmu.c
+ *
+ * Copyright (c) 2011, Intel Corporation.
+ *
+ * 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.
+ */
+
+#ifndef _MRST_PMU_H_
+#define _MRST_PMU_H_
+
+#define PCI_DEV_ID_MRST_PMU 0x0810
+#define MRST_PMU_DRV_NAME "mrst_pmu"
+#define PCI_SUB_CLASS_MASK 0xFF00
+
+#define PCI_VENDOR_CAP_LOG_ID_MASK 0x7F
+#define PCI_VENDOR_CAP_LOG_SS_MASK 0x80
+
+#define SUB_SYS_ALL_D0I1 0x01155555
+#define S0I3_WAKE_SOURCES 0x00001FFF
+
+#define PM_S0I3_COMMAND \
+ ((0 << 31) | /* Reserved */ \
+ (0 << 30) | /* Core must be idle */ \
+ (0xc2 << 22) | /* ACK C6 trigger */ \
+ (3 << 19) | /* Trigger on DMI message */ \
+ (3 << 16) | /* Enter S0i3 */ \
+ (0 << 13) | /* Numeric mode ID (sw) */ \
+ (3 << 9) | /* Trigger mode */ \
+ (0 << 8) | /* Do not interrupt */ \
+ (1 << 0)) /* Set configuration */
+
+#define LSS_DMI 0
+#define LSS_SD_HC0 1
+#define LSS_SD_HC1 2
+#define LSS_NAND 3
+#define LSS_IMAGING 4
+#define LSS_SECURITY 5
+#define LSS_DISPLAY 6
+#define LSS_USB_HC 7
+#define LSS_USB_OTG 8
+#define LSS_AUDIO 9
+#define LSS_AUDIO_LPE 9
+#define LSS_AUDIO_SSP 9
+#define LSS_I2C0 10
+#define LSS_I2C1 10
+#define LSS_I2C2 10
+#define LSS_KBD 10
+#define LSS_SPI0 10
+#define LSS_SPI1 10
+#define LSS_SPI2 10
+#define LSS_GPIO 10
+#define LSS_SRAM 11 /* used by SCU, do not touch */
+#define LSS_SD_HC2 12
+/* LSS hardware bits 15,14,13 are hardwired to 0, thus unusable */
+#define MRST_NUM_LSS 13
+
+#define MIN(a, b) (((a) < (b)) ? (a) : (b))
+
+#define SSMSK(mask, lss) ((mask) << ((lss) * 2))
+#define D0 0
+#define D0i1 1
+#define D0i2 2
+#define D0i3 3
+
+#define S0I3_SSS_TARGET ( \
+ SSMSK(D0i1, LSS_DMI) | \
+ SSMSK(D0i3, LSS_SD_HC0) | \
+ SSMSK(D0i3, LSS_SD_HC1) | \
+ SSMSK(D0i3, LSS_NAND) | \
+ SSMSK(D0i3, LSS_SD_HC2) | \
+ SSMSK(D0i3, LSS_IMAGING) | \
+ SSMSK(D0i3, LSS_SECURITY) | \
+ SSMSK(D0i3, LSS_DISPLAY) | \
+ SSMSK(D0i3, LSS_USB_HC) | \
+ SSMSK(D0i3, LSS_USB_OTG) | \
+ SSMSK(D0i3, LSS_AUDIO) | \
+ SSMSK(D0i1, LSS_I2C0))
+
+/*
+ * D0i1 on Langwell is Autonomous Clock Gating (ACG).
+ * Enable ACG on every LSS except camera and audio
+ */
+#define D0I1_ACG_SSS_TARGET \
+ (SUB_SYS_ALL_D0I1 & ~SSMSK(D0i1, LSS_IMAGING) & ~SSMSK(D0i1, LSS_AUDIO))
+
+enum cm_mode {
+ CM_NOP, /* ignore the config mode value */
+ CM_IMMEDIATE,
+ CM_DELAY,
+ CM_TRIGGER,
+ CM_INVALID
+};
+
+enum sys_state {
+ SYS_STATE_S0I0,
+ SYS_STATE_S0I1,
+ SYS_STATE_S0I2,
+ SYS_STATE_S0I3,
+ SYS_STATE_S3,
+ SYS_STATE_S5
+};
+
+#define SET_CFG_CMD 1
+
+enum int_status {
+ INT_SPURIOUS = 0,
+ INT_CMD_DONE = 1,
+ INT_CMD_ERR = 2,
+ INT_WAKE_RX = 3,
+ INT_SS_ERROR = 4,
+ INT_S0IX_MISS = 5,
+ INT_NO_ACKC6 = 6,
+ INT_INVALID = 7,
+};
+
+/* PMU register interface */
+static struct mrst_pmu_reg {
+ u32 pm_sts; /* 0x00 */
+ u32 pm_cmd; /* 0x04 */
+ u32 pm_ics; /* 0x08 */
+ u32 _resv1; /* 0x0C */
+ u32 pm_wkc[2]; /* 0x10 */
+ u32 pm_wks[2]; /* 0x18 */
+ u32 pm_ssc[4]; /* 0x20 */
+ u32 pm_sss[4]; /* 0x30 */
+ u32 pm_wssc[4]; /* 0x40 */
+ u32 pm_c3c4; /* 0x50 */
+ u32 pm_c5c6; /* 0x54 */
+ u32 pm_msi_disable; /* 0x58 */
+} *pmu_reg;
+
+static inline u32 pmu_read_sts(void) { return readl(&pmu_reg->pm_sts); }
+static inline u32 pmu_read_ics(void) { return readl(&pmu_reg->pm_ics); }
+static inline u32 pmu_read_wks(void) { return readl(&pmu_reg->pm_wks[0]); }
+static inline u32 pmu_read_sss(void) { return readl(&pmu_reg->pm_sss[0]); }
+
+static inline void pmu_write_cmd(u32 arg) { writel(arg, &pmu_reg->pm_cmd); }
+static inline void pmu_write_ics(u32 arg) { writel(arg, &pmu_reg->pm_ics); }
+static inline void pmu_write_wkc(u32 arg) { writel(arg, &pmu_reg->pm_wkc[0]); }
+static inline void pmu_write_ssc(u32 arg) { writel(arg, &pmu_reg->pm_ssc[0]); }
+static inline void pmu_write_wssc(u32 arg)
+ { writel(arg, &pmu_reg->pm_wssc[0]); }
+
+static inline void pmu_msi_enable(void) { writel(0, &pmu_reg->pm_msi_disable); }
+static inline u32 pmu_msi_is_disabled(void)
+ { return readl(&pmu_reg->pm_msi_disable); }
+
+union pmu_pm_ics {
+ struct {
+ u32 cause:8;
+ u32 enable:1;
+ u32 pending:1;
+ u32 reserved:22;
+ } bits;
+ u32 value;
+};
+
+static inline void pmu_irq_enable(void)
+{
+ union pmu_pm_ics pmu_ics;
+
+ pmu_ics.value = pmu_read_ics();
+ pmu_ics.bits.enable = 1;
+ pmu_write_ics(pmu_ics.value);
+}
+
+union pmu_pm_status {
+ struct {
+ u32 pmu_rev:8;
+ u32 pmu_busy:1;
+ u32 mode_id:4;
+ u32 Reserved:19;
+ } pmu_status_parts;
+ u32 pmu_status_value;
+};
+
+static inline int pmu_read_busy_status(void)
+{
+ union pmu_pm_status result;
+
+ result.pmu_status_value = pmu_read_sts();
+
+ return result.pmu_status_parts.pmu_busy;
+}
+
+/* pmu set config parameters */
+struct cfg_delay_param_t {
+ u32 cmd:8;
+ u32 ioc:1;
+ u32 cfg_mode:4;
+ u32 mode_id:3;
+ u32 sys_state:3;
+ u32 cfg_delay:8;
+ u32 rsvd:5;
+};
+
+struct cfg_trig_param_t {
+ u32 cmd:8;
+ u32 ioc:1;
+ u32 cfg_mode:4;
+ u32 mode_id:3;
+ u32 sys_state:3;
+ u32 cfg_trig_type:3;
+ u32 cfg_trig_val:8;
+ u32 cmbi:1;
+ u32 rsvd1:1;
+};
+
+union pmu_pm_set_cfg_cmd_t {
+ union {
+ struct cfg_delay_param_t d_param;
+ struct cfg_trig_param_t t_param;
+ } pmu2_params;
+ u32 pmu_pm_set_cfg_cmd_value;
+};
+
+#ifdef FUTURE_PATCH
+extern int mrst_s0i3_entry(u32 regval, u32 *regaddr);
+#else
+static inline int mrst_s0i3_entry(u32 regval, u32 *regaddr) { return -1; }
+#endif
+#endif
diff --git a/arch/x86/vdso/vdso.S b/arch/x86/vdso/vdso.S
index 1b979c1..01f5e3b 100644
--- a/arch/x86/vdso/vdso.S
+++ b/arch/x86/vdso/vdso.S
@@ -9,6 +9,7 @@
vdso_start:
.incbin "arch/x86/vdso/vdso.so"
vdso_end:
+ .align PAGE_SIZE /* extra data here leaks to userspace. */
.previous
diff --git a/arch/x86/xen/Makefile b/arch/x86/xen/Makefile
index 45e94ac..3326204 100644
--- a/arch/x86/xen/Makefile
+++ b/arch/x86/xen/Makefile
@@ -15,7 +15,7 @@
grant-table.o suspend.o platform-pci-unplug.o \
p2m.o
-obj-$(CONFIG_FUNCTION_TRACER) += trace.o
+obj-$(CONFIG_FTRACE) += trace.o
obj-$(CONFIG_SMP) += smp.o
obj-$(CONFIG_PARAVIRT_SPINLOCKS)+= spinlock.o
diff --git a/arch/x86/xen/enlighten.c b/arch/x86/xen/enlighten.c
index 974a528..e2345af 100644
--- a/arch/x86/xen/enlighten.c
+++ b/arch/x86/xen/enlighten.c
@@ -951,6 +951,10 @@
.paravirt_enabled = 1,
.shared_kernel_pmd = 0,
+#ifdef CONFIG_X86_64
+ .extra_user_64bit_cs = FLAT_USER_CS64,
+#endif
+
.name = "Xen",
};
diff --git a/arch/x86/xen/mmu.c b/arch/x86/xen/mmu.c
index f987bde..8cce339 100644
--- a/arch/x86/xen/mmu.c
+++ b/arch/x86/xen/mmu.c
@@ -1916,6 +1916,7 @@
# endif
#else
case VSYSCALL_LAST_PAGE ... VSYSCALL_FIRST_PAGE:
+ case VVAR_PAGE:
#endif
case FIX_TEXT_POKE0:
case FIX_TEXT_POKE1:
@@ -1956,7 +1957,8 @@
#ifdef CONFIG_X86_64
/* Replicate changes to map the vsyscall page into the user
pagetable vsyscall mapping. */
- if (idx >= VSYSCALL_LAST_PAGE && idx <= VSYSCALL_FIRST_PAGE) {
+ if ((idx >= VSYSCALL_LAST_PAGE && idx <= VSYSCALL_FIRST_PAGE) ||
+ idx == VVAR_PAGE) {
unsigned long vaddr = __fix_to_virt(idx);
set_pte_vaddr_pud(level3_user_vsyscall, vaddr, pte);
}
diff --git a/arch/x86/xen/setup.c b/arch/x86/xen/setup.c
index 60aeeb5..df118a8 100644
--- a/arch/x86/xen/setup.c
+++ b/arch/x86/xen/setup.c
@@ -9,6 +9,7 @@
#include <linux/mm.h>
#include <linux/pm.h>
#include <linux/memblock.h>
+#include <linux/cpuidle.h>
#include <asm/elf.h>
#include <asm/vdso.h>
@@ -92,8 +93,6 @@
if (end <= start)
return 0;
- printk(KERN_INFO "xen_release_chunk: looking at area pfn %lx-%lx: ",
- start, end);
for(pfn = start; pfn < end; pfn++) {
unsigned long mfn = pfn_to_mfn(pfn);
@@ -106,14 +105,14 @@
ret = HYPERVISOR_memory_op(XENMEM_decrease_reservation,
&reservation);
- WARN(ret != 1, "Failed to release memory %lx-%lx err=%d\n",
- start, end, ret);
+ WARN(ret != 1, "Failed to release pfn %lx err=%d\n", pfn, ret);
if (ret == 1) {
__set_phys_to_machine(pfn, INVALID_P2M_ENTRY);
len++;
}
}
- printk(KERN_CONT "%ld pages freed\n", len);
+ printk(KERN_INFO "Freeing %lx-%lx pfn range: %lu pages freed\n",
+ start, end, len);
return len;
}
@@ -139,7 +138,7 @@
if (last_end < max_addr)
released += xen_release_chunk(last_end, max_addr);
- printk(KERN_INFO "released %ld pages of unused memory\n", released);
+ printk(KERN_INFO "released %lu pages of unused memory\n", released);
return released;
}
@@ -426,7 +425,7 @@
#ifdef CONFIG_X86_32
boot_cpu_data.hlt_works_ok = 1;
#endif
- pm_idle = default_idle;
+ disable_cpuidle();
boot_option_idle_override = IDLE_HALT;
fiddle_vdso();
diff --git a/arch/x86/xen/trace.c b/arch/x86/xen/trace.c
index 734beba..520022d 100644
--- a/arch/x86/xen/trace.c
+++ b/arch/x86/xen/trace.c
@@ -1,4 +1,5 @@
#include <linux/ftrace.h>
+#include <xen/interface/xen.h>
#define N(x) [__HYPERVISOR_##x] = "("#x")"
static const char *xen_hypercall_names[] = {
diff --git a/block/blk-core.c b/block/blk-core.c
index 7c59b0f..90e1ffd 100644
--- a/block/blk-core.c
+++ b/block/blk-core.c
@@ -1368,8 +1368,10 @@
static int __init fail_make_request_debugfs(void)
{
- return init_fault_attr_dentries(&fail_make_request,
- "fail_make_request");
+ struct dentry *dir = fault_create_debugfs_attr("fail_make_request",
+ NULL, &fail_make_request);
+
+ return IS_ERR(dir) ? PTR_ERR(dir) : 0;
}
late_initcall(fail_make_request_debugfs);
diff --git a/block/blk-timeout.c b/block/blk-timeout.c
index 4f0c06c..7803548 100644
--- a/block/blk-timeout.c
+++ b/block/blk-timeout.c
@@ -28,7 +28,10 @@
static int __init fail_io_timeout_debugfs(void)
{
- return init_fault_attr_dentries(&fail_io_timeout, "fail_io_timeout");
+ struct dentry *dir = fault_create_debugfs_attr("fail_io_timeout",
+ NULL, &fail_io_timeout);
+
+ return IS_ERR(dir) ? PTR_ERR(dir) : 0;
}
late_initcall(fail_io_timeout_debugfs);
diff --git a/crypto/md5.c b/crypto/md5.c
index 30efc7d..7febeaa 100644
--- a/crypto/md5.c
+++ b/crypto/md5.c
@@ -21,99 +21,9 @@
#include <linux/module.h>
#include <linux/string.h>
#include <linux/types.h>
+#include <linux/cryptohash.h>
#include <asm/byteorder.h>
-#define F1(x, y, z) (z ^ (x & (y ^ z)))
-#define F2(x, y, z) F1(z, x, y)
-#define F3(x, y, z) (x ^ y ^ z)
-#define F4(x, y, z) (y ^ (x | ~z))
-
-#define MD5STEP(f, w, x, y, z, in, s) \
- (w += f(x, y, z) + in, w = (w<<s | w>>(32-s)) + x)
-
-static void md5_transform(u32 *hash, u32 const *in)
-{
- u32 a, b, c, d;
-
- a = hash[0];
- b = hash[1];
- c = hash[2];
- d = hash[3];
-
- MD5STEP(F1, a, b, c, d, in[0] + 0xd76aa478, 7);
- MD5STEP(F1, d, a, b, c, in[1] + 0xe8c7b756, 12);
- MD5STEP(F1, c, d, a, b, in[2] + 0x242070db, 17);
- MD5STEP(F1, b, c, d, a, in[3] + 0xc1bdceee, 22);
- MD5STEP(F1, a, b, c, d, in[4] + 0xf57c0faf, 7);
- MD5STEP(F1, d, a, b, c, in[5] + 0x4787c62a, 12);
- MD5STEP(F1, c, d, a, b, in[6] + 0xa8304613, 17);
- MD5STEP(F1, b, c, d, a, in[7] + 0xfd469501, 22);
- MD5STEP(F1, a, b, c, d, in[8] + 0x698098d8, 7);
- MD5STEP(F1, d, a, b, c, in[9] + 0x8b44f7af, 12);
- MD5STEP(F1, c, d, a, b, in[10] + 0xffff5bb1, 17);
- MD5STEP(F1, b, c, d, a, in[11] + 0x895cd7be, 22);
- MD5STEP(F1, a, b, c, d, in[12] + 0x6b901122, 7);
- MD5STEP(F1, d, a, b, c, in[13] + 0xfd987193, 12);
- MD5STEP(F1, c, d, a, b, in[14] + 0xa679438e, 17);
- MD5STEP(F1, b, c, d, a, in[15] + 0x49b40821, 22);
-
- MD5STEP(F2, a, b, c, d, in[1] + 0xf61e2562, 5);
- MD5STEP(F2, d, a, b, c, in[6] + 0xc040b340, 9);
- MD5STEP(F2, c, d, a, b, in[11] + 0x265e5a51, 14);
- MD5STEP(F2, b, c, d, a, in[0] + 0xe9b6c7aa, 20);
- MD5STEP(F2, a, b, c, d, in[5] + 0xd62f105d, 5);
- MD5STEP(F2, d, a, b, c, in[10] + 0x02441453, 9);
- MD5STEP(F2, c, d, a, b, in[15] + 0xd8a1e681, 14);
- MD5STEP(F2, b, c, d, a, in[4] + 0xe7d3fbc8, 20);
- MD5STEP(F2, a, b, c, d, in[9] + 0x21e1cde6, 5);
- MD5STEP(F2, d, a, b, c, in[14] + 0xc33707d6, 9);
- MD5STEP(F2, c, d, a, b, in[3] + 0xf4d50d87, 14);
- MD5STEP(F2, b, c, d, a, in[8] + 0x455a14ed, 20);
- MD5STEP(F2, a, b, c, d, in[13] + 0xa9e3e905, 5);
- MD5STEP(F2, d, a, b, c, in[2] + 0xfcefa3f8, 9);
- MD5STEP(F2, c, d, a, b, in[7] + 0x676f02d9, 14);
- MD5STEP(F2, b, c, d, a, in[12] + 0x8d2a4c8a, 20);
-
- MD5STEP(F3, a, b, c, d, in[5] + 0xfffa3942, 4);
- MD5STEP(F3, d, a, b, c, in[8] + 0x8771f681, 11);
- MD5STEP(F3, c, d, a, b, in[11] + 0x6d9d6122, 16);
- MD5STEP(F3, b, c, d, a, in[14] + 0xfde5380c, 23);
- MD5STEP(F3, a, b, c, d, in[1] + 0xa4beea44, 4);
- MD5STEP(F3, d, a, b, c, in[4] + 0x4bdecfa9, 11);
- MD5STEP(F3, c, d, a, b, in[7] + 0xf6bb4b60, 16);
- MD5STEP(F3, b, c, d, a, in[10] + 0xbebfbc70, 23);
- MD5STEP(F3, a, b, c, d, in[13] + 0x289b7ec6, 4);
- MD5STEP(F3, d, a, b, c, in[0] + 0xeaa127fa, 11);
- MD5STEP(F3, c, d, a, b, in[3] + 0xd4ef3085, 16);
- MD5STEP(F3, b, c, d, a, in[6] + 0x04881d05, 23);
- MD5STEP(F3, a, b, c, d, in[9] + 0xd9d4d039, 4);
- MD5STEP(F3, d, a, b, c, in[12] + 0xe6db99e5, 11);
- MD5STEP(F3, c, d, a, b, in[15] + 0x1fa27cf8, 16);
- MD5STEP(F3, b, c, d, a, in[2] + 0xc4ac5665, 23);
-
- MD5STEP(F4, a, b, c, d, in[0] + 0xf4292244, 6);
- MD5STEP(F4, d, a, b, c, in[7] + 0x432aff97, 10);
- MD5STEP(F4, c, d, a, b, in[14] + 0xab9423a7, 15);
- MD5STEP(F4, b, c, d, a, in[5] + 0xfc93a039, 21);
- MD5STEP(F4, a, b, c, d, in[12] + 0x655b59c3, 6);
- MD5STEP(F4, d, a, b, c, in[3] + 0x8f0ccc92, 10);
- MD5STEP(F4, c, d, a, b, in[10] + 0xffeff47d, 15);
- MD5STEP(F4, b, c, d, a, in[1] + 0x85845dd1, 21);
- MD5STEP(F4, a, b, c, d, in[8] + 0x6fa87e4f, 6);
- MD5STEP(F4, d, a, b, c, in[15] + 0xfe2ce6e0, 10);
- MD5STEP(F4, c, d, a, b, in[6] + 0xa3014314, 15);
- MD5STEP(F4, b, c, d, a, in[13] + 0x4e0811a1, 21);
- MD5STEP(F4, a, b, c, d, in[4] + 0xf7537e82, 6);
- MD5STEP(F4, d, a, b, c, in[11] + 0xbd3af235, 10);
- MD5STEP(F4, c, d, a, b, in[2] + 0x2ad7d2bb, 15);
- MD5STEP(F4, b, c, d, a, in[9] + 0xeb86d391, 21);
-
- hash[0] += a;
- hash[1] += b;
- hash[2] += c;
- hash[3] += d;
-}
-
/* XXX: this stuff can be optimized */
static inline void le32_to_cpu_array(u32 *buf, unsigned int words)
{
diff --git a/drivers/acpi/acpica/acglobal.h b/drivers/acpi/acpica/acglobal.h
index 73863d8..76dc02f 100644
--- a/drivers/acpi/acpica/acglobal.h
+++ b/drivers/acpi/acpica/acglobal.h
@@ -126,6 +126,12 @@
*/
u8 ACPI_INIT_GLOBAL(acpi_gbl_truncate_io_addresses, FALSE);
+/*
+ * Disable runtime checking and repair of values returned by control methods.
+ * Use only if the repair is causing a problem on a particular machine.
+ */
+u8 ACPI_INIT_GLOBAL(acpi_gbl_disable_auto_repair, FALSE);
+
/* acpi_gbl_FADT is a local copy of the FADT, converted to a common format. */
struct acpi_table_fadt acpi_gbl_FADT;
diff --git a/drivers/acpi/acpica/aclocal.h b/drivers/acpi/acpica/aclocal.h
index c7f743c..5552125 100644
--- a/drivers/acpi/acpica/aclocal.h
+++ b/drivers/acpi/acpica/aclocal.h
@@ -357,6 +357,7 @@
char *pathname;
const union acpi_predefined_info *predefined;
union acpi_operand_object *parent_package;
+ struct acpi_namespace_node *node;
u32 flags;
u8 node_flags;
};
diff --git a/drivers/acpi/acpica/acpredef.h b/drivers/acpi/acpica/acpredef.h
index 94e73c9..c445cca 100644
--- a/drivers/acpi/acpica/acpredef.h
+++ b/drivers/acpi/acpica/acpredef.h
@@ -468,6 +468,7 @@
{{"_SWS", 0, ACPI_RTYPE_INTEGER}},
{{"_TC1", 0, ACPI_RTYPE_INTEGER}},
{{"_TC2", 0, ACPI_RTYPE_INTEGER}},
+ {{"_TDL", 0, ACPI_RTYPE_INTEGER}},
{{"_TIP", 1, ACPI_RTYPE_INTEGER}},
{{"_TIV", 1, ACPI_RTYPE_INTEGER}},
{{"_TMP", 0, ACPI_RTYPE_INTEGER}},
diff --git a/drivers/acpi/acpica/nspredef.c b/drivers/acpi/acpica/nspredef.c
index 9fb03fa..c845c80 100644
--- a/drivers/acpi/acpica/nspredef.c
+++ b/drivers/acpi/acpica/nspredef.c
@@ -193,14 +193,20 @@
}
/*
- * 1) We have a return value, but if one wasn't expected, just exit, this is
- * not a problem. For example, if the "Implicit Return" feature is
- * enabled, methods will always return a value.
+ * Return value validation and possible repair.
*
- * 2) If the return value can be of any type, then we cannot perform any
- * validation, exit.
+ * 1) Don't perform return value validation/repair if this feature
+ * has been disabled via a global option.
+ *
+ * 2) We have a return value, but if one wasn't expected, just exit,
+ * this is not a problem. For example, if the "Implicit Return"
+ * feature is enabled, methods will always return a value.
+ *
+ * 3) If the return value can be of any type, then we cannot perform
+ * any validation, just exit.
*/
- if ((!predefined->info.expected_btypes) ||
+ if (acpi_gbl_disable_auto_repair ||
+ (!predefined->info.expected_btypes) ||
(predefined->info.expected_btypes == ACPI_RTYPE_ALL)) {
goto cleanup;
}
@@ -212,6 +218,7 @@
goto cleanup;
}
data->predefined = predefined;
+ data->node = node;
data->node_flags = node->flags;
data->pathname = pathname;
diff --git a/drivers/acpi/acpica/nsrepair2.c b/drivers/acpi/acpica/nsrepair2.c
index 973883b..024c4f2 100644
--- a/drivers/acpi/acpica/nsrepair2.c
+++ b/drivers/acpi/acpica/nsrepair2.c
@@ -503,6 +503,21 @@
{
union acpi_operand_object *return_object = *return_object_ptr;
acpi_status status;
+ struct acpi_namespace_node *node;
+
+ /*
+ * We can only sort the _TSS return package if there is no _PSS in the
+ * same scope. This is because if _PSS is present, the ACPI specification
+ * dictates that the _TSS Power Dissipation field is to be ignored, and
+ * therefore some BIOSs leave garbage values in the _TSS Power field(s).
+ * In this case, it is best to just return the _TSS package as-is.
+ * (May, 2011)
+ */
+ status =
+ acpi_ns_get_node(data->node, "^_PSS", ACPI_NS_NO_UPSEARCH, &node);
+ if (ACPI_SUCCESS(status)) {
+ return (AE_OK);
+ }
status = acpi_ns_check_sorted_list(data, return_object, 5, 1,
ACPI_SORT_DESCENDING,
diff --git a/drivers/acpi/acpica/tbinstal.c b/drivers/acpi/acpica/tbinstal.c
index 48db094..62365f6 100644
--- a/drivers/acpi/acpica/tbinstal.c
+++ b/drivers/acpi/acpica/tbinstal.c
@@ -126,12 +126,29 @@
}
/*
- * Originally, we checked the table signature for "SSDT" or "PSDT" here.
- * Next, we added support for OEMx tables, signature "OEM".
- * Valid tables were encountered with a null signature, so we've just
- * given up on validating the signature, since it seems to be a waste
- * of code. The original code was removed (05/2008).
+ * Validate the incoming table signature.
+ *
+ * 1) Originally, we checked the table signature for "SSDT" or "PSDT".
+ * 2) We added support for OEMx tables, signature "OEM".
+ * 3) Valid tables were encountered with a null signature, so we just
+ * gave up on validating the signature, (05/2008).
+ * 4) We encountered non-AML tables such as the MADT, which caused
+ * interpreter errors and kernel faults. So now, we once again allow
+ * only "SSDT", "OEMx", and now, also a null signature. (05/2011).
*/
+ if ((table_desc->pointer->signature[0] != 0x00) &&
+ (!ACPI_COMPARE_NAME(table_desc->pointer->signature, ACPI_SIG_SSDT))
+ && (ACPI_STRNCMP(table_desc->pointer->signature, "OEM", 3))) {
+ ACPI_ERROR((AE_INFO,
+ "Table has invalid signature [%4.4s] (0x%8.8X), must be SSDT or OEMx",
+ acpi_ut_valid_acpi_name(*(u32 *)table_desc->
+ pointer->
+ signature) ? table_desc->
+ pointer->signature : "????",
+ *(u32 *)table_desc->pointer->signature));
+
+ return_ACPI_STATUS(AE_BAD_SIGNATURE);
+ }
(void)acpi_ut_acquire_mutex(ACPI_MTX_TABLES);
diff --git a/drivers/acpi/apei/Kconfig b/drivers/acpi/apei/Kconfig
index f739a70..c34aa51 100644
--- a/drivers/acpi/apei/Kconfig
+++ b/drivers/acpi/apei/Kconfig
@@ -10,9 +10,11 @@
error injection.
config ACPI_APEI_GHES
- tristate "APEI Generic Hardware Error Source"
+ bool "APEI Generic Hardware Error Source"
depends on ACPI_APEI && X86
select ACPI_HED
+ select LLIST
+ select GENERIC_ALLOCATOR
help
Generic Hardware Error Source provides a way to report
platform hardware errors (such as that from chipset). It
@@ -30,6 +32,13 @@
PCIe AER errors may be reported via APEI firmware first mode.
Turn on this option to enable the corresponding support.
+config ACPI_APEI_MEMORY_FAILURE
+ bool "APEI memory error recovering support"
+ depends on ACPI_APEI && MEMORY_FAILURE
+ help
+ Memory errors may be reported via APEI firmware first mode.
+ Turn on this option to enable the memory recovering support.
+
config ACPI_APEI_EINJ
tristate "APEI Error INJection (EINJ)"
depends on ACPI_APEI && DEBUG_FS
diff --git a/drivers/acpi/apei/apei-base.c b/drivers/acpi/apei/apei-base.c
index 4a904a4..8041248 100644
--- a/drivers/acpi/apei/apei-base.c
+++ b/drivers/acpi/apei/apei-base.c
@@ -157,9 +157,10 @@
* Interpret the specified action. Go through whole action table,
* execute all instructions belong to the action.
*/
-int apei_exec_run(struct apei_exec_context *ctx, u8 action)
+int __apei_exec_run(struct apei_exec_context *ctx, u8 action,
+ bool optional)
{
- int rc;
+ int rc = -ENOENT;
u32 i, ip;
struct acpi_whea_header *entry;
apei_exec_ins_func_t run;
@@ -198,9 +199,9 @@
goto rewind;
}
- return 0;
+ return !optional && rc < 0 ? rc : 0;
}
-EXPORT_SYMBOL_GPL(apei_exec_run);
+EXPORT_SYMBOL_GPL(__apei_exec_run);
typedef int (*apei_exec_entry_func_t)(struct apei_exec_context *ctx,
struct acpi_whea_header *entry,
@@ -603,3 +604,29 @@
return dapei;
}
EXPORT_SYMBOL_GPL(apei_get_debugfs_dir);
+
+int apei_osc_setup(void)
+{
+ static u8 whea_uuid_str[] = "ed855e0c-6c90-47bf-a62a-26de0fc5ad5c";
+ acpi_handle handle;
+ u32 capbuf[3];
+ struct acpi_osc_context context = {
+ .uuid_str = whea_uuid_str,
+ .rev = 1,
+ .cap.length = sizeof(capbuf),
+ .cap.pointer = capbuf,
+ };
+
+ capbuf[OSC_QUERY_TYPE] = OSC_QUERY_ENABLE;
+ capbuf[OSC_SUPPORT_TYPE] = 0;
+ capbuf[OSC_CONTROL_TYPE] = 0;
+
+ if (ACPI_FAILURE(acpi_get_handle(NULL, "\\_SB", &handle))
+ || ACPI_FAILURE(acpi_run_osc(handle, &context)))
+ return -EIO;
+ else {
+ kfree(context.ret.pointer);
+ return 0;
+ }
+}
+EXPORT_SYMBOL_GPL(apei_osc_setup);
diff --git a/drivers/acpi/apei/apei-internal.h b/drivers/acpi/apei/apei-internal.h
index ef0581f..f57050e 100644
--- a/drivers/acpi/apei/apei-internal.h
+++ b/drivers/acpi/apei/apei-internal.h
@@ -50,7 +50,18 @@
return ctx->value;
}
-int apei_exec_run(struct apei_exec_context *ctx, u8 action);
+int __apei_exec_run(struct apei_exec_context *ctx, u8 action, bool optional);
+
+static inline int apei_exec_run(struct apei_exec_context *ctx, u8 action)
+{
+ return __apei_exec_run(ctx, action, 0);
+}
+
+/* It is optional whether the firmware provides the action */
+static inline int apei_exec_run_optional(struct apei_exec_context *ctx, u8 action)
+{
+ return __apei_exec_run(ctx, action, 1);
+}
/* Common instruction implementation */
@@ -113,4 +124,6 @@
const struct acpi_hest_generic_status *estatus);
int apei_estatus_check_header(const struct acpi_hest_generic_status *estatus);
int apei_estatus_check(const struct acpi_hest_generic_status *estatus);
+
+int apei_osc_setup(void);
#endif
diff --git a/drivers/acpi/apei/einj.c b/drivers/acpi/apei/einj.c
index f74b2ea..589b96c 100644
--- a/drivers/acpi/apei/einj.c
+++ b/drivers/acpi/apei/einj.c
@@ -46,7 +46,8 @@
* Some BIOSes allow parameters to the SET_ERROR_TYPE entries in the
* EINJ table through an unpublished extension. Use with caution as
* most will ignore the parameter and make their own choice of address
- * for error injection.
+ * for error injection. This extension is used only if
+ * param_extension module parameter is specified.
*/
struct einj_parameter {
u64 type;
@@ -65,6 +66,9 @@
((struct acpi_whea_header *)((char *)(tab) + \
sizeof(struct acpi_table_einj)))
+static bool param_extension;
+module_param(param_extension, bool, 0);
+
static struct acpi_table_einj *einj_tab;
static struct apei_resources einj_resources;
@@ -285,7 +289,7 @@
einj_exec_ctx_init(&ctx);
- rc = apei_exec_run(&ctx, ACPI_EINJ_BEGIN_OPERATION);
+ rc = apei_exec_run_optional(&ctx, ACPI_EINJ_BEGIN_OPERATION);
if (rc)
return rc;
apei_exec_ctx_set_input(&ctx, type);
@@ -323,7 +327,7 @@
rc = __einj_error_trigger(trigger_paddr);
if (rc)
return rc;
- rc = apei_exec_run(&ctx, ACPI_EINJ_END_OPERATION);
+ rc = apei_exec_run_optional(&ctx, ACPI_EINJ_END_OPERATION);
return rc;
}
@@ -489,14 +493,6 @@
einj_debug_dir, NULL, &error_type_fops);
if (!fentry)
goto err_cleanup;
- fentry = debugfs_create_x64("param1", S_IRUSR | S_IWUSR,
- einj_debug_dir, &error_param1);
- if (!fentry)
- goto err_cleanup;
- fentry = debugfs_create_x64("param2", S_IRUSR | S_IWUSR,
- einj_debug_dir, &error_param2);
- if (!fentry)
- goto err_cleanup;
fentry = debugfs_create_file("error_inject", S_IWUSR,
einj_debug_dir, NULL, &error_inject_fops);
if (!fentry)
@@ -513,12 +509,23 @@
rc = apei_exec_pre_map_gars(&ctx);
if (rc)
goto err_release;
- param_paddr = einj_get_parameter_address();
- if (param_paddr) {
- einj_param = ioremap(param_paddr, sizeof(*einj_param));
- rc = -ENOMEM;
- if (!einj_param)
- goto err_unmap;
+ if (param_extension) {
+ param_paddr = einj_get_parameter_address();
+ if (param_paddr) {
+ einj_param = ioremap(param_paddr, sizeof(*einj_param));
+ rc = -ENOMEM;
+ if (!einj_param)
+ goto err_unmap;
+ fentry = debugfs_create_x64("param1", S_IRUSR | S_IWUSR,
+ einj_debug_dir, &error_param1);
+ if (!fentry)
+ goto err_unmap;
+ fentry = debugfs_create_x64("param2", S_IRUSR | S_IWUSR,
+ einj_debug_dir, &error_param2);
+ if (!fentry)
+ goto err_unmap;
+ } else
+ pr_warn(EINJ_PFX "Parameter extension is not supported.\n");
}
pr_info(EINJ_PFX "Error INJection is initialized.\n");
@@ -526,6 +533,8 @@
return 0;
err_unmap:
+ if (einj_param)
+ iounmap(einj_param);
apei_exec_post_unmap_gars(&ctx);
err_release:
apei_resources_release(&einj_resources);
diff --git a/drivers/acpi/apei/erst-dbg.c b/drivers/acpi/apei/erst-dbg.c
index a4cfb64..903549d 100644
--- a/drivers/acpi/apei/erst-dbg.c
+++ b/drivers/acpi/apei/erst-dbg.c
@@ -33,7 +33,7 @@
#define ERST_DBG_PFX "ERST DBG: "
-#define ERST_DBG_RECORD_LEN_MAX 4096
+#define ERST_DBG_RECORD_LEN_MAX 0x4000
static void *erst_dbg_buf;
static unsigned int erst_dbg_buf_len;
@@ -213,6 +213,10 @@
static __init int erst_dbg_init(void)
{
+ if (erst_disable) {
+ pr_info(ERST_DBG_PFX "ERST support is disabled.\n");
+ return -ENODEV;
+ }
return misc_register(&erst_dbg_dev);
}
diff --git a/drivers/acpi/apei/erst.c b/drivers/acpi/apei/erst.c
index e6cef8e..2ca59dc 100644
--- a/drivers/acpi/apei/erst.c
+++ b/drivers/acpi/apei/erst.c
@@ -642,7 +642,7 @@
int rc;
erst_exec_ctx_init(&ctx);
- rc = apei_exec_run(&ctx, ACPI_ERST_BEGIN_WRITE);
+ rc = apei_exec_run_optional(&ctx, ACPI_ERST_BEGIN_WRITE);
if (rc)
return rc;
apei_exec_ctx_set_input(&ctx, offset);
@@ -666,7 +666,7 @@
if (rc)
return rc;
val = apei_exec_ctx_get_output(&ctx);
- rc = apei_exec_run(&ctx, ACPI_ERST_END);
+ rc = apei_exec_run_optional(&ctx, ACPI_ERST_END);
if (rc)
return rc;
@@ -681,7 +681,7 @@
int rc;
erst_exec_ctx_init(&ctx);
- rc = apei_exec_run(&ctx, ACPI_ERST_BEGIN_READ);
+ rc = apei_exec_run_optional(&ctx, ACPI_ERST_BEGIN_READ);
if (rc)
return rc;
apei_exec_ctx_set_input(&ctx, offset);
@@ -709,7 +709,7 @@
if (rc)
return rc;
val = apei_exec_ctx_get_output(&ctx);
- rc = apei_exec_run(&ctx, ACPI_ERST_END);
+ rc = apei_exec_run_optional(&ctx, ACPI_ERST_END);
if (rc)
return rc;
@@ -724,7 +724,7 @@
int rc;
erst_exec_ctx_init(&ctx);
- rc = apei_exec_run(&ctx, ACPI_ERST_BEGIN_CLEAR);
+ rc = apei_exec_run_optional(&ctx, ACPI_ERST_BEGIN_CLEAR);
if (rc)
return rc;
apei_exec_ctx_set_input(&ctx, record_id);
@@ -748,7 +748,7 @@
if (rc)
return rc;
val = apei_exec_ctx_get_output(&ctx);
- rc = apei_exec_run(&ctx, ACPI_ERST_END);
+ rc = apei_exec_run_optional(&ctx, ACPI_ERST_END);
if (rc)
return rc;
@@ -932,8 +932,11 @@
static int erst_open_pstore(struct pstore_info *psi);
static int erst_close_pstore(struct pstore_info *psi);
static ssize_t erst_reader(u64 *id, enum pstore_type_id *type,
- struct timespec *time);
-static u64 erst_writer(enum pstore_type_id type, size_t size);
+ struct timespec *time, struct pstore_info *psi);
+static u64 erst_writer(enum pstore_type_id type, unsigned int part,
+ size_t size, struct pstore_info *psi);
+static int erst_clearer(enum pstore_type_id type, u64 id,
+ struct pstore_info *psi);
static struct pstore_info erst_info = {
.owner = THIS_MODULE,
@@ -942,7 +945,7 @@
.close = erst_close_pstore,
.read = erst_reader,
.write = erst_writer,
- .erase = erst_clear
+ .erase = erst_clearer
};
#define CPER_CREATOR_PSTORE \
@@ -983,7 +986,7 @@
}
static ssize_t erst_reader(u64 *id, enum pstore_type_id *type,
- struct timespec *time)
+ struct timespec *time, struct pstore_info *psi)
{
int rc;
ssize_t len = 0;
@@ -1037,7 +1040,8 @@
return (rc < 0) ? rc : (len - sizeof(*rcd));
}
-static u64 erst_writer(enum pstore_type_id type, size_t size)
+static u64 erst_writer(enum pstore_type_id type, unsigned int part,
+ size_t size, struct pstore_info *psi)
{
struct cper_pstore_record *rcd = (struct cper_pstore_record *)
(erst_info.buf - sizeof(*rcd));
@@ -1080,6 +1084,12 @@
return rcd->hdr.record_id;
}
+static int erst_clearer(enum pstore_type_id type, u64 id,
+ struct pstore_info *psi)
+{
+ return erst_clear(id);
+}
+
static int __init erst_init(void)
{
int rc = 0;
diff --git a/drivers/acpi/apei/ghes.c b/drivers/acpi/apei/ghes.c
index f703b28..0784f99 100644
--- a/drivers/acpi/apei/ghes.c
+++ b/drivers/acpi/apei/ghes.c
@@ -12,7 +12,7 @@
* For more information about Generic Hardware Error Source, please
* refer to ACPI Specification version 4.0, section 17.3.2.6
*
- * Copyright 2010 Intel Corp.
+ * Copyright 2010,2011 Intel Corp.
* Author: Huang Ying <ying.huang@intel.com>
*
* This program is free software; you can redistribute it and/or
@@ -42,6 +42,9 @@
#include <linux/mutex.h>
#include <linux/ratelimit.h>
#include <linux/vmalloc.h>
+#include <linux/irq_work.h>
+#include <linux/llist.h>
+#include <linux/genalloc.h>
#include <acpi/apei.h>
#include <acpi/atomicio.h>
#include <acpi/hed.h>
@@ -53,6 +56,30 @@
#define GHES_PFX "GHES: "
#define GHES_ESTATUS_MAX_SIZE 65536
+#define GHES_ESOURCE_PREALLOC_MAX_SIZE 65536
+
+#define GHES_ESTATUS_POOL_MIN_ALLOC_ORDER 3
+
+/* This is just an estimation for memory pool allocation */
+#define GHES_ESTATUS_CACHE_AVG_SIZE 512
+
+#define GHES_ESTATUS_CACHES_SIZE 4
+
+#define GHES_ESTATUS_IN_CACHE_MAX_NSEC 10000000000ULL
+/* Prevent too many caches are allocated because of RCU */
+#define GHES_ESTATUS_CACHE_ALLOCED_MAX (GHES_ESTATUS_CACHES_SIZE * 3 / 2)
+
+#define GHES_ESTATUS_CACHE_LEN(estatus_len) \
+ (sizeof(struct ghes_estatus_cache) + (estatus_len))
+#define GHES_ESTATUS_FROM_CACHE(estatus_cache) \
+ ((struct acpi_hest_generic_status *) \
+ ((struct ghes_estatus_cache *)(estatus_cache) + 1))
+
+#define GHES_ESTATUS_NODE_LEN(estatus_len) \
+ (sizeof(struct ghes_estatus_node) + (estatus_len))
+#define GHES_ESTATUS_FROM_NODE(estatus_node) \
+ ((struct acpi_hest_generic_status *) \
+ ((struct ghes_estatus_node *)(estatus_node) + 1))
/*
* One struct ghes is created for each generic hardware error source.
@@ -77,6 +104,22 @@
};
};
+struct ghes_estatus_node {
+ struct llist_node llnode;
+ struct acpi_hest_generic *generic;
+};
+
+struct ghes_estatus_cache {
+ u32 estatus_len;
+ atomic_t count;
+ struct acpi_hest_generic *generic;
+ unsigned long long time_in;
+ struct rcu_head rcu;
+};
+
+int ghes_disable;
+module_param_named(disable, ghes_disable, bool, 0);
+
static int ghes_panic_timeout __read_mostly = 30;
/*
@@ -121,6 +164,22 @@
static DEFINE_RAW_SPINLOCK(ghes_ioremap_lock_nmi);
static DEFINE_SPINLOCK(ghes_ioremap_lock_irq);
+/*
+ * printk is not safe in NMI context. So in NMI handler, we allocate
+ * required memory from lock-less memory allocator
+ * (ghes_estatus_pool), save estatus into it, put them into lock-less
+ * list (ghes_estatus_llist), then delay printk into IRQ context via
+ * irq_work (ghes_proc_irq_work). ghes_estatus_size_request record
+ * required pool size by all NMI error source.
+ */
+static struct gen_pool *ghes_estatus_pool;
+static unsigned long ghes_estatus_pool_size_request;
+static struct llist_head ghes_estatus_llist;
+static struct irq_work ghes_proc_irq_work;
+
+struct ghes_estatus_cache *ghes_estatus_caches[GHES_ESTATUS_CACHES_SIZE];
+static atomic_t ghes_estatus_cache_alloced;
+
static int ghes_ioremap_init(void)
{
ghes_ioremap_area = __get_vm_area(PAGE_SIZE * GHES_IOREMAP_PAGES,
@@ -180,6 +239,55 @@
__flush_tlb_one(vaddr);
}
+static int ghes_estatus_pool_init(void)
+{
+ ghes_estatus_pool = gen_pool_create(GHES_ESTATUS_POOL_MIN_ALLOC_ORDER, -1);
+ if (!ghes_estatus_pool)
+ return -ENOMEM;
+ return 0;
+}
+
+static void ghes_estatus_pool_free_chunk_page(struct gen_pool *pool,
+ struct gen_pool_chunk *chunk,
+ void *data)
+{
+ free_page(chunk->start_addr);
+}
+
+static void ghes_estatus_pool_exit(void)
+{
+ gen_pool_for_each_chunk(ghes_estatus_pool,
+ ghes_estatus_pool_free_chunk_page, NULL);
+ gen_pool_destroy(ghes_estatus_pool);
+}
+
+static int ghes_estatus_pool_expand(unsigned long len)
+{
+ unsigned long i, pages, size, addr;
+ int ret;
+
+ ghes_estatus_pool_size_request += PAGE_ALIGN(len);
+ size = gen_pool_size(ghes_estatus_pool);
+ if (size >= ghes_estatus_pool_size_request)
+ return 0;
+ pages = (ghes_estatus_pool_size_request - size) / PAGE_SIZE;
+ for (i = 0; i < pages; i++) {
+ addr = __get_free_page(GFP_KERNEL);
+ if (!addr)
+ return -ENOMEM;
+ ret = gen_pool_add(ghes_estatus_pool, addr, PAGE_SIZE, -1);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+static void ghes_estatus_pool_shrink(unsigned long len)
+{
+ ghes_estatus_pool_size_request -= PAGE_ALIGN(len);
+}
+
static struct ghes *ghes_new(struct acpi_hest_generic *generic)
{
struct ghes *ghes;
@@ -341,43 +449,196 @@
ghes->flags &= ~GHES_TO_CLEAR;
}
-static void ghes_do_proc(struct ghes *ghes)
+static void ghes_do_proc(const struct acpi_hest_generic_status *estatus)
{
- int sev, processed = 0;
+ int sev, sec_sev;
struct acpi_hest_generic_data *gdata;
- sev = ghes_severity(ghes->estatus->error_severity);
- apei_estatus_for_each_section(ghes->estatus, gdata) {
-#ifdef CONFIG_X86_MCE
+ sev = ghes_severity(estatus->error_severity);
+ apei_estatus_for_each_section(estatus, gdata) {
+ sec_sev = ghes_severity(gdata->error_severity);
if (!uuid_le_cmp(*(uuid_le *)gdata->section_type,
CPER_SEC_PLATFORM_MEM)) {
- apei_mce_report_mem_error(
- sev == GHES_SEV_CORRECTED,
- (struct cper_sec_mem_err *)(gdata+1));
- processed = 1;
- }
+ struct cper_sec_mem_err *mem_err;
+ mem_err = (struct cper_sec_mem_err *)(gdata+1);
+#ifdef CONFIG_X86_MCE
+ apei_mce_report_mem_error(sev == GHES_SEV_CORRECTED,
+ mem_err);
#endif
+#ifdef CONFIG_ACPI_APEI_MEMORY_FAILURE
+ if (sev == GHES_SEV_RECOVERABLE &&
+ sec_sev == GHES_SEV_RECOVERABLE &&
+ mem_err->validation_bits & CPER_MEM_VALID_PHYSICAL_ADDRESS) {
+ unsigned long pfn;
+ pfn = mem_err->physical_addr >> PAGE_SHIFT;
+ memory_failure_queue(pfn, 0, 0);
+ }
+#endif
+ }
}
}
-static void ghes_print_estatus(const char *pfx, struct ghes *ghes)
+static void __ghes_print_estatus(const char *pfx,
+ const struct acpi_hest_generic *generic,
+ const struct acpi_hest_generic_status *estatus)
{
- /* Not more than 2 messages every 5 seconds */
- static DEFINE_RATELIMIT_STATE(ratelimit, 5*HZ, 2);
-
if (pfx == NULL) {
- if (ghes_severity(ghes->estatus->error_severity) <=
+ if (ghes_severity(estatus->error_severity) <=
GHES_SEV_CORRECTED)
pfx = KERN_WARNING HW_ERR;
else
pfx = KERN_ERR HW_ERR;
}
- if (__ratelimit(&ratelimit)) {
- printk(
- "%s""Hardware error from APEI Generic Hardware Error Source: %d\n",
- pfx, ghes->generic->header.source_id);
- apei_estatus_print(pfx, ghes->estatus);
+ printk("%s""Hardware error from APEI Generic Hardware Error Source: %d\n",
+ pfx, generic->header.source_id);
+ apei_estatus_print(pfx, estatus);
+}
+
+static int ghes_print_estatus(const char *pfx,
+ const struct acpi_hest_generic *generic,
+ const struct acpi_hest_generic_status *estatus)
+{
+ /* Not more than 2 messages every 5 seconds */
+ static DEFINE_RATELIMIT_STATE(ratelimit_corrected, 5*HZ, 2);
+ static DEFINE_RATELIMIT_STATE(ratelimit_uncorrected, 5*HZ, 2);
+ struct ratelimit_state *ratelimit;
+
+ if (ghes_severity(estatus->error_severity) <= GHES_SEV_CORRECTED)
+ ratelimit = &ratelimit_corrected;
+ else
+ ratelimit = &ratelimit_uncorrected;
+ if (__ratelimit(ratelimit)) {
+ __ghes_print_estatus(pfx, generic, estatus);
+ return 1;
}
+ return 0;
+}
+
+/*
+ * GHES error status reporting throttle, to report more kinds of
+ * errors, instead of just most frequently occurred errors.
+ */
+static int ghes_estatus_cached(struct acpi_hest_generic_status *estatus)
+{
+ u32 len;
+ int i, cached = 0;
+ unsigned long long now;
+ struct ghes_estatus_cache *cache;
+ struct acpi_hest_generic_status *cache_estatus;
+
+ len = apei_estatus_len(estatus);
+ rcu_read_lock();
+ for (i = 0; i < GHES_ESTATUS_CACHES_SIZE; i++) {
+ cache = rcu_dereference(ghes_estatus_caches[i]);
+ if (cache == NULL)
+ continue;
+ if (len != cache->estatus_len)
+ continue;
+ cache_estatus = GHES_ESTATUS_FROM_CACHE(cache);
+ if (memcmp(estatus, cache_estatus, len))
+ continue;
+ atomic_inc(&cache->count);
+ now = sched_clock();
+ if (now - cache->time_in < GHES_ESTATUS_IN_CACHE_MAX_NSEC)
+ cached = 1;
+ break;
+ }
+ rcu_read_unlock();
+ return cached;
+}
+
+static struct ghes_estatus_cache *ghes_estatus_cache_alloc(
+ struct acpi_hest_generic *generic,
+ struct acpi_hest_generic_status *estatus)
+{
+ int alloced;
+ u32 len, cache_len;
+ struct ghes_estatus_cache *cache;
+ struct acpi_hest_generic_status *cache_estatus;
+
+ alloced = atomic_add_return(1, &ghes_estatus_cache_alloced);
+ if (alloced > GHES_ESTATUS_CACHE_ALLOCED_MAX) {
+ atomic_dec(&ghes_estatus_cache_alloced);
+ return NULL;
+ }
+ len = apei_estatus_len(estatus);
+ cache_len = GHES_ESTATUS_CACHE_LEN(len);
+ cache = (void *)gen_pool_alloc(ghes_estatus_pool, cache_len);
+ if (!cache) {
+ atomic_dec(&ghes_estatus_cache_alloced);
+ return NULL;
+ }
+ cache_estatus = GHES_ESTATUS_FROM_CACHE(cache);
+ memcpy(cache_estatus, estatus, len);
+ cache->estatus_len = len;
+ atomic_set(&cache->count, 0);
+ cache->generic = generic;
+ cache->time_in = sched_clock();
+ return cache;
+}
+
+static void ghes_estatus_cache_free(struct ghes_estatus_cache *cache)
+{
+ u32 len;
+
+ len = apei_estatus_len(GHES_ESTATUS_FROM_CACHE(cache));
+ len = GHES_ESTATUS_CACHE_LEN(len);
+ gen_pool_free(ghes_estatus_pool, (unsigned long)cache, len);
+ atomic_dec(&ghes_estatus_cache_alloced);
+}
+
+static void ghes_estatus_cache_rcu_free(struct rcu_head *head)
+{
+ struct ghes_estatus_cache *cache;
+
+ cache = container_of(head, struct ghes_estatus_cache, rcu);
+ ghes_estatus_cache_free(cache);
+}
+
+static void ghes_estatus_cache_add(
+ struct acpi_hest_generic *generic,
+ struct acpi_hest_generic_status *estatus)
+{
+ int i, slot = -1, count;
+ unsigned long long now, duration, period, max_period = 0;
+ struct ghes_estatus_cache *cache, *slot_cache = NULL, *new_cache;
+
+ new_cache = ghes_estatus_cache_alloc(generic, estatus);
+ if (new_cache == NULL)
+ return;
+ rcu_read_lock();
+ now = sched_clock();
+ for (i = 0; i < GHES_ESTATUS_CACHES_SIZE; i++) {
+ cache = rcu_dereference(ghes_estatus_caches[i]);
+ if (cache == NULL) {
+ slot = i;
+ slot_cache = NULL;
+ break;
+ }
+ duration = now - cache->time_in;
+ if (duration >= GHES_ESTATUS_IN_CACHE_MAX_NSEC) {
+ slot = i;
+ slot_cache = cache;
+ break;
+ }
+ count = atomic_read(&cache->count);
+ period = duration;
+ do_div(period, (count + 1));
+ if (period > max_period) {
+ max_period = period;
+ slot = i;
+ slot_cache = cache;
+ }
+ }
+ /* new_cache must be put into array after its contents are written */
+ smp_wmb();
+ if (slot != -1 && cmpxchg(ghes_estatus_caches + slot,
+ slot_cache, new_cache) == slot_cache) {
+ if (slot_cache)
+ call_rcu(&slot_cache->rcu, ghes_estatus_cache_rcu_free);
+ } else
+ ghes_estatus_cache_free(new_cache);
+ rcu_read_unlock();
}
static int ghes_proc(struct ghes *ghes)
@@ -387,9 +648,11 @@
rc = ghes_read_estatus(ghes, 0);
if (rc)
goto out;
- ghes_print_estatus(NULL, ghes);
- ghes_do_proc(ghes);
-
+ if (!ghes_estatus_cached(ghes->estatus)) {
+ if (ghes_print_estatus(NULL, ghes->generic, ghes->estatus))
+ ghes_estatus_cache_add(ghes->generic, ghes->estatus);
+ }
+ ghes_do_proc(ghes->estatus);
out:
ghes_clear_estatus(ghes);
return 0;
@@ -447,6 +710,45 @@
return ret;
}
+static void ghes_proc_in_irq(struct irq_work *irq_work)
+{
+ struct llist_node *llnode, *next, *tail = NULL;
+ struct ghes_estatus_node *estatus_node;
+ struct acpi_hest_generic *generic;
+ struct acpi_hest_generic_status *estatus;
+ u32 len, node_len;
+
+ /*
+ * Because the time order of estatus in list is reversed,
+ * revert it back to proper order.
+ */
+ llnode = llist_del_all(&ghes_estatus_llist);
+ while (llnode) {
+ next = llnode->next;
+ llnode->next = tail;
+ tail = llnode;
+ llnode = next;
+ }
+ llnode = tail;
+ while (llnode) {
+ next = llnode->next;
+ estatus_node = llist_entry(llnode, struct ghes_estatus_node,
+ llnode);
+ estatus = GHES_ESTATUS_FROM_NODE(estatus_node);
+ len = apei_estatus_len(estatus);
+ node_len = GHES_ESTATUS_NODE_LEN(len);
+ ghes_do_proc(estatus);
+ if (!ghes_estatus_cached(estatus)) {
+ generic = estatus_node->generic;
+ if (ghes_print_estatus(NULL, generic, estatus))
+ ghes_estatus_cache_add(generic, estatus);
+ }
+ gen_pool_free(ghes_estatus_pool, (unsigned long)estatus_node,
+ node_len);
+ llnode = next;
+ }
+}
+
static int ghes_notify_nmi(struct notifier_block *this,
unsigned long cmd, void *data)
{
@@ -476,7 +778,8 @@
if (sev_global >= GHES_SEV_PANIC) {
oops_begin();
- ghes_print_estatus(KERN_EMERG HW_ERR, ghes_global);
+ __ghes_print_estatus(KERN_EMERG HW_ERR, ghes_global->generic,
+ ghes_global->estatus);
/* reboot to log the error! */
if (panic_timeout == 0)
panic_timeout = ghes_panic_timeout;
@@ -484,12 +787,34 @@
}
list_for_each_entry_rcu(ghes, &ghes_nmi, list) {
+#ifdef CONFIG_ARCH_HAVE_NMI_SAFE_CMPXCHG
+ u32 len, node_len;
+ struct ghes_estatus_node *estatus_node;
+ struct acpi_hest_generic_status *estatus;
+#endif
if (!(ghes->flags & GHES_TO_CLEAR))
continue;
- /* Do not print estatus because printk is not NMI safe */
- ghes_do_proc(ghes);
+#ifdef CONFIG_ARCH_HAVE_NMI_SAFE_CMPXCHG
+ if (ghes_estatus_cached(ghes->estatus))
+ goto next;
+ /* Save estatus for further processing in IRQ context */
+ len = apei_estatus_len(ghes->estatus);
+ node_len = GHES_ESTATUS_NODE_LEN(len);
+ estatus_node = (void *)gen_pool_alloc(ghes_estatus_pool,
+ node_len);
+ if (estatus_node) {
+ estatus_node->generic = ghes->generic;
+ estatus = GHES_ESTATUS_FROM_NODE(estatus_node);
+ memcpy(estatus, ghes->estatus, len);
+ llist_add(&estatus_node->llnode, &ghes_estatus_llist);
+ }
+next:
+#endif
ghes_clear_estatus(ghes);
}
+#ifdef CONFIG_ARCH_HAVE_NMI_SAFE_CMPXCHG
+ irq_work_queue(&ghes_proc_irq_work);
+#endif
out:
raw_spin_unlock(&ghes_nmi_lock);
@@ -504,10 +829,26 @@
.notifier_call = ghes_notify_nmi,
};
+static unsigned long ghes_esource_prealloc_size(
+ const struct acpi_hest_generic *generic)
+{
+ unsigned long block_length, prealloc_records, prealloc_size;
+
+ block_length = min_t(unsigned long, generic->error_block_length,
+ GHES_ESTATUS_MAX_SIZE);
+ prealloc_records = max_t(unsigned long,
+ generic->records_to_preallocate, 1);
+ prealloc_size = min_t(unsigned long, block_length * prealloc_records,
+ GHES_ESOURCE_PREALLOC_MAX_SIZE);
+
+ return prealloc_size;
+}
+
static int __devinit ghes_probe(struct platform_device *ghes_dev)
{
struct acpi_hest_generic *generic;
struct ghes *ghes = NULL;
+ unsigned long len;
int rc = -EINVAL;
generic = *(struct acpi_hest_generic **)ghes_dev->dev.platform_data;
@@ -573,6 +914,8 @@
mutex_unlock(&ghes_list_mutex);
break;
case ACPI_HEST_NOTIFY_NMI:
+ len = ghes_esource_prealloc_size(generic);
+ ghes_estatus_pool_expand(len);
mutex_lock(&ghes_list_mutex);
if (list_empty(&ghes_nmi))
register_die_notifier(&ghes_notifier_nmi);
@@ -597,6 +940,7 @@
{
struct ghes *ghes;
struct acpi_hest_generic *generic;
+ unsigned long len;
ghes = platform_get_drvdata(ghes_dev);
generic = ghes->generic;
@@ -627,6 +971,8 @@
* freed after NMI handler finishes.
*/
synchronize_rcu();
+ len = ghes_esource_prealloc_size(generic);
+ ghes_estatus_pool_shrink(len);
break;
default:
BUG();
@@ -662,15 +1008,43 @@
return -EINVAL;
}
+ if (ghes_disable) {
+ pr_info(GHES_PFX "GHES is not enabled!\n");
+ return -EINVAL;
+ }
+
+ init_irq_work(&ghes_proc_irq_work, ghes_proc_in_irq);
+
rc = ghes_ioremap_init();
if (rc)
goto err;
- rc = platform_driver_register(&ghes_platform_driver);
+ rc = ghes_estatus_pool_init();
if (rc)
goto err_ioremap_exit;
+ rc = ghes_estatus_pool_expand(GHES_ESTATUS_CACHE_AVG_SIZE *
+ GHES_ESTATUS_CACHE_ALLOCED_MAX);
+ if (rc)
+ goto err_pool_exit;
+
+ rc = platform_driver_register(&ghes_platform_driver);
+ if (rc)
+ goto err_pool_exit;
+
+ rc = apei_osc_setup();
+ if (rc == 0 && osc_sb_apei_support_acked)
+ pr_info(GHES_PFX "APEI firmware first mode is enabled by APEI bit and WHEA _OSC.\n");
+ else if (rc == 0 && !osc_sb_apei_support_acked)
+ pr_info(GHES_PFX "APEI firmware first mode is enabled by WHEA _OSC.\n");
+ else if (rc && osc_sb_apei_support_acked)
+ pr_info(GHES_PFX "APEI firmware first mode is enabled by APEI bit.\n");
+ else
+ pr_info(GHES_PFX "Failed to enable APEI firmware first mode.\n");
+
return 0;
+err_pool_exit:
+ ghes_estatus_pool_exit();
err_ioremap_exit:
ghes_ioremap_exit();
err:
@@ -680,6 +1054,7 @@
static void __exit ghes_exit(void)
{
platform_driver_unregister(&ghes_platform_driver);
+ ghes_estatus_pool_exit();
ghes_ioremap_exit();
}
diff --git a/drivers/acpi/apei/hest.c b/drivers/acpi/apei/hest.c
index 181bc2f..05fee06 100644
--- a/drivers/acpi/apei/hest.c
+++ b/drivers/acpi/apei/hest.c
@@ -231,16 +231,17 @@
goto err;
}
- rc = apei_hest_parse(hest_parse_ghes_count, &ghes_count);
- if (rc)
- goto err;
-
- rc = hest_ghes_dev_register(ghes_count);
- if (!rc) {
- pr_info(HEST_PFX "Table parsing has been initialized.\n");
- return;
+ if (!ghes_disable) {
+ rc = apei_hest_parse(hest_parse_ghes_count, &ghes_count);
+ if (rc)
+ goto err;
+ rc = hest_ghes_dev_register(ghes_count);
+ if (rc)
+ goto err;
}
+ pr_info(HEST_PFX "Table parsing has been initialized.\n");
+ return;
err:
hest_disable = 1;
}
diff --git a/drivers/acpi/battery.c b/drivers/acpi/battery.c
index 2c66135..7711d94 100644
--- a/drivers/acpi/battery.c
+++ b/drivers/acpi/battery.c
@@ -55,6 +55,9 @@
#define ACPI_BATTERY_NOTIFY_INFO 0x81
#define ACPI_BATTERY_NOTIFY_THRESHOLD 0x82
+/* Battery power unit: 0 means mW, 1 means mA */
+#define ACPI_BATTERY_POWER_UNIT_MA 1
+
#define _COMPONENT ACPI_BATTERY_COMPONENT
ACPI_MODULE_NAME("battery");
@@ -91,16 +94,12 @@
enum {
ACPI_BATTERY_ALARM_PRESENT,
ACPI_BATTERY_XINFO_PRESENT,
- /* For buggy DSDTs that report negative 16-bit values for either
- * charging or discharging current and/or report 0 as 65536
- * due to bad math.
- */
- ACPI_BATTERY_QUIRK_SIGNED16_CURRENT,
ACPI_BATTERY_QUIRK_PERCENTAGE_CAPACITY,
};
struct acpi_battery {
struct mutex lock;
+ struct mutex sysfs_lock;
struct power_supply bat;
struct acpi_device *device;
struct notifier_block pm_nb;
@@ -301,7 +300,8 @@
#ifdef CONFIG_ACPI_PROCFS_POWER
inline char *acpi_battery_units(struct acpi_battery *battery)
{
- return (battery->power_unit)?"mA":"mW";
+ return (battery->power_unit == ACPI_BATTERY_POWER_UNIT_MA) ?
+ "mA" : "mW";
}
#endif
@@ -461,9 +461,17 @@
battery->update_time = jiffies;
kfree(buffer.pointer);
- if (test_bit(ACPI_BATTERY_QUIRK_SIGNED16_CURRENT, &battery->flags) &&
- battery->rate_now != -1)
+ /* For buggy DSDTs that report negative 16-bit values for either
+ * charging or discharging current and/or report 0 as 65536
+ * due to bad math.
+ */
+ if (battery->power_unit == ACPI_BATTERY_POWER_UNIT_MA &&
+ battery->rate_now != ACPI_BATTERY_VALUE_UNKNOWN &&
+ (s16)(battery->rate_now) < 0) {
battery->rate_now = abs((s16)battery->rate_now);
+ printk_once(KERN_WARNING FW_BUG "battery: (dis)charge rate"
+ " invalid.\n");
+ }
if (test_bit(ACPI_BATTERY_QUIRK_PERCENTAGE_CAPACITY, &battery->flags)
&& battery->capacity_now >= 0 && battery->capacity_now <= 100)
@@ -544,7 +552,7 @@
{
int result;
- if (battery->power_unit) {
+ if (battery->power_unit == ACPI_BATTERY_POWER_UNIT_MA) {
battery->bat.properties = charge_battery_props;
battery->bat.num_properties =
ARRAY_SIZE(charge_battery_props);
@@ -566,18 +574,16 @@
static void sysfs_remove_battery(struct acpi_battery *battery)
{
- if (!battery->bat.dev)
+ mutex_lock(&battery->sysfs_lock);
+ if (!battery->bat.dev) {
+ mutex_unlock(&battery->sysfs_lock);
return;
+ }
+
device_remove_file(battery->bat.dev, &alarm_attr);
power_supply_unregister(&battery->bat);
battery->bat.dev = NULL;
-}
-
-static void acpi_battery_quirks(struct acpi_battery *battery)
-{
- if (dmi_name_in_vendors("Acer") && battery->power_unit) {
- set_bit(ACPI_BATTERY_QUIRK_SIGNED16_CURRENT, &battery->flags);
- }
+ mutex_unlock(&battery->sysfs_lock);
}
/*
@@ -592,7 +598,7 @@
*
* Handle this correctly so that they won't break userspace.
*/
-static void acpi_battery_quirks2(struct acpi_battery *battery)
+static void acpi_battery_quirks(struct acpi_battery *battery)
{
if (test_bit(ACPI_BATTERY_QUIRK_PERCENTAGE_CAPACITY, &battery->flags))
return ;
@@ -623,13 +629,15 @@
result = acpi_battery_get_info(battery);
if (result)
return result;
- acpi_battery_quirks(battery);
acpi_battery_init_alarm(battery);
}
- if (!battery->bat.dev)
- sysfs_add_battery(battery);
+ if (!battery->bat.dev) {
+ result = sysfs_add_battery(battery);
+ if (result)
+ return result;
+ }
result = acpi_battery_get_state(battery);
- acpi_battery_quirks2(battery);
+ acpi_battery_quirks(battery);
return result;
}
@@ -863,7 +871,7 @@
}, \
}
-static struct battery_file {
+static const struct battery_file {
struct file_operations ops;
mode_t mode;
const char *name;
@@ -948,9 +956,12 @@
struct acpi_battery *battery = container_of(nb, struct acpi_battery,
pm_nb);
switch (mode) {
+ case PM_POST_HIBERNATION:
case PM_POST_SUSPEND:
- sysfs_remove_battery(battery);
- sysfs_add_battery(battery);
+ if (battery->bat.dev) {
+ sysfs_remove_battery(battery);
+ sysfs_add_battery(battery);
+ }
break;
}
@@ -972,28 +983,38 @@
strcpy(acpi_device_class(device), ACPI_BATTERY_CLASS);
device->driver_data = battery;
mutex_init(&battery->lock);
+ mutex_init(&battery->sysfs_lock);
if (ACPI_SUCCESS(acpi_get_handle(battery->device->handle,
"_BIX", &handle)))
set_bit(ACPI_BATTERY_XINFO_PRESENT, &battery->flags);
- acpi_battery_update(battery);
+ result = acpi_battery_update(battery);
+ if (result)
+ goto fail;
#ifdef CONFIG_ACPI_PROCFS_POWER
result = acpi_battery_add_fs(device);
#endif
- if (!result) {
- printk(KERN_INFO PREFIX "%s Slot [%s] (battery %s)\n",
- ACPI_BATTERY_DEVICE_NAME, acpi_device_bid(device),
- device->status.battery_present ? "present" : "absent");
- } else {
+ if (result) {
#ifdef CONFIG_ACPI_PROCFS_POWER
acpi_battery_remove_fs(device);
#endif
- kfree(battery);
+ goto fail;
}
+ printk(KERN_INFO PREFIX "%s Slot [%s] (battery %s)\n",
+ ACPI_BATTERY_DEVICE_NAME, acpi_device_bid(device),
+ device->status.battery_present ? "present" : "absent");
+
battery->pm_nb.notifier_call = battery_notify;
register_pm_notifier(&battery->pm_nb);
return result;
+
+fail:
+ sysfs_remove_battery(battery);
+ mutex_destroy(&battery->lock);
+ mutex_destroy(&battery->sysfs_lock);
+ kfree(battery);
+ return result;
}
static int acpi_battery_remove(struct acpi_device *device, int type)
@@ -1009,6 +1030,7 @@
#endif
sysfs_remove_battery(battery);
mutex_destroy(&battery->lock);
+ mutex_destroy(&battery->sysfs_lock);
kfree(battery);
return 0;
}
diff --git a/drivers/acpi/bus.c b/drivers/acpi/bus.c
index d1e06c1..437ddbf 100644
--- a/drivers/acpi/bus.c
+++ b/drivers/acpi/bus.c
@@ -39,6 +39,7 @@
#include <linux/pci.h>
#include <acpi/acpi_bus.h>
#include <acpi/acpi_drivers.h>
+#include <acpi/apei.h>
#include <linux/dmi.h>
#include <linux/suspend.h>
@@ -519,6 +520,7 @@
}
EXPORT_SYMBOL(acpi_run_osc);
+bool osc_sb_apei_support_acked;
static u8 sb_uuid_str[] = "0811B06E-4A27-44F9-8D60-3CBBC22E7B48";
static void acpi_bus_osc_support(void)
{
@@ -541,11 +543,19 @@
#if defined(CONFIG_ACPI_PROCESSOR) || defined(CONFIG_ACPI_PROCESSOR_MODULE)
capbuf[OSC_SUPPORT_TYPE] |= OSC_SB_PPC_OST_SUPPORT;
#endif
+
+ if (!ghes_disable)
+ capbuf[OSC_SUPPORT_TYPE] |= OSC_SB_APEI_SUPPORT;
if (ACPI_FAILURE(acpi_get_handle(NULL, "\\_SB", &handle)))
return;
- if (ACPI_SUCCESS(acpi_run_osc(handle, &context)))
+ if (ACPI_SUCCESS(acpi_run_osc(handle, &context))) {
+ u32 *capbuf_ret = context.ret.pointer;
+ if (context.ret.length > OSC_SUPPORT_TYPE)
+ osc_sb_apei_support_acked =
+ capbuf_ret[OSC_SUPPORT_TYPE] & OSC_SB_APEI_SUPPORT;
kfree(context.ret.pointer);
- /* do we need to check the returned cap? Sounds no */
+ }
+ /* do we need to check other returned cap? Sounds no */
}
/* --------------------------------------------------------------------------
diff --git a/drivers/acpi/dock.c b/drivers/acpi/dock.c
index 1864ad3..19a6113 100644
--- a/drivers/acpi/dock.c
+++ b/drivers/acpi/dock.c
@@ -77,7 +77,7 @@
struct list_head list;
struct list_head hotplug_list;
acpi_handle handle;
- struct acpi_dock_ops *ops;
+ const struct acpi_dock_ops *ops;
void *context;
};
@@ -589,7 +589,7 @@
* the dock driver after _DCK is executed.
*/
int
-register_hotplug_dock_device(acpi_handle handle, struct acpi_dock_ops *ops,
+register_hotplug_dock_device(acpi_handle handle, const struct acpi_dock_ops *ops,
void *context)
{
struct dock_dependent_device *dd;
diff --git a/drivers/acpi/ec_sys.c b/drivers/acpi/ec_sys.c
index 05b4420..22f918b 100644
--- a/drivers/acpi/ec_sys.c
+++ b/drivers/acpi/ec_sys.c
@@ -92,7 +92,7 @@
return count;
}
-static struct file_operations acpi_ec_io_ops = {
+static const struct file_operations acpi_ec_io_ops = {
.owner = THIS_MODULE,
.open = acpi_ec_open_io,
.read = acpi_ec_read_io,
diff --git a/drivers/acpi/fan.c b/drivers/acpi/fan.c
index 467479f..0f0356c 100644
--- a/drivers/acpi/fan.c
+++ b/drivers/acpi/fan.c
@@ -110,7 +110,7 @@
return result;
}
-static struct thermal_cooling_device_ops fan_cooling_ops = {
+static const struct thermal_cooling_device_ops fan_cooling_ops = {
.get_max_state = fan_get_max_state,
.get_cur_state = fan_get_cur_state,
.set_cur_state = fan_set_cur_state,
diff --git a/drivers/acpi/osl.c b/drivers/acpi/osl.c
index 372f9b7..fa32f58 100644
--- a/drivers/acpi/osl.c
+++ b/drivers/acpi/osl.c
@@ -155,7 +155,7 @@
{
if (!strcmp("Linux", interface)) {
- printk(KERN_NOTICE FW_BUG PREFIX
+ printk_once(KERN_NOTICE FW_BUG PREFIX
"BIOS _OSI(Linux) query %s%s\n",
osi_linux.enable ? "honored" : "ignored",
osi_linux.cmdline ? " via cmdline" :
@@ -237,8 +237,23 @@
#endif
}
+#ifdef CONFIG_KEXEC
+static unsigned long acpi_rsdp;
+static int __init setup_acpi_rsdp(char *arg)
+{
+ acpi_rsdp = simple_strtoul(arg, NULL, 16);
+ return 0;
+}
+early_param("acpi_rsdp", setup_acpi_rsdp);
+#endif
+
acpi_physical_address __init acpi_os_get_root_pointer(void)
{
+#ifdef CONFIG_KEXEC
+ if (acpi_rsdp)
+ return acpi_rsdp;
+#endif
+
if (efi_enabled) {
if (efi.acpi20 != EFI_INVALID_TABLE_ADDR)
return efi.acpi20;
@@ -1083,7 +1098,13 @@
bool enable;
};
-static struct osi_setup_entry __initdata osi_setup_entries[OSI_STRING_ENTRIES_MAX];
+static struct osi_setup_entry __initdata
+ osi_setup_entries[OSI_STRING_ENTRIES_MAX] = {
+ {"Module Device", true},
+ {"Processor Device", true},
+ {"3.0 _SCP Extensions", true},
+ {"Processor Aggregator Device", true},
+};
void __init acpi_osi_setup(char *str)
{
diff --git a/drivers/acpi/pci_irq.c b/drivers/acpi/pci_irq.c
index f907cfb..7f9eba9 100644
--- a/drivers/acpi/pci_irq.c
+++ b/drivers/acpi/pci_irq.c
@@ -303,6 +303,61 @@
/* --------------------------------------------------------------------------
PCI Interrupt Routing Support
-------------------------------------------------------------------------- */
+#ifdef CONFIG_X86_IO_APIC
+extern int noioapicquirk;
+extern int noioapicreroute;
+
+static int bridge_has_boot_interrupt_variant(struct pci_bus *bus)
+{
+ struct pci_bus *bus_it;
+
+ for (bus_it = bus ; bus_it ; bus_it = bus_it->parent) {
+ if (!bus_it->self)
+ return 0;
+ if (bus_it->self->irq_reroute_variant)
+ return bus_it->self->irq_reroute_variant;
+ }
+ return 0;
+}
+
+/*
+ * Some chipsets (e.g. Intel 6700PXH) generate a legacy INTx when the IRQ
+ * entry in the chipset's IO-APIC is masked (as, e.g. the RT kernel does
+ * during interrupt handling). When this INTx generation cannot be disabled,
+ * we reroute these interrupts to their legacy equivalent to get rid of
+ * spurious interrupts.
+ */
+static int acpi_reroute_boot_interrupt(struct pci_dev *dev,
+ struct acpi_prt_entry *entry)
+{
+ if (noioapicquirk || noioapicreroute) {
+ return 0;
+ } else {
+ switch (bridge_has_boot_interrupt_variant(dev->bus)) {
+ case 0:
+ /* no rerouting necessary */
+ return 0;
+ case INTEL_IRQ_REROUTE_VARIANT:
+ /*
+ * Remap according to INTx routing table in 6700PXH
+ * specs, intel order number 302628-002, section
+ * 2.15.2. Other chipsets (80332, ...) have the same
+ * mapping and are handled here as well.
+ */
+ dev_info(&dev->dev, "PCI IRQ %d -> rerouted to legacy "
+ "IRQ %d\n", entry->index,
+ (entry->index % 4) + 16);
+ entry->index = (entry->index % 4) + 16;
+ return 1;
+ default:
+ dev_warn(&dev->dev, "Cannot reroute IRQ %d to legacy "
+ "IRQ: unknown mapping\n", entry->index);
+ return -1;
+ }
+ }
+}
+#endif /* CONFIG_X86_IO_APIC */
+
static struct acpi_prt_entry *acpi_pci_irq_lookup(struct pci_dev *dev, int pin)
{
struct acpi_prt_entry *entry;
@@ -311,6 +366,9 @@
entry = acpi_pci_irq_find_prt_entry(dev, pin);
if (entry) {
+#ifdef CONFIG_X86_IO_APIC
+ acpi_reroute_boot_interrupt(dev, entry);
+#endif /* CONFIG_X86_IO_APIC */
ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Found %s[%c] _PRT entry\n",
pci_name(dev), pin_name(pin)));
return entry;
diff --git a/drivers/acpi/pci_root.c b/drivers/acpi/pci_root.c
index d06078d..2672c79 100644
--- a/drivers/acpi/pci_root.c
+++ b/drivers/acpi/pci_root.c
@@ -485,7 +485,8 @@
root->secondary.end = 0xFF;
printk(KERN_WARNING FW_BUG PREFIX
"no secondary bus range in _CRS\n");
- status = acpi_evaluate_integer(device->handle, METHOD_NAME__BBN, NULL, &bus);
+ status = acpi_evaluate_integer(device->handle, METHOD_NAME__BBN,
+ NULL, &bus);
if (ACPI_SUCCESS(status))
root->secondary.start = bus;
else if (status == AE_NOT_FOUND)
diff --git a/drivers/acpi/processor_thermal.c b/drivers/acpi/processor_thermal.c
index 79cb653..870550d 100644
--- a/drivers/acpi/processor_thermal.c
+++ b/drivers/acpi/processor_thermal.c
@@ -244,7 +244,7 @@
return result;
}
-struct thermal_cooling_device_ops processor_cooling_ops = {
+const struct thermal_cooling_device_ops processor_cooling_ops = {
.get_max_state = processor_get_max_state,
.get_cur_state = processor_get_cur_state,
.set_cur_state = processor_set_cur_state,
diff --git a/drivers/acpi/sbs.c b/drivers/acpi/sbs.c
index 50658ff..6e36d0c 100644
--- a/drivers/acpi/sbs.c
+++ b/drivers/acpi/sbs.c
@@ -130,6 +130,9 @@
#define to_acpi_sbs(x) container_of(x, struct acpi_sbs, charger)
+static int acpi_sbs_remove(struct acpi_device *device, int type);
+static int acpi_battery_get_state(struct acpi_battery *battery);
+
static inline int battery_scale(int log)
{
int scale = 1;
@@ -195,6 +198,8 @@
if ((!battery->present) && psp != POWER_SUPPLY_PROP_PRESENT)
return -ENODEV;
+
+ acpi_battery_get_state(battery);
switch (psp) {
case POWER_SUPPLY_PROP_STATUS:
if (battery->rate_now < 0)
@@ -225,11 +230,17 @@
case POWER_SUPPLY_PROP_POWER_NOW:
val->intval = abs(battery->rate_now) *
acpi_battery_ipscale(battery) * 1000;
+ val->intval *= (acpi_battery_mode(battery)) ?
+ (battery->voltage_now *
+ acpi_battery_vscale(battery) / 1000) : 1;
break;
case POWER_SUPPLY_PROP_CURRENT_AVG:
case POWER_SUPPLY_PROP_POWER_AVG:
val->intval = abs(battery->rate_avg) *
acpi_battery_ipscale(battery) * 1000;
+ val->intval *= (acpi_battery_mode(battery)) ?
+ (battery->voltage_now *
+ acpi_battery_vscale(battery) / 1000) : 1;
break;
case POWER_SUPPLY_PROP_CAPACITY:
val->intval = battery->state_of_charge;
@@ -903,8 +914,6 @@
}
}
-static int acpi_sbs_remove(struct acpi_device *device, int type);
-
static int acpi_sbs_add(struct acpi_device *device)
{
struct acpi_sbs *sbs;
diff --git a/drivers/acpi/sleep.c b/drivers/acpi/sleep.c
index 6c94960..3ed80b2 100644
--- a/drivers/acpi/sleep.c
+++ b/drivers/acpi/sleep.c
@@ -428,6 +428,22 @@
DMI_MATCH(DMI_PRODUCT_NAME, "1000 Series"),
},
},
+ {
+ .callback = init_old_suspend_ordering,
+ .ident = "Asus A8N-SLI DELUXE",
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "ASUSTeK Computer INC."),
+ DMI_MATCH(DMI_BOARD_NAME, "A8N-SLI DELUXE"),
+ },
+ },
+ {
+ .callback = init_old_suspend_ordering,
+ .ident = "Asus A8N-SLI Premium",
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "ASUSTeK Computer INC."),
+ DMI_MATCH(DMI_BOARD_NAME, "A8N-SLI Premium"),
+ },
+ },
{},
};
#endif /* CONFIG_SUSPEND */
diff --git a/drivers/acpi/sysfs.c b/drivers/acpi/sysfs.c
index 77255f2..c538d0e 100644
--- a/drivers/acpi/sysfs.c
+++ b/drivers/acpi/sysfs.c
@@ -149,12 +149,12 @@
return result;
}
-static struct kernel_param_ops param_ops_debug_layer = {
+static const struct kernel_param_ops param_ops_debug_layer = {
.set = param_set_uint,
.get = param_get_debug_layer,
};
-static struct kernel_param_ops param_ops_debug_level = {
+static const struct kernel_param_ops param_ops_debug_level = {
.set = param_set_uint,
.get = param_get_debug_level,
};
diff --git a/drivers/acpi/thermal.c b/drivers/acpi/thermal.c
index 2607e17..48fbc64 100644
--- a/drivers/acpi/thermal.c
+++ b/drivers/acpi/thermal.c
@@ -812,7 +812,7 @@
thermal_zone_unbind_cooling_device);
}
-static struct thermal_zone_device_ops acpi_thermal_zone_ops = {
+static const struct thermal_zone_device_ops acpi_thermal_zone_ops = {
.bind = acpi_thermal_bind_cooling_device,
.unbind = acpi_thermal_unbind_cooling_device,
.get_temp = thermal_get_temp,
diff --git a/drivers/acpi/video.c b/drivers/acpi/video.c
index ada4b4d..08a44b5 100644
--- a/drivers/acpi/video.c
+++ b/drivers/acpi/video.c
@@ -307,7 +307,7 @@
return acpi_video_device_lcd_set_level(video, level);
}
-static struct thermal_cooling_device_ops video_cooling_ops = {
+static const struct thermal_cooling_device_ops video_cooling_ops = {
.get_max_state = video_get_max_state,
.get_cur_state = video_get_cur_state,
.set_cur_state = video_set_cur_state,
diff --git a/drivers/ata/Kconfig b/drivers/ata/Kconfig
index ca3e6be..5987e0b 100644
--- a/drivers/ata/Kconfig
+++ b/drivers/ata/Kconfig
@@ -468,6 +468,15 @@
interface card. This is not required for ICS partition support.
If you are unsure, say N to this.
+config PATA_IMX
+ tristate "PATA support for Freescale iMX"
+ depends on ARCH_MXC
+ help
+ This option enables support for the PATA host available on Freescale
+ iMX SoCs.
+
+ If unsure, say N.
+
config PATA_IT8213
tristate "IT8213 PATA support (Experimental)"
depends on PCI && EXPERIMENTAL
diff --git a/drivers/ata/Makefile b/drivers/ata/Makefile
index 8ac64e1..9550d69 100644
--- a/drivers/ata/Makefile
+++ b/drivers/ata/Makefile
@@ -48,6 +48,7 @@
obj-$(CONFIG_PATA_HPT3X2N) += pata_hpt3x2n.o
obj-$(CONFIG_PATA_HPT3X3) += pata_hpt3x3.o
obj-$(CONFIG_PATA_ICSIDE) += pata_icside.o
+obj-$(CONFIG_PATA_IMX) += pata_imx.o
obj-$(CONFIG_PATA_IT8213) += pata_it8213.o
obj-$(CONFIG_PATA_IT821X) += pata_it821x.o
obj-$(CONFIG_PATA_JMICRON) += pata_jmicron.o
diff --git a/drivers/ata/libata-acpi.c b/drivers/ata/libata-acpi.c
index e0a5b55..bb7c5f1 100644
--- a/drivers/ata/libata-acpi.c
+++ b/drivers/ata/libata-acpi.c
@@ -218,12 +218,12 @@
ata_acpi_uevent(dev->link->ap, dev, event);
}
-static struct acpi_dock_ops ata_acpi_dev_dock_ops = {
+static const struct acpi_dock_ops ata_acpi_dev_dock_ops = {
.handler = ata_acpi_dev_notify_dock,
.uevent = ata_acpi_dev_uevent,
};
-static struct acpi_dock_ops ata_acpi_ap_dock_ops = {
+static const struct acpi_dock_ops ata_acpi_ap_dock_ops = {
.handler = ata_acpi_ap_notify_dock,
.uevent = ata_acpi_ap_uevent,
};
diff --git a/drivers/ata/pata_imx.c b/drivers/ata/pata_imx.c
new file mode 100644
index 0000000..ca9d9ca
--- /dev/null
+++ b/drivers/ata/pata_imx.c
@@ -0,0 +1,253 @@
+/*
+ * Freescale iMX PATA driver
+ *
+ * Copyright (C) 2011 Arnaud Patard <arnaud.patard@rtp-net.org>
+ *
+ * Based on pata_platform - Copyright (C) 2006 - 2007 Paul Mundt
+ *
+ * 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 more details.
+ *
+ * TODO:
+ * - dmaengine support
+ * - check if timing stuff needed
+ */
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/blkdev.h>
+#include <scsi/scsi_host.h>
+#include <linux/ata.h>
+#include <linux/libata.h>
+#include <linux/platform_device.h>
+#include <linux/clk.h>
+
+#define DRV_NAME "pata_imx"
+
+#define PATA_IMX_ATA_CONTROL 0x24
+#define PATA_IMX_ATA_CTRL_FIFO_RST_B (1<<7)
+#define PATA_IMX_ATA_CTRL_ATA_RST_B (1<<6)
+#define PATA_IMX_ATA_CTRL_IORDY_EN (1<<0)
+#define PATA_IMX_ATA_INT_EN 0x2C
+#define PATA_IMX_ATA_INTR_ATA_INTRQ2 (1<<3)
+#define PATA_IMX_DRIVE_DATA 0xA0
+#define PATA_IMX_DRIVE_CONTROL 0xD8
+
+struct pata_imx_priv {
+ struct clk *clk;
+ /* timings/interrupt/control regs */
+ u8 *host_regs;
+ u32 ata_ctl;
+};
+
+static int pata_imx_set_mode(struct ata_link *link, struct ata_device **unused)
+{
+ struct ata_device *dev;
+ struct ata_port *ap = link->ap;
+ struct pata_imx_priv *priv = ap->host->private_data;
+ u32 val;
+
+ ata_for_each_dev(dev, link, ENABLED) {
+ dev->pio_mode = dev->xfer_mode = XFER_PIO_0;
+ dev->xfer_shift = ATA_SHIFT_PIO;
+ dev->flags |= ATA_DFLAG_PIO;
+
+ val = __raw_readl(priv->host_regs + PATA_IMX_ATA_CONTROL);
+ if (ata_pio_need_iordy(dev))
+ val |= PATA_IMX_ATA_CTRL_IORDY_EN;
+ else
+ val &= ~PATA_IMX_ATA_CTRL_IORDY_EN;
+ __raw_writel(val, priv->host_regs + PATA_IMX_ATA_CONTROL);
+
+ ata_dev_printk(dev, KERN_INFO, "configured for PIO\n");
+ }
+ return 0;
+}
+
+static struct scsi_host_template pata_imx_sht = {
+ ATA_PIO_SHT(DRV_NAME),
+};
+
+static struct ata_port_operations pata_imx_port_ops = {
+ .inherits = &ata_sff_port_ops,
+ .sff_data_xfer = ata_sff_data_xfer_noirq,
+ .cable_detect = ata_cable_unknown,
+ .set_mode = pata_imx_set_mode,
+};
+
+static void pata_imx_setup_port(struct ata_ioports *ioaddr)
+{
+ /* Fixup the port shift for platforms that need it */
+ ioaddr->data_addr = ioaddr->cmd_addr + (ATA_REG_DATA << 2);
+ ioaddr->error_addr = ioaddr->cmd_addr + (ATA_REG_ERR << 2);
+ ioaddr->feature_addr = ioaddr->cmd_addr + (ATA_REG_FEATURE << 2);
+ ioaddr->nsect_addr = ioaddr->cmd_addr + (ATA_REG_NSECT << 2);
+ ioaddr->lbal_addr = ioaddr->cmd_addr + (ATA_REG_LBAL << 2);
+ ioaddr->lbam_addr = ioaddr->cmd_addr + (ATA_REG_LBAM << 2);
+ ioaddr->lbah_addr = ioaddr->cmd_addr + (ATA_REG_LBAH << 2);
+ ioaddr->device_addr = ioaddr->cmd_addr + (ATA_REG_DEVICE << 2);
+ ioaddr->status_addr = ioaddr->cmd_addr + (ATA_REG_STATUS << 2);
+ ioaddr->command_addr = ioaddr->cmd_addr + (ATA_REG_CMD << 2);
+}
+
+static int __devinit pata_imx_probe(struct platform_device *pdev)
+{
+ struct ata_host *host;
+ struct ata_port *ap;
+ struct pata_imx_priv *priv;
+ int irq = 0;
+ struct resource *io_res;
+
+ io_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (io_res == NULL)
+ return -EINVAL;
+
+ irq = platform_get_irq(pdev, 0);
+ if (irq <= 0)
+ return -EINVAL;
+
+ priv = devm_kzalloc(&pdev->dev,
+ sizeof(struct pata_imx_priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ priv->clk = clk_get(&pdev->dev, NULL);
+ if (IS_ERR(priv->clk)) {
+ dev_err(&pdev->dev, "Failed to get clock\n");
+ return PTR_ERR(priv->clk);
+ }
+
+ clk_enable(priv->clk);
+
+ host = ata_host_alloc(&pdev->dev, 1);
+ if (!host)
+ goto free_priv;
+
+ host->private_data = priv;
+ ap = host->ports[0];
+
+ ap->ops = &pata_imx_port_ops;
+ ap->pio_mask = ATA_PIO0;
+ ap->flags |= ATA_FLAG_SLAVE_POSS;
+
+ priv->host_regs = devm_ioremap(&pdev->dev, io_res->start,
+ resource_size(io_res));
+ if (!priv->host_regs) {
+ dev_err(&pdev->dev, "failed to map IO/CTL base\n");
+ goto free_priv;
+ }
+
+ ap->ioaddr.cmd_addr = priv->host_regs + PATA_IMX_DRIVE_DATA;
+ ap->ioaddr.ctl_addr = priv->host_regs + PATA_IMX_DRIVE_CONTROL;
+
+ ap->ioaddr.altstatus_addr = ap->ioaddr.ctl_addr;
+
+ pata_imx_setup_port(&ap->ioaddr);
+
+ ata_port_desc(ap, "cmd 0x%llx ctl 0x%llx",
+ (unsigned long long)io_res->start + PATA_IMX_DRIVE_DATA,
+ (unsigned long long)io_res->start + PATA_IMX_DRIVE_CONTROL);
+
+ /* deassert resets */
+ __raw_writel(PATA_IMX_ATA_CTRL_FIFO_RST_B |
+ PATA_IMX_ATA_CTRL_ATA_RST_B,
+ priv->host_regs + PATA_IMX_ATA_CONTROL);
+ /* enable interrupts */
+ __raw_writel(PATA_IMX_ATA_INTR_ATA_INTRQ2,
+ priv->host_regs + PATA_IMX_ATA_INT_EN);
+
+ /* activate */
+ return ata_host_activate(host, irq, ata_sff_interrupt, 0,
+ &pata_imx_sht);
+
+free_priv:
+ clk_disable(priv->clk);
+ clk_put(priv->clk);
+ return -ENOMEM;
+}
+
+static int __devexit pata_imx_remove(struct platform_device *pdev)
+{
+ struct ata_host *host = dev_get_drvdata(&pdev->dev);
+ struct pata_imx_priv *priv = host->private_data;
+
+ ata_host_detach(host);
+
+ __raw_writel(0, priv->host_regs + PATA_IMX_ATA_INT_EN);
+
+ clk_disable(priv->clk);
+ clk_put(priv->clk);
+
+ return 0;
+}
+
+#ifdef CONFIG_PM
+static int pata_imx_suspend(struct device *dev)
+{
+ struct ata_host *host = dev_get_drvdata(dev);
+ struct pata_imx_priv *priv = host->private_data;
+ int ret;
+
+ ret = ata_host_suspend(host, PMSG_SUSPEND);
+ if (!ret) {
+ __raw_writel(0, priv->host_regs + PATA_IMX_ATA_INT_EN);
+ priv->ata_ctl =
+ __raw_readl(priv->host_regs + PATA_IMX_ATA_CONTROL);
+ clk_disable(priv->clk);
+ }
+
+ return ret;
+}
+
+static int pata_imx_resume(struct device *dev)
+{
+ struct ata_host *host = dev_get_drvdata(dev);
+ struct pata_imx_priv *priv = host->private_data;
+
+ clk_enable(priv->clk);
+
+ __raw_writel(priv->ata_ctl, priv->host_regs + PATA_IMX_ATA_CONTROL);
+
+ __raw_writel(PATA_IMX_ATA_INTR_ATA_INTRQ2,
+ priv->host_regs + PATA_IMX_ATA_INT_EN);
+
+ ata_host_resume(host);
+
+ return 0;
+}
+
+static const struct dev_pm_ops pata_imx_pm_ops = {
+ .suspend = pata_imx_suspend,
+ .resume = pata_imx_resume,
+};
+#endif
+
+static struct platform_driver pata_imx_driver = {
+ .probe = pata_imx_probe,
+ .remove = __devexit_p(pata_imx_remove),
+ .driver = {
+ .name = DRV_NAME,
+ .owner = THIS_MODULE,
+#ifdef CONFIG_PM
+ .pm = &pata_imx_pm_ops,
+#endif
+ },
+};
+
+static int __init pata_imx_init(void)
+{
+ return platform_driver_register(&pata_imx_driver);
+}
+
+static void __exit pata_imx_exit(void)
+{
+ platform_driver_unregister(&pata_imx_driver);
+}
+module_init(pata_imx_init);
+module_exit(pata_imx_exit);
+
+MODULE_AUTHOR("Arnaud Patard <arnaud.patard@rtp-net.org>");
+MODULE_DESCRIPTION("low-level driver for iMX PATA");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:" DRV_NAME);
diff --git a/drivers/ata/pata_via.c b/drivers/ata/pata_via.c
index 65e4be6..8e9f504 100644
--- a/drivers/ata/pata_via.c
+++ b/drivers/ata/pata_via.c
@@ -124,6 +124,17 @@
{ NULL }
};
+static const struct dmi_system_id no_atapi_dma_dmi_table[] = {
+ {
+ .ident = "AVERATEC 3200",
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "AVERATEC"),
+ DMI_MATCH(DMI_BOARD_NAME, "3200"),
+ },
+ },
+ { }
+};
+
struct via_port {
u8 cached_device;
};
@@ -355,6 +366,13 @@
mask &= ~ ATA_MASK_UDMA;
}
}
+
+ if (dev->class == ATA_DEV_ATAPI &&
+ dmi_check_system(no_atapi_dma_dmi_table)) {
+ ata_dev_warn(dev, "controller locks up on ATAPI DMA, forcing PIO\n");
+ mask &= ATA_MASK_PIO;
+ }
+
return mask;
}
diff --git a/drivers/ata/sata_dwc_460ex.c b/drivers/ata/sata_dwc_460ex.c
index 0a9a774..5c42374 100644
--- a/drivers/ata/sata_dwc_460ex.c
+++ b/drivers/ata/sata_dwc_460ex.c
@@ -1329,7 +1329,7 @@
dev_err(ap->dev, "%s: dma_alloc_coherent failed\n",
__func__);
err = -ENOMEM;
- goto CLEANUP;
+ goto CLEANUP_ALLOC;
}
}
@@ -1349,15 +1349,13 @@
/* Clear any error bits before libata starts issuing commands */
clear_serror();
ap->private_data = hsdevp;
+ dev_dbg(ap->dev, "%s: done\n", __func__);
+ return 0;
+CLEANUP_ALLOC:
+ kfree(hsdevp);
CLEANUP:
- if (err) {
- sata_dwc_port_stop(ap);
- dev_dbg(ap->dev, "%s: fail\n", __func__);
- } else {
- dev_dbg(ap->dev, "%s: done\n", __func__);
- }
-
+ dev_dbg(ap->dev, "%s: fail. ap->id = %d\n", __func__, ap->print_id);
return err;
}
diff --git a/drivers/ata/sata_sil.c b/drivers/ata/sata_sil.c
index 98c1d78..9dfb40b 100644
--- a/drivers/ata/sata_sil.c
+++ b/drivers/ata/sata_sil.c
@@ -438,7 +438,7 @@
u8 status;
if (unlikely(bmdma2 & SIL_DMA_SATA_IRQ)) {
- u32 serror;
+ u32 serror = 0xffffffff;
/* SIEN doesn't mask SATA IRQs on some 3112s. Those
* controllers continue to assert IRQ as long as
diff --git a/drivers/base/devtmpfs.c b/drivers/base/devtmpfs.c
index b89fffc..33e1bed 100644
--- a/drivers/base/devtmpfs.c
+++ b/drivers/base/devtmpfs.c
@@ -166,7 +166,7 @@
{
char *path;
char *s;
- int err;
+ int err = 0;
/* parent directories do not exist, create them */
path = kstrdup(nodepath, GFP_KERNEL);
diff --git a/drivers/base/power/domain.c b/drivers/base/power/domain.c
index be8714a..1c37457 100644
--- a/drivers/base/power/domain.c
+++ b/drivers/base/power/domain.c
@@ -80,7 +80,6 @@
int pm_genpd_poweron(struct generic_pm_domain *genpd)
{
struct generic_pm_domain *parent = genpd->parent;
- DEFINE_WAIT(wait);
int ret = 0;
start:
@@ -112,7 +111,7 @@
}
if (genpd->power_on) {
- int ret = genpd->power_on(genpd);
+ ret = genpd->power_on(genpd);
if (ret)
goto out;
}
@@ -461,6 +460,21 @@
return 0;
}
+/**
+ * pm_genpd_poweroff_unused - Power off all PM domains with no devices in use.
+ */
+void pm_genpd_poweroff_unused(void)
+{
+ struct generic_pm_domain *genpd;
+
+ mutex_lock(&gpd_list_lock);
+
+ list_for_each_entry(genpd, &gpd_list, gpd_list_node)
+ genpd_queue_power_off_work(genpd);
+
+ mutex_unlock(&gpd_list_lock);
+}
+
#else
static inline void genpd_power_off_work_fn(struct work_struct *work) {}
@@ -1256,18 +1270,3 @@
list_add(&genpd->gpd_list_node, &gpd_list);
mutex_unlock(&gpd_list_lock);
}
-
-/**
- * pm_genpd_poweroff_unused - Power off all PM domains with no devices in use.
- */
-void pm_genpd_poweroff_unused(void)
-{
- struct generic_pm_domain *genpd;
-
- mutex_lock(&gpd_list_lock);
-
- list_for_each_entry(genpd, &gpd_list, gpd_list_node)
- genpd_queue_power_off_work(genpd);
-
- mutex_unlock(&gpd_list_lock);
-}
diff --git a/drivers/base/power/runtime.c b/drivers/base/power/runtime.c
index 8dc247c..acb3f83 100644
--- a/drivers/base/power/runtime.c
+++ b/drivers/base/power/runtime.c
@@ -226,11 +226,17 @@
callback = NULL;
if (callback) {
- spin_unlock_irq(&dev->power.lock);
+ if (dev->power.irq_safe)
+ spin_unlock(&dev->power.lock);
+ else
+ spin_unlock_irq(&dev->power.lock);
callback(dev);
- spin_lock_irq(&dev->power.lock);
+ if (dev->power.irq_safe)
+ spin_lock(&dev->power.lock);
+ else
+ spin_lock_irq(&dev->power.lock);
}
dev->power.idle_notification = false;
diff --git a/drivers/base/regmap/regmap-i2c.c b/drivers/base/regmap/regmap-i2c.c
index c2231ff..c4f7a45 100644
--- a/drivers/base/regmap/regmap-i2c.c
+++ b/drivers/base/regmap/regmap-i2c.c
@@ -113,3 +113,4 @@
}
EXPORT_SYMBOL_GPL(regmap_init_i2c);
+MODULE_LICENSE("GPL");
diff --git a/drivers/base/regmap/regmap-spi.c b/drivers/base/regmap/regmap-spi.c
index 4deba06..f839694 100644
--- a/drivers/base/regmap/regmap-spi.c
+++ b/drivers/base/regmap/regmap-spi.c
@@ -13,6 +13,7 @@
#include <linux/regmap.h>
#include <linux/spi/spi.h>
#include <linux/init.h>
+#include <linux/module.h>
static int regmap_spi_write(struct device *dev, const void *data, size_t count)
{
@@ -70,3 +71,5 @@
return regmap_init(&spi->dev, ®map_spi, config);
}
EXPORT_SYMBOL_GPL(regmap_init_spi);
+
+MODULE_LICENSE("GPL");
diff --git a/drivers/base/regmap/regmap.c b/drivers/base/regmap/regmap.c
index cf3565c..0eef4da 100644
--- a/drivers/base/regmap/regmap.c
+++ b/drivers/base/regmap/regmap.c
@@ -317,7 +317,7 @@
u8[0] |= map->bus->read_flag_mask;
ret = map->bus->read(map->dev, map->work_buf, map->format.reg_bytes,
- val, map->format.val_bytes);
+ val, val_len);
if (ret != 0)
return ret;
diff --git a/drivers/char/Kconfig b/drivers/char/Kconfig
index 49502bc..423fd56 100644
--- a/drivers/char/Kconfig
+++ b/drivers/char/Kconfig
@@ -616,5 +616,16 @@
Enables userspace clients to read and write to some packet SMD
ports via device interface for MSM chipset.
+config TILE_SROM
+ bool "Character-device access via hypervisor to the Tilera SPI ROM"
+ depends on TILE
+ default y
+ ---help---
+ This device provides character-level read-write access
+ to the SROM, typically via the "0", "1", and "2" devices
+ in /dev/srom/. The Tilera hypervisor makes the flash
+ device appear much like a simple EEPROM, and knows
+ how to partition a single ROM for multiple purposes.
+
endmenu
diff --git a/drivers/char/Makefile b/drivers/char/Makefile
index 7a00672..32762ba 100644
--- a/drivers/char/Makefile
+++ b/drivers/char/Makefile
@@ -63,3 +63,5 @@
obj-$(CONFIG_JS_RTC) += js-rtc.o
js-rtc-y = rtc.o
+
+obj-$(CONFIG_TILE_SROM) += tile-srom.o
diff --git a/drivers/char/ramoops.c b/drivers/char/ramoops.c
index fca0c51..810aff9 100644
--- a/drivers/char/ramoops.c
+++ b/drivers/char/ramoops.c
@@ -147,6 +147,14 @@
cxt->phys_addr = pdata->mem_address;
cxt->record_size = pdata->record_size;
cxt->dump_oops = pdata->dump_oops;
+ /*
+ * Update the module parameter variables as well so they are visible
+ * through /sys/module/ramoops/parameters/
+ */
+ mem_size = pdata->mem_size;
+ mem_address = pdata->mem_address;
+ record_size = pdata->record_size;
+ dump_oops = pdata->dump_oops;
if (!request_mem_region(cxt->phys_addr, cxt->size, "ramoops")) {
pr_err("request mem region failed\n");
diff --git a/drivers/char/random.c b/drivers/char/random.c
index 7292819..c35a785 100644
--- a/drivers/char/random.c
+++ b/drivers/char/random.c
@@ -1300,345 +1300,14 @@
};
#endif /* CONFIG_SYSCTL */
-/********************************************************************
- *
- * Random functions for networking
- *
- ********************************************************************/
+static u32 random_int_secret[MD5_MESSAGE_BYTES / 4] ____cacheline_aligned;
-/*
- * TCP initial sequence number picking. This uses the random number
- * generator to pick an initial secret value. This value is hashed
- * along with the TCP endpoint information to provide a unique
- * starting point for each pair of TCP endpoints. This defeats
- * attacks which rely on guessing the initial TCP sequence number.
- * This algorithm was suggested by Steve Bellovin.
- *
- * Using a very strong hash was taking an appreciable amount of the total
- * TCP connection establishment time, so this is a weaker hash,
- * compensated for by changing the secret periodically.
- */
-
-/* F, G and H are basic MD4 functions: selection, majority, parity */
-#define F(x, y, z) ((z) ^ ((x) & ((y) ^ (z))))
-#define G(x, y, z) (((x) & (y)) + (((x) ^ (y)) & (z)))
-#define H(x, y, z) ((x) ^ (y) ^ (z))
-
-/*
- * The generic round function. The application is so specific that
- * we don't bother protecting all the arguments with parens, as is generally
- * good macro practice, in favor of extra legibility.
- * Rotation is separate from addition to prevent recomputation
- */
-#define ROUND(f, a, b, c, d, x, s) \
- (a += f(b, c, d) + x, a = (a << s) | (a >> (32 - s)))
-#define K1 0
-#define K2 013240474631UL
-#define K3 015666365641UL
-
-#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
-
-static __u32 twothirdsMD4Transform(__u32 const buf[4], __u32 const in[12])
+static int __init random_int_secret_init(void)
{
- __u32 a = buf[0], b = buf[1], c = buf[2], d = buf[3];
-
- /* Round 1 */
- ROUND(F, a, b, c, d, in[ 0] + K1, 3);
- ROUND(F, d, a, b, c, in[ 1] + K1, 7);
- ROUND(F, c, d, a, b, in[ 2] + K1, 11);
- ROUND(F, b, c, d, a, in[ 3] + K1, 19);
- ROUND(F, a, b, c, d, in[ 4] + K1, 3);
- ROUND(F, d, a, b, c, in[ 5] + K1, 7);
- ROUND(F, c, d, a, b, in[ 6] + K1, 11);
- ROUND(F, b, c, d, a, in[ 7] + K1, 19);
- ROUND(F, a, b, c, d, in[ 8] + K1, 3);
- ROUND(F, d, a, b, c, in[ 9] + K1, 7);
- ROUND(F, c, d, a, b, in[10] + K1, 11);
- ROUND(F, b, c, d, a, in[11] + K1, 19);
-
- /* Round 2 */
- ROUND(G, a, b, c, d, in[ 1] + K2, 3);
- ROUND(G, d, a, b, c, in[ 3] + K2, 5);
- ROUND(G, c, d, a, b, in[ 5] + K2, 9);
- ROUND(G, b, c, d, a, in[ 7] + K2, 13);
- ROUND(G, a, b, c, d, in[ 9] + K2, 3);
- ROUND(G, d, a, b, c, in[11] + K2, 5);
- ROUND(G, c, d, a, b, in[ 0] + K2, 9);
- ROUND(G, b, c, d, a, in[ 2] + K2, 13);
- ROUND(G, a, b, c, d, in[ 4] + K2, 3);
- ROUND(G, d, a, b, c, in[ 6] + K2, 5);
- ROUND(G, c, d, a, b, in[ 8] + K2, 9);
- ROUND(G, b, c, d, a, in[10] + K2, 13);
-
- /* Round 3 */
- ROUND(H, a, b, c, d, in[ 3] + K3, 3);
- ROUND(H, d, a, b, c, in[ 7] + K3, 9);
- ROUND(H, c, d, a, b, in[11] + K3, 11);
- ROUND(H, b, c, d, a, in[ 2] + K3, 15);
- ROUND(H, a, b, c, d, in[ 6] + K3, 3);
- ROUND(H, d, a, b, c, in[10] + K3, 9);
- ROUND(H, c, d, a, b, in[ 1] + K3, 11);
- ROUND(H, b, c, d, a, in[ 5] + K3, 15);
- ROUND(H, a, b, c, d, in[ 9] + K3, 3);
- ROUND(H, d, a, b, c, in[ 0] + K3, 9);
- ROUND(H, c, d, a, b, in[ 4] + K3, 11);
- ROUND(H, b, c, d, a, in[ 8] + K3, 15);
-
- return buf[1] + b; /* "most hashed" word */
- /* Alternative: return sum of all words? */
-}
-#endif
-
-#undef ROUND
-#undef F
-#undef G
-#undef H
-#undef K1
-#undef K2
-#undef K3
-
-/* This should not be decreased so low that ISNs wrap too fast. */
-#define REKEY_INTERVAL (300 * HZ)
-/*
- * Bit layout of the tcp sequence numbers (before adding current time):
- * bit 24-31: increased after every key exchange
- * bit 0-23: hash(source,dest)
- *
- * The implementation is similar to the algorithm described
- * in the Appendix of RFC 1185, except that
- * - it uses a 1 MHz clock instead of a 250 kHz clock
- * - it performs a rekey every 5 minutes, which is equivalent
- * to a (source,dest) tulple dependent forward jump of the
- * clock by 0..2^(HASH_BITS+1)
- *
- * Thus the average ISN wraparound time is 68 minutes instead of
- * 4.55 hours.
- *
- * SMP cleanup and lock avoidance with poor man's RCU.
- * Manfred Spraul <manfred@colorfullife.com>
- *
- */
-#define COUNT_BITS 8
-#define COUNT_MASK ((1 << COUNT_BITS) - 1)
-#define HASH_BITS 24
-#define HASH_MASK ((1 << HASH_BITS) - 1)
-
-static struct keydata {
- __u32 count; /* already shifted to the final position */
- __u32 secret[12];
-} ____cacheline_aligned ip_keydata[2];
-
-static unsigned int ip_cnt;
-
-static void rekey_seq_generator(struct work_struct *work);
-
-static DECLARE_DELAYED_WORK(rekey_work, rekey_seq_generator);
-
-/*
- * Lock avoidance:
- * The ISN generation runs lockless - it's just a hash over random data.
- * State changes happen every 5 minutes when the random key is replaced.
- * Synchronization is performed by having two copies of the hash function
- * state and rekey_seq_generator always updates the inactive copy.
- * The copy is then activated by updating ip_cnt.
- * The implementation breaks down if someone blocks the thread
- * that processes SYN requests for more than 5 minutes. Should never
- * happen, and even if that happens only a not perfectly compliant
- * ISN is generated, nothing fatal.
- */
-static void rekey_seq_generator(struct work_struct *work)
-{
- struct keydata *keyptr = &ip_keydata[1 ^ (ip_cnt & 1)];
-
- get_random_bytes(keyptr->secret, sizeof(keyptr->secret));
- keyptr->count = (ip_cnt & COUNT_MASK) << HASH_BITS;
- smp_wmb();
- ip_cnt++;
- schedule_delayed_work(&rekey_work,
- round_jiffies_relative(REKEY_INTERVAL));
-}
-
-static inline struct keydata *get_keyptr(void)
-{
- struct keydata *keyptr = &ip_keydata[ip_cnt & 1];
-
- smp_rmb();
-
- return keyptr;
-}
-
-static __init int seqgen_init(void)
-{
- rekey_seq_generator(NULL);
+ get_random_bytes(random_int_secret, sizeof(random_int_secret));
return 0;
}
-late_initcall(seqgen_init);
-
-#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
-__u32 secure_tcpv6_sequence_number(__be32 *saddr, __be32 *daddr,
- __be16 sport, __be16 dport)
-{
- __u32 seq;
- __u32 hash[12];
- struct keydata *keyptr = get_keyptr();
-
- /* The procedure is the same as for IPv4, but addresses are longer.
- * Thus we must use twothirdsMD4Transform.
- */
-
- memcpy(hash, saddr, 16);
- hash[4] = ((__force u16)sport << 16) + (__force u16)dport;
- memcpy(&hash[5], keyptr->secret, sizeof(__u32) * 7);
-
- seq = twothirdsMD4Transform((const __u32 *)daddr, hash) & HASH_MASK;
- seq += keyptr->count;
-
- seq += ktime_to_ns(ktime_get_real());
-
- return seq;
-}
-EXPORT_SYMBOL(secure_tcpv6_sequence_number);
-#endif
-
-/* The code below is shamelessly stolen from secure_tcp_sequence_number().
- * All blames to Andrey V. Savochkin <saw@msu.ru>.
- */
-__u32 secure_ip_id(__be32 daddr)
-{
- struct keydata *keyptr;
- __u32 hash[4];
-
- keyptr = get_keyptr();
-
- /*
- * Pick a unique starting offset for each IP destination.
- * The dest ip address is placed in the starting vector,
- * which is then hashed with random data.
- */
- hash[0] = (__force __u32)daddr;
- hash[1] = keyptr->secret[9];
- hash[2] = keyptr->secret[10];
- hash[3] = keyptr->secret[11];
-
- return half_md4_transform(hash, keyptr->secret);
-}
-
-__u32 secure_ipv6_id(const __be32 daddr[4])
-{
- const struct keydata *keyptr;
- __u32 hash[4];
-
- keyptr = get_keyptr();
-
- hash[0] = (__force __u32)daddr[0];
- hash[1] = (__force __u32)daddr[1];
- hash[2] = (__force __u32)daddr[2];
- hash[3] = (__force __u32)daddr[3];
-
- return half_md4_transform(hash, keyptr->secret);
-}
-
-#ifdef CONFIG_INET
-
-__u32 secure_tcp_sequence_number(__be32 saddr, __be32 daddr,
- __be16 sport, __be16 dport)
-{
- __u32 seq;
- __u32 hash[4];
- struct keydata *keyptr = get_keyptr();
-
- /*
- * Pick a unique starting offset for each TCP connection endpoints
- * (saddr, daddr, sport, dport).
- * Note that the words are placed into the starting vector, which is
- * then mixed with a partial MD4 over random data.
- */
- hash[0] = (__force u32)saddr;
- hash[1] = (__force u32)daddr;
- hash[2] = ((__force u16)sport << 16) + (__force u16)dport;
- hash[3] = keyptr->secret[11];
-
- seq = half_md4_transform(hash, keyptr->secret) & HASH_MASK;
- seq += keyptr->count;
- /*
- * As close as possible to RFC 793, which
- * suggests using a 250 kHz clock.
- * Further reading shows this assumes 2 Mb/s networks.
- * For 10 Mb/s Ethernet, a 1 MHz clock is appropriate.
- * For 10 Gb/s Ethernet, a 1 GHz clock should be ok, but
- * we also need to limit the resolution so that the u32 seq
- * overlaps less than one time per MSL (2 minutes).
- * Choosing a clock of 64 ns period is OK. (period of 274 s)
- */
- seq += ktime_to_ns(ktime_get_real()) >> 6;
-
- return seq;
-}
-
-/* Generate secure starting point for ephemeral IPV4 transport port search */
-u32 secure_ipv4_port_ephemeral(__be32 saddr, __be32 daddr, __be16 dport)
-{
- struct keydata *keyptr = get_keyptr();
- u32 hash[4];
-
- /*
- * Pick a unique starting offset for each ephemeral port search
- * (saddr, daddr, dport) and 48bits of random data.
- */
- hash[0] = (__force u32)saddr;
- hash[1] = (__force u32)daddr;
- hash[2] = (__force u32)dport ^ keyptr->secret[10];
- hash[3] = keyptr->secret[11];
-
- return half_md4_transform(hash, keyptr->secret);
-}
-EXPORT_SYMBOL_GPL(secure_ipv4_port_ephemeral);
-
-#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
-u32 secure_ipv6_port_ephemeral(const __be32 *saddr, const __be32 *daddr,
- __be16 dport)
-{
- struct keydata *keyptr = get_keyptr();
- u32 hash[12];
-
- memcpy(hash, saddr, 16);
- hash[4] = (__force u32)dport;
- memcpy(&hash[5], keyptr->secret, sizeof(__u32) * 7);
-
- return twothirdsMD4Transform((const __u32 *)daddr, hash);
-}
-#endif
-
-#if defined(CONFIG_IP_DCCP) || defined(CONFIG_IP_DCCP_MODULE)
-/* Similar to secure_tcp_sequence_number but generate a 48 bit value
- * bit's 32-47 increase every key exchange
- * 0-31 hash(source, dest)
- */
-u64 secure_dccp_sequence_number(__be32 saddr, __be32 daddr,
- __be16 sport, __be16 dport)
-{
- u64 seq;
- __u32 hash[4];
- struct keydata *keyptr = get_keyptr();
-
- hash[0] = (__force u32)saddr;
- hash[1] = (__force u32)daddr;
- hash[2] = ((__force u16)sport << 16) + (__force u16)dport;
- hash[3] = keyptr->secret[11];
-
- seq = half_md4_transform(hash, keyptr->secret);
- seq |= ((u64)keyptr->count) << (32 - HASH_BITS);
-
- seq += ktime_to_ns(ktime_get_real());
- seq &= (1ull << 48) - 1;
-
- return seq;
-}
-EXPORT_SYMBOL(secure_dccp_sequence_number);
-#endif
-
-#endif /* CONFIG_INET */
-
+late_initcall(random_int_secret_init);
/*
* Get a random word for internal kernel use only. Similar to urandom but
@@ -1646,17 +1315,15 @@
* value is not cryptographically secure but for several uses the cost of
* depleting entropy is too high
*/
-DEFINE_PER_CPU(__u32 [4], get_random_int_hash);
+DEFINE_PER_CPU(__u32 [MD5_DIGEST_WORDS], get_random_int_hash);
unsigned int get_random_int(void)
{
- struct keydata *keyptr;
__u32 *hash = get_cpu_var(get_random_int_hash);
- int ret;
+ unsigned int ret;
- keyptr = get_keyptr();
hash[0] += current->pid + jiffies + get_cycles();
-
- ret = half_md4_transform(hash, keyptr->secret);
+ md5_transform(hash, random_int_secret);
+ ret = hash[0];
put_cpu_var(get_random_int_hash);
return ret;
diff --git a/drivers/char/tile-srom.c b/drivers/char/tile-srom.c
new file mode 100644
index 0000000..cf3ee00
--- /dev/null
+++ b/drivers/char/tile-srom.c
@@ -0,0 +1,481 @@
+/*
+ * Copyright 2011 Tilera Corporation. All Rights Reserved.
+ *
+ * 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 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, GOOD TITLE or
+ * NON INFRINGEMENT. See the GNU General Public License for
+ * more details.
+ *
+ * SPI Flash ROM driver
+ *
+ * This source code is derived from code provided in "Linux Device
+ * Drivers, Third Edition", by Jonathan Corbet, Alessandro Rubini, and
+ * Greg Kroah-Hartman, published by O'Reilly Media, Inc.
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/kernel.h> /* printk() */
+#include <linux/slab.h> /* kmalloc() */
+#include <linux/fs.h> /* everything... */
+#include <linux/errno.h> /* error codes */
+#include <linux/types.h> /* size_t */
+#include <linux/proc_fs.h>
+#include <linux/fcntl.h> /* O_ACCMODE */
+#include <linux/aio.h>
+#include <linux/pagemap.h>
+#include <linux/hugetlb.h>
+#include <linux/uaccess.h>
+#include <linux/platform_device.h>
+#include <hv/hypervisor.h>
+#include <linux/ioctl.h>
+#include <linux/cdev.h>
+#include <linux/delay.h>
+#include <hv/drv_srom_intf.h>
+
+/*
+ * Size of our hypervisor I/O requests. We break up large transfers
+ * so that we don't spend large uninterrupted spans of time in the
+ * hypervisor. Erasing an SROM sector takes a significant fraction of
+ * a second, so if we allowed the user to, say, do one I/O to write the
+ * entire ROM, we'd get soft lockup timeouts, or worse.
+ */
+#define SROM_CHUNK_SIZE ((size_t)4096)
+
+/*
+ * When hypervisor is busy (e.g. erasing), poll the status periodically.
+ */
+
+/*
+ * Interval to poll the state in msec
+ */
+#define SROM_WAIT_TRY_INTERVAL 20
+
+/*
+ * Maximum times to poll the state
+ */
+#define SROM_MAX_WAIT_TRY_TIMES 1000
+
+struct srom_dev {
+ int hv_devhdl; /* Handle for hypervisor device */
+ u32 total_size; /* Size of this device */
+ u32 sector_size; /* Size of a sector */
+ u32 page_size; /* Size of a page */
+ struct mutex lock; /* Allow only one accessor at a time */
+};
+
+static int srom_major; /* Dynamic major by default */
+module_param(srom_major, int, 0);
+MODULE_AUTHOR("Tilera Corporation");
+MODULE_LICENSE("GPL");
+
+static int srom_devs; /* Number of SROM partitions */
+static struct cdev srom_cdev;
+static struct class *srom_class;
+static struct srom_dev *srom_devices;
+
+/*
+ * Handle calling the hypervisor and managing EAGAIN/EBUSY.
+ */
+
+static ssize_t _srom_read(int hv_devhdl, void *buf,
+ loff_t off, size_t count)
+{
+ int retval, retries = SROM_MAX_WAIT_TRY_TIMES;
+ for (;;) {
+ retval = hv_dev_pread(hv_devhdl, 0, (HV_VirtAddr)buf,
+ count, off);
+ if (retval >= 0)
+ return retval;
+ if (retval == HV_EAGAIN)
+ continue;
+ if (retval == HV_EBUSY && --retries > 0) {
+ msleep(SROM_WAIT_TRY_INTERVAL);
+ continue;
+ }
+ pr_err("_srom_read: error %d\n", retval);
+ return -EIO;
+ }
+}
+
+static ssize_t _srom_write(int hv_devhdl, const void *buf,
+ loff_t off, size_t count)
+{
+ int retval, retries = SROM_MAX_WAIT_TRY_TIMES;
+ for (;;) {
+ retval = hv_dev_pwrite(hv_devhdl, 0, (HV_VirtAddr)buf,
+ count, off);
+ if (retval >= 0)
+ return retval;
+ if (retval == HV_EAGAIN)
+ continue;
+ if (retval == HV_EBUSY && --retries > 0) {
+ msleep(SROM_WAIT_TRY_INTERVAL);
+ continue;
+ }
+ pr_err("_srom_write: error %d\n", retval);
+ return -EIO;
+ }
+}
+
+/**
+ * srom_open() - Device open routine.
+ * @inode: Inode for this device.
+ * @filp: File for this specific open of the device.
+ *
+ * Returns zero, or an error code.
+ */
+static int srom_open(struct inode *inode, struct file *filp)
+{
+ filp->private_data = &srom_devices[iminor(inode)];
+ return 0;
+}
+
+
+/**
+ * srom_release() - Device release routine.
+ * @inode: Inode for this device.
+ * @filp: File for this specific open of the device.
+ *
+ * Returns zero, or an error code.
+ */
+static int srom_release(struct inode *inode, struct file *filp)
+{
+ struct srom_dev *srom = filp->private_data;
+ char dummy;
+
+ /* Make sure we've flushed anything written to the ROM. */
+ mutex_lock(&srom->lock);
+ if (srom->hv_devhdl >= 0)
+ _srom_write(srom->hv_devhdl, &dummy, SROM_FLUSH_OFF, 1);
+ mutex_unlock(&srom->lock);
+
+ filp->private_data = NULL;
+
+ return 0;
+}
+
+
+/**
+ * srom_read() - Read data from the device.
+ * @filp: File for this specific open of the device.
+ * @buf: User's data buffer.
+ * @count: Number of bytes requested.
+ * @f_pos: File position.
+ *
+ * Returns number of bytes read, or an error code.
+ */
+static ssize_t srom_read(struct file *filp, char __user *buf,
+ size_t count, loff_t *f_pos)
+{
+ int retval = 0;
+ void *kernbuf;
+ struct srom_dev *srom = filp->private_data;
+
+ kernbuf = kmalloc(SROM_CHUNK_SIZE, GFP_KERNEL);
+ if (!kernbuf)
+ return -ENOMEM;
+
+ if (mutex_lock_interruptible(&srom->lock)) {
+ retval = -ERESTARTSYS;
+ kfree(kernbuf);
+ return retval;
+ }
+
+ while (count) {
+ int hv_retval;
+ int bytes_this_pass = min(count, SROM_CHUNK_SIZE);
+
+ hv_retval = _srom_read(srom->hv_devhdl, kernbuf,
+ *f_pos, bytes_this_pass);
+ if (hv_retval > 0) {
+ if (copy_to_user(buf, kernbuf, hv_retval) != 0) {
+ retval = -EFAULT;
+ break;
+ }
+ } else if (hv_retval <= 0) {
+ if (retval == 0)
+ retval = hv_retval;
+ break;
+ }
+
+ retval += hv_retval;
+ *f_pos += hv_retval;
+ buf += hv_retval;
+ count -= hv_retval;
+ }
+
+ mutex_unlock(&srom->lock);
+ kfree(kernbuf);
+
+ return retval;
+}
+
+/**
+ * srom_write() - Write data to the device.
+ * @filp: File for this specific open of the device.
+ * @buf: User's data buffer.
+ * @count: Number of bytes requested.
+ * @f_pos: File position.
+ *
+ * Returns number of bytes written, or an error code.
+ */
+static ssize_t srom_write(struct file *filp, const char __user *buf,
+ size_t count, loff_t *f_pos)
+{
+ int retval = 0;
+ void *kernbuf;
+ struct srom_dev *srom = filp->private_data;
+
+ kernbuf = kmalloc(SROM_CHUNK_SIZE, GFP_KERNEL);
+ if (!kernbuf)
+ return -ENOMEM;
+
+ if (mutex_lock_interruptible(&srom->lock)) {
+ retval = -ERESTARTSYS;
+ kfree(kernbuf);
+ return retval;
+ }
+
+ while (count) {
+ int hv_retval;
+ int bytes_this_pass = min(count, SROM_CHUNK_SIZE);
+
+ if (copy_from_user(kernbuf, buf, bytes_this_pass) != 0) {
+ retval = -EFAULT;
+ break;
+ }
+
+ hv_retval = _srom_write(srom->hv_devhdl, kernbuf,
+ *f_pos, bytes_this_pass);
+ if (hv_retval <= 0) {
+ if (retval == 0)
+ retval = hv_retval;
+ break;
+ }
+
+ retval += hv_retval;
+ *f_pos += hv_retval;
+ buf += hv_retval;
+ count -= hv_retval;
+ }
+
+ mutex_unlock(&srom->lock);
+ kfree(kernbuf);
+
+ return retval;
+}
+
+/* Provide our own implementation so we can use srom->total_size. */
+loff_t srom_llseek(struct file *filp, loff_t offset, int origin)
+{
+ struct srom_dev *srom = filp->private_data;
+
+ if (mutex_lock_interruptible(&srom->lock))
+ return -ERESTARTSYS;
+
+ switch (origin) {
+ case SEEK_END:
+ offset += srom->total_size;
+ break;
+ case SEEK_CUR:
+ offset += filp->f_pos;
+ break;
+ }
+
+ if (offset < 0 || offset > srom->total_size) {
+ offset = -EINVAL;
+ } else {
+ filp->f_pos = offset;
+ filp->f_version = 0;
+ }
+
+ mutex_unlock(&srom->lock);
+
+ return offset;
+}
+
+static ssize_t total_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct srom_dev *srom = dev_get_drvdata(dev);
+ return sprintf(buf, "%u\n", srom->total_size);
+}
+
+static ssize_t sector_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct srom_dev *srom = dev_get_drvdata(dev);
+ return sprintf(buf, "%u\n", srom->sector_size);
+}
+
+static ssize_t page_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct srom_dev *srom = dev_get_drvdata(dev);
+ return sprintf(buf, "%u\n", srom->page_size);
+}
+
+static struct device_attribute srom_dev_attrs[] = {
+ __ATTR(total_size, S_IRUGO, total_show, NULL),
+ __ATTR(sector_size, S_IRUGO, sector_show, NULL),
+ __ATTR(page_size, S_IRUGO, page_show, NULL),
+ __ATTR_NULL
+};
+
+static char *srom_devnode(struct device *dev, mode_t *mode)
+{
+ *mode = S_IRUGO | S_IWUSR;
+ return kasprintf(GFP_KERNEL, "srom/%s", dev_name(dev));
+}
+
+/*
+ * The fops
+ */
+static const struct file_operations srom_fops = {
+ .owner = THIS_MODULE,
+ .llseek = srom_llseek,
+ .read = srom_read,
+ .write = srom_write,
+ .open = srom_open,
+ .release = srom_release,
+};
+
+/**
+ * srom_setup_minor() - Initialize per-minor information.
+ * @srom: Per-device SROM state.
+ * @index: Device to set up.
+ */
+static int srom_setup_minor(struct srom_dev *srom, int index)
+{
+ struct device *dev;
+ int devhdl = srom->hv_devhdl;
+
+ mutex_init(&srom->lock);
+
+ if (_srom_read(devhdl, &srom->total_size,
+ SROM_TOTAL_SIZE_OFF, sizeof(srom->total_size)) < 0)
+ return -EIO;
+ if (_srom_read(devhdl, &srom->sector_size,
+ SROM_SECTOR_SIZE_OFF, sizeof(srom->sector_size)) < 0)
+ return -EIO;
+ if (_srom_read(devhdl, &srom->page_size,
+ SROM_PAGE_SIZE_OFF, sizeof(srom->page_size)) < 0)
+ return -EIO;
+
+ dev = device_create(srom_class, &platform_bus,
+ MKDEV(srom_major, index), srom, "%d", index);
+ return IS_ERR(dev) ? PTR_ERR(dev) : 0;
+}
+
+/** srom_init() - Initialize the driver's module. */
+static int srom_init(void)
+{
+ int result, i;
+ dev_t dev = MKDEV(srom_major, 0);
+
+ /*
+ * Start with a plausible number of partitions; the krealloc() call
+ * below will yield about log(srom_devs) additional allocations.
+ */
+ srom_devices = kzalloc(4 * sizeof(struct srom_dev), GFP_KERNEL);
+
+ /* Discover the number of srom partitions. */
+ for (i = 0; ; i++) {
+ int devhdl;
+ char buf[20];
+ struct srom_dev *new_srom_devices =
+ krealloc(srom_devices, (i+1) * sizeof(struct srom_dev),
+ GFP_KERNEL | __GFP_ZERO);
+ if (!new_srom_devices) {
+ result = -ENOMEM;
+ goto fail_mem;
+ }
+ srom_devices = new_srom_devices;
+ sprintf(buf, "srom/0/%d", i);
+ devhdl = hv_dev_open((HV_VirtAddr)buf, 0);
+ if (devhdl < 0) {
+ if (devhdl != HV_ENODEV)
+ pr_notice("srom/%d: hv_dev_open failed: %d.\n",
+ i, devhdl);
+ break;
+ }
+ srom_devices[i].hv_devhdl = devhdl;
+ }
+ srom_devs = i;
+
+ /* Bail out early if we have no partitions at all. */
+ if (srom_devs == 0) {
+ result = -ENODEV;
+ goto fail_mem;
+ }
+
+ /* Register our major, and accept a dynamic number. */
+ if (srom_major)
+ result = register_chrdev_region(dev, srom_devs, "srom");
+ else {
+ result = alloc_chrdev_region(&dev, 0, srom_devs, "srom");
+ srom_major = MAJOR(dev);
+ }
+ if (result < 0)
+ goto fail_mem;
+
+ /* Register a character device. */
+ cdev_init(&srom_cdev, &srom_fops);
+ srom_cdev.owner = THIS_MODULE;
+ srom_cdev.ops = &srom_fops;
+ result = cdev_add(&srom_cdev, dev, srom_devs);
+ if (result < 0)
+ goto fail_chrdev;
+
+ /* Create a sysfs class. */
+ srom_class = class_create(THIS_MODULE, "srom");
+ if (IS_ERR(srom_class)) {
+ result = PTR_ERR(srom_class);
+ goto fail_cdev;
+ }
+ srom_class->dev_attrs = srom_dev_attrs;
+ srom_class->devnode = srom_devnode;
+
+ /* Do per-partition initialization */
+ for (i = 0; i < srom_devs; i++) {
+ result = srom_setup_minor(srom_devices + i, i);
+ if (result < 0)
+ goto fail_class;
+ }
+
+ return 0;
+
+fail_class:
+ for (i = 0; i < srom_devs; i++)
+ device_destroy(srom_class, MKDEV(srom_major, i));
+ class_destroy(srom_class);
+fail_cdev:
+ cdev_del(&srom_cdev);
+fail_chrdev:
+ unregister_chrdev_region(dev, srom_devs);
+fail_mem:
+ kfree(srom_devices);
+ return result;
+}
+
+/** srom_cleanup() - Clean up the driver's module. */
+static void srom_cleanup(void)
+{
+ int i;
+ for (i = 0; i < srom_devs; i++)
+ device_destroy(srom_class, MKDEV(srom_major, i));
+ class_destroy(srom_class);
+ cdev_del(&srom_cdev);
+ unregister_chrdev_region(MKDEV(srom_major, 0), srom_devs);
+ kfree(srom_devices);
+}
+
+module_init(srom_init);
+module_exit(srom_cleanup);
diff --git a/drivers/char/tpm/tpm_tis.c b/drivers/char/tpm/tpm_tis.c
index 7fc2f10..3f4051a 100644
--- a/drivers/char/tpm/tpm_tis.c
+++ b/drivers/char/tpm/tpm_tis.c
@@ -80,7 +80,7 @@
static LIST_HEAD(tis_chips);
static DEFINE_SPINLOCK(tis_lock);
-#ifdef CONFIG_PNP
+#if defined(CONFIG_PNP) && defined(CONFIG_ACPI)
static int is_itpm(struct pnp_dev *dev)
{
struct acpi_device *acpi = pnp_acpi_device(dev);
@@ -93,6 +93,11 @@
return 0;
}
+#else
+static inline int is_itpm(struct pnp_dev *dev)
+{
+ return 0;
+}
#endif
static int check_locality(struct tpm_chip *chip, int l)
diff --git a/drivers/connector/cn_proc.c b/drivers/connector/cn_proc.c
index 3ee1fdb..e55814b 100644
--- a/drivers/connector/cn_proc.c
+++ b/drivers/connector/cn_proc.c
@@ -57,6 +57,7 @@
struct proc_event *ev;
__u8 buffer[CN_PROC_MSG_SIZE];
struct timespec ts;
+ struct task_struct *parent;
if (atomic_read(&proc_event_num_listeners) < 1)
return;
@@ -67,8 +68,11 @@
ktime_get_ts(&ts); /* get high res monotonic timestamp */
put_unaligned(timespec_to_ns(&ts), (__u64 *)&ev->timestamp_ns);
ev->what = PROC_EVENT_FORK;
- ev->event_data.fork.parent_pid = task->real_parent->pid;
- ev->event_data.fork.parent_tgid = task->real_parent->tgid;
+ rcu_read_lock();
+ parent = rcu_dereference(task->real_parent);
+ ev->event_data.fork.parent_pid = parent->pid;
+ ev->event_data.fork.parent_tgid = parent->tgid;
+ rcu_read_unlock();
ev->event_data.fork.child_pid = task->pid;
ev->event_data.fork.child_tgid = task->tgid;
diff --git a/drivers/cpuidle/cpuidle.c b/drivers/cpuidle/cpuidle.c
index bf50924..d4c5423 100644
--- a/drivers/cpuidle/cpuidle.c
+++ b/drivers/cpuidle/cpuidle.c
@@ -25,9 +25,19 @@
DEFINE_MUTEX(cpuidle_lock);
LIST_HEAD(cpuidle_detected_devices);
-static void (*pm_idle_old)(void);
static int enabled_devices;
+static int off __read_mostly;
+static int initialized __read_mostly;
+
+int cpuidle_disabled(void)
+{
+ return off;
+}
+void disable_cpuidle(void)
+{
+ off = 1;
+}
#if defined(CONFIG_ARCH_HAS_CPU_IDLE_WAIT)
static void cpuidle_kick_cpus(void)
@@ -46,25 +56,23 @@
* cpuidle_idle_call - the main idle loop
*
* NOTE: no locks or semaphores should be used here
+ * return non-zero on failure
*/
-static void cpuidle_idle_call(void)
+int cpuidle_idle_call(void)
{
struct cpuidle_device *dev = __this_cpu_read(cpuidle_devices);
struct cpuidle_state *target_state;
int next_state;
+ if (off)
+ return -ENODEV;
+
+ if (!initialized)
+ return -ENODEV;
+
/* check if the device is ready */
- if (!dev || !dev->enabled) {
- if (pm_idle_old)
- pm_idle_old();
- else
-#if defined(CONFIG_ARCH_HAS_DEFAULT_IDLE)
- default_idle();
-#else
- local_irq_enable();
-#endif
- return;
- }
+ if (!dev || !dev->enabled)
+ return -EBUSY;
#if 0
/* shows regressions, re-enable for 2.6.29 */
@@ -89,7 +97,7 @@
next_state = cpuidle_curr_governor->select(dev);
if (need_resched()) {
local_irq_enable();
- return;
+ return 0;
}
target_state = &dev->states[next_state];
@@ -114,6 +122,8 @@
/* give the governor an opportunity to reflect on the outcome */
if (cpuidle_curr_governor->reflect)
cpuidle_curr_governor->reflect(dev);
+
+ return 0;
}
/**
@@ -121,10 +131,10 @@
*/
void cpuidle_install_idle_handler(void)
{
- if (enabled_devices && (pm_idle != cpuidle_idle_call)) {
+ if (enabled_devices) {
/* Make sure all changes finished before we switch to new idle */
smp_wmb();
- pm_idle = cpuidle_idle_call;
+ initialized = 1;
}
}
@@ -133,8 +143,8 @@
*/
void cpuidle_uninstall_idle_handler(void)
{
- if (enabled_devices && pm_idle_old && (pm_idle != pm_idle_old)) {
- pm_idle = pm_idle_old;
+ if (enabled_devices) {
+ initialized = 0;
cpuidle_kick_cpus();
}
}
@@ -427,7 +437,8 @@
{
int ret;
- pm_idle_old = pm_idle;
+ if (cpuidle_disabled())
+ return -ENODEV;
ret = cpuidle_add_class_sysfs(&cpu_sysdev_class);
if (ret)
@@ -438,4 +449,5 @@
return 0;
}
+module_param(off, int, 0444);
core_initcall(cpuidle_init);
diff --git a/drivers/cpuidle/cpuidle.h b/drivers/cpuidle/cpuidle.h
index 33e50d5..38c3fd8 100644
--- a/drivers/cpuidle/cpuidle.h
+++ b/drivers/cpuidle/cpuidle.h
@@ -13,6 +13,7 @@
extern struct list_head cpuidle_detected_devices;
extern struct mutex cpuidle_lock;
extern spinlock_t cpuidle_driver_lock;
+extern int cpuidle_disabled(void);
/* idle loop */
extern void cpuidle_install_idle_handler(void);
diff --git a/drivers/cpuidle/driver.c b/drivers/cpuidle/driver.c
index fd1601e..3f7e3ce 100644
--- a/drivers/cpuidle/driver.c
+++ b/drivers/cpuidle/driver.c
@@ -26,6 +26,9 @@
if (!drv)
return -EINVAL;
+ if (cpuidle_disabled())
+ return -ENODEV;
+
spin_lock(&cpuidle_driver_lock);
if (cpuidle_curr_driver) {
spin_unlock(&cpuidle_driver_lock);
diff --git a/drivers/cpuidle/governor.c b/drivers/cpuidle/governor.c
index 724c164..ea2f8e7 100644
--- a/drivers/cpuidle/governor.c
+++ b/drivers/cpuidle/governor.c
@@ -81,6 +81,9 @@
if (!gov || !gov->select)
return -EINVAL;
+ if (cpuidle_disabled())
+ return -ENODEV;
+
mutex_lock(&cpuidle_lock);
if (__cpuidle_find_governor(gov->name) == NULL) {
ret = 0;
diff --git a/drivers/dma/TODO b/drivers/dma/TODO
index a4af858..734ed02 100644
--- a/drivers/dma/TODO
+++ b/drivers/dma/TODO
@@ -9,6 +9,5 @@
- mxs-dma.c
- dw_dmac
- intel_mid_dma
- - ste_dma40
4. Check other subsystems for dma drivers and merge/move to dmaengine
5. Remove dma_slave_config's dma direction.
diff --git a/drivers/dma/amba-pl08x.c b/drivers/dma/amba-pl08x.c
index e6d7228..be21e3f 100644
--- a/drivers/dma/amba-pl08x.c
+++ b/drivers/dma/amba-pl08x.c
@@ -80,6 +80,7 @@
#include <linux/interrupt.h>
#include <linux/slab.h>
#include <linux/delay.h>
+#include <linux/dma-mapping.h>
#include <linux/dmapool.h>
#include <linux/dmaengine.h>
#include <linux/amba/bus.h>
@@ -156,14 +157,10 @@
#define PL08X_BOUNDARY_SHIFT (10) /* 1KB 0x400 */
#define PL08X_BOUNDARY_SIZE (1 << PL08X_BOUNDARY_SHIFT)
-/* Minimum period between work queue runs */
-#define PL08X_WQ_PERIODMIN 20
-
/* Size (bytes) of each LLI buffer allocated for one transfer */
# define PL08X_LLI_TSFR_SIZE 0x2000
/* Maximum times we call dma_pool_alloc on this pool without freeing */
-#define PL08X_MAX_ALLOCS 0x40
#define MAX_NUM_TSFR_LLIS (PL08X_LLI_TSFR_SIZE/sizeof(struct pl08x_lli))
#define PL08X_ALIGN 8
@@ -495,10 +492,10 @@
struct pl08x_lli_build_data {
struct pl08x_txd *txd;
- struct pl08x_driver_data *pl08x;
struct pl08x_bus_data srcbus;
struct pl08x_bus_data dstbus;
size_t remainder;
+ u32 lli_bus;
};
/*
@@ -551,8 +548,7 @@
llis_va[num_llis].src = bd->srcbus.addr;
llis_va[num_llis].dst = bd->dstbus.addr;
llis_va[num_llis].lli = llis_bus + (num_llis + 1) * sizeof(struct pl08x_lli);
- if (bd->pl08x->lli_buses & PL08X_AHB2)
- llis_va[num_llis].lli |= PL080_LLI_LM_AHB2;
+ llis_va[num_llis].lli |= bd->lli_bus;
if (cctl & PL080_CONTROL_SRC_INCR)
bd->srcbus.addr += len;
@@ -605,9 +601,9 @@
cctl = txd->cctl;
bd.txd = txd;
- bd.pl08x = pl08x;
bd.srcbus.addr = txd->src_addr;
bd.dstbus.addr = txd->dst_addr;
+ bd.lli_bus = (pl08x->lli_buses & PL08X_AHB2) ? PL080_LLI_LM_AHB2 : 0;
/* Find maximum width of the source bus */
bd.srcbus.maxwidth =
@@ -622,25 +618,15 @@
/* Set up the bus widths to the maximum */
bd.srcbus.buswidth = bd.srcbus.maxwidth;
bd.dstbus.buswidth = bd.dstbus.maxwidth;
- dev_vdbg(&pl08x->adev->dev,
- "%s source bus is %d bytes wide, dest bus is %d bytes wide\n",
- __func__, bd.srcbus.buswidth, bd.dstbus.buswidth);
-
/*
* Bytes transferred == tsize * MIN(buswidths), not max(buswidths)
*/
max_bytes_per_lli = min(bd.srcbus.buswidth, bd.dstbus.buswidth) *
PL080_CONTROL_TRANSFER_SIZE_MASK;
- dev_vdbg(&pl08x->adev->dev,
- "%s max bytes per lli = %zu\n",
- __func__, max_bytes_per_lli);
/* We need to count this down to zero */
bd.remainder = txd->len;
- dev_vdbg(&pl08x->adev->dev,
- "%s remainder = %zu\n",
- __func__, bd.remainder);
/*
* Choose bus to align to
@@ -649,6 +635,16 @@
*/
pl08x_choose_master_bus(&bd, &mbus, &sbus, cctl);
+ dev_vdbg(&pl08x->adev->dev, "src=0x%08x%s/%u dst=0x%08x%s/%u len=%zu llimax=%zu\n",
+ bd.srcbus.addr, cctl & PL080_CONTROL_SRC_INCR ? "+" : "",
+ bd.srcbus.buswidth,
+ bd.dstbus.addr, cctl & PL080_CONTROL_DST_INCR ? "+" : "",
+ bd.dstbus.buswidth,
+ bd.remainder, max_bytes_per_lli);
+ dev_vdbg(&pl08x->adev->dev, "mbus=%s sbus=%s\n",
+ mbus == &bd.srcbus ? "src" : "dst",
+ sbus == &bd.srcbus ? "src" : "dst");
+
if (txd->len < mbus->buswidth) {
/* Less than a bus width available - send as single bytes */
while (bd.remainder) {
@@ -840,15 +836,14 @@
{
int i;
+ dev_vdbg(&pl08x->adev->dev,
+ "%-3s %-9s %-10s %-10s %-10s %s\n",
+ "lli", "", "csrc", "cdst", "clli", "cctl");
for (i = 0; i < num_llis; i++) {
dev_vdbg(&pl08x->adev->dev,
- "lli %d @%p: csrc=0x%08x, cdst=0x%08x, cctl=0x%08x, clli=0x%08x\n",
- i,
- &llis_va[i],
- llis_va[i].src,
- llis_va[i].dst,
- llis_va[i].cctl,
- llis_va[i].lli
+ "%3d @%p: 0x%08x 0x%08x 0x%08x 0x%08x\n",
+ i, &llis_va[i], llis_va[i].src,
+ llis_va[i].dst, llis_va[i].lli, llis_va[i].cctl
);
}
}
@@ -1054,64 +1049,105 @@
/* PrimeCell DMA extension */
struct burst_table {
- int burstwords;
+ u32 burstwords;
u32 reg;
};
static const struct burst_table burst_sizes[] = {
{
.burstwords = 256,
- .reg = (PL080_BSIZE_256 << PL080_CONTROL_SB_SIZE_SHIFT) |
- (PL080_BSIZE_256 << PL080_CONTROL_DB_SIZE_SHIFT),
+ .reg = PL080_BSIZE_256,
},
{
.burstwords = 128,
- .reg = (PL080_BSIZE_128 << PL080_CONTROL_SB_SIZE_SHIFT) |
- (PL080_BSIZE_128 << PL080_CONTROL_DB_SIZE_SHIFT),
+ .reg = PL080_BSIZE_128,
},
{
.burstwords = 64,
- .reg = (PL080_BSIZE_64 << PL080_CONTROL_SB_SIZE_SHIFT) |
- (PL080_BSIZE_64 << PL080_CONTROL_DB_SIZE_SHIFT),
+ .reg = PL080_BSIZE_64,
},
{
.burstwords = 32,
- .reg = (PL080_BSIZE_32 << PL080_CONTROL_SB_SIZE_SHIFT) |
- (PL080_BSIZE_32 << PL080_CONTROL_DB_SIZE_SHIFT),
+ .reg = PL080_BSIZE_32,
},
{
.burstwords = 16,
- .reg = (PL080_BSIZE_16 << PL080_CONTROL_SB_SIZE_SHIFT) |
- (PL080_BSIZE_16 << PL080_CONTROL_DB_SIZE_SHIFT),
+ .reg = PL080_BSIZE_16,
},
{
.burstwords = 8,
- .reg = (PL080_BSIZE_8 << PL080_CONTROL_SB_SIZE_SHIFT) |
- (PL080_BSIZE_8 << PL080_CONTROL_DB_SIZE_SHIFT),
+ .reg = PL080_BSIZE_8,
},
{
.burstwords = 4,
- .reg = (PL080_BSIZE_4 << PL080_CONTROL_SB_SIZE_SHIFT) |
- (PL080_BSIZE_4 << PL080_CONTROL_DB_SIZE_SHIFT),
+ .reg = PL080_BSIZE_4,
},
{
- .burstwords = 1,
- .reg = (PL080_BSIZE_1 << PL080_CONTROL_SB_SIZE_SHIFT) |
- (PL080_BSIZE_1 << PL080_CONTROL_DB_SIZE_SHIFT),
+ .burstwords = 0,
+ .reg = PL080_BSIZE_1,
},
};
+/*
+ * Given the source and destination available bus masks, select which
+ * will be routed to each port. We try to have source and destination
+ * on separate ports, but always respect the allowable settings.
+ */
+static u32 pl08x_select_bus(u8 src, u8 dst)
+{
+ u32 cctl = 0;
+
+ if (!(dst & PL08X_AHB1) || ((dst & PL08X_AHB2) && (src & PL08X_AHB1)))
+ cctl |= PL080_CONTROL_DST_AHB2;
+ if (!(src & PL08X_AHB1) || ((src & PL08X_AHB2) && !(dst & PL08X_AHB2)))
+ cctl |= PL080_CONTROL_SRC_AHB2;
+
+ return cctl;
+}
+
+static u32 pl08x_cctl(u32 cctl)
+{
+ cctl &= ~(PL080_CONTROL_SRC_AHB2 | PL080_CONTROL_DST_AHB2 |
+ PL080_CONTROL_SRC_INCR | PL080_CONTROL_DST_INCR |
+ PL080_CONTROL_PROT_MASK);
+
+ /* Access the cell in privileged mode, non-bufferable, non-cacheable */
+ return cctl | PL080_CONTROL_PROT_SYS;
+}
+
+static u32 pl08x_width(enum dma_slave_buswidth width)
+{
+ switch (width) {
+ case DMA_SLAVE_BUSWIDTH_1_BYTE:
+ return PL080_WIDTH_8BIT;
+ case DMA_SLAVE_BUSWIDTH_2_BYTES:
+ return PL080_WIDTH_16BIT;
+ case DMA_SLAVE_BUSWIDTH_4_BYTES:
+ return PL080_WIDTH_32BIT;
+ default:
+ return ~0;
+ }
+}
+
+static u32 pl08x_burst(u32 maxburst)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(burst_sizes); i++)
+ if (burst_sizes[i].burstwords <= maxburst)
+ break;
+
+ return burst_sizes[i].reg;
+}
+
static int dma_set_runtime_config(struct dma_chan *chan,
struct dma_slave_config *config)
{
struct pl08x_dma_chan *plchan = to_pl08x_chan(chan);
struct pl08x_driver_data *pl08x = plchan->host;
- struct pl08x_channel_data *cd = plchan->cd;
enum dma_slave_buswidth addr_width;
- dma_addr_t addr;
- u32 maxburst;
+ u32 width, burst, maxburst;
u32 cctl = 0;
- int i;
if (!plchan->slave)
return -EINVAL;
@@ -1119,11 +1155,9 @@
/* Transfer direction */
plchan->runtime_direction = config->direction;
if (config->direction == DMA_TO_DEVICE) {
- addr = config->dst_addr;
addr_width = config->dst_addr_width;
maxburst = config->dst_maxburst;
} else if (config->direction == DMA_FROM_DEVICE) {
- addr = config->src_addr;
addr_width = config->src_addr_width;
maxburst = config->src_maxburst;
} else {
@@ -1132,46 +1166,40 @@
return -EINVAL;
}
- switch (addr_width) {
- case DMA_SLAVE_BUSWIDTH_1_BYTE:
- cctl |= (PL080_WIDTH_8BIT << PL080_CONTROL_SWIDTH_SHIFT) |
- (PL080_WIDTH_8BIT << PL080_CONTROL_DWIDTH_SHIFT);
- break;
- case DMA_SLAVE_BUSWIDTH_2_BYTES:
- cctl |= (PL080_WIDTH_16BIT << PL080_CONTROL_SWIDTH_SHIFT) |
- (PL080_WIDTH_16BIT << PL080_CONTROL_DWIDTH_SHIFT);
- break;
- case DMA_SLAVE_BUSWIDTH_4_BYTES:
- cctl |= (PL080_WIDTH_32BIT << PL080_CONTROL_SWIDTH_SHIFT) |
- (PL080_WIDTH_32BIT << PL080_CONTROL_DWIDTH_SHIFT);
- break;
- default:
+ width = pl08x_width(addr_width);
+ if (width == ~0) {
dev_err(&pl08x->adev->dev,
"bad runtime_config: alien address width\n");
return -EINVAL;
}
+ cctl |= width << PL080_CONTROL_SWIDTH_SHIFT;
+ cctl |= width << PL080_CONTROL_DWIDTH_SHIFT;
+
/*
- * Now decide on a maxburst:
* If this channel will only request single transfers, set this
* down to ONE element. Also select one element if no maxburst
* is specified.
*/
- if (plchan->cd->single || maxburst == 0) {
- cctl |= (PL080_BSIZE_1 << PL080_CONTROL_SB_SIZE_SHIFT) |
- (PL080_BSIZE_1 << PL080_CONTROL_DB_SIZE_SHIFT);
+ if (plchan->cd->single)
+ maxburst = 1;
+
+ burst = pl08x_burst(maxburst);
+ cctl |= burst << PL080_CONTROL_SB_SIZE_SHIFT;
+ cctl |= burst << PL080_CONTROL_DB_SIZE_SHIFT;
+
+ if (plchan->runtime_direction == DMA_FROM_DEVICE) {
+ plchan->src_addr = config->src_addr;
+ plchan->src_cctl = pl08x_cctl(cctl) | PL080_CONTROL_DST_INCR |
+ pl08x_select_bus(plchan->cd->periph_buses,
+ pl08x->mem_buses);
} else {
- for (i = 0; i < ARRAY_SIZE(burst_sizes); i++)
- if (burst_sizes[i].burstwords <= maxburst)
- break;
- cctl |= burst_sizes[i].reg;
+ plchan->dst_addr = config->dst_addr;
+ plchan->dst_cctl = pl08x_cctl(cctl) | PL080_CONTROL_SRC_INCR |
+ pl08x_select_bus(pl08x->mem_buses,
+ plchan->cd->periph_buses);
}
- plchan->runtime_addr = addr;
-
- /* Modify the default channel data to fit PrimeCell request */
- cd->cctl = cctl;
-
dev_dbg(&pl08x->adev->dev,
"configured channel %s (%s) for %s, data width %d, "
"maxburst %d words, LE, CCTL=0x%08x\n",
@@ -1270,23 +1298,6 @@
return 0;
}
-/*
- * Given the source and destination available bus masks, select which
- * will be routed to each port. We try to have source and destination
- * on separate ports, but always respect the allowable settings.
- */
-static u32 pl08x_select_bus(struct pl08x_driver_data *pl08x, u8 src, u8 dst)
-{
- u32 cctl = 0;
-
- if (!(dst & PL08X_AHB1) || ((dst & PL08X_AHB2) && (src & PL08X_AHB1)))
- cctl |= PL080_CONTROL_DST_AHB2;
- if (!(src & PL08X_AHB1) || ((src & PL08X_AHB2) && !(dst & PL08X_AHB2)))
- cctl |= PL080_CONTROL_SRC_AHB2;
-
- return cctl;
-}
-
static struct pl08x_txd *pl08x_get_txd(struct pl08x_dma_chan *plchan,
unsigned long flags)
{
@@ -1338,8 +1349,8 @@
txd->cctl |= PL080_CONTROL_SRC_INCR | PL080_CONTROL_DST_INCR;
if (pl08x->vd->dualmaster)
- txd->cctl |= pl08x_select_bus(pl08x,
- pl08x->mem_buses, pl08x->mem_buses);
+ txd->cctl |= pl08x_select_bus(pl08x->mem_buses,
+ pl08x->mem_buses);
ret = pl08x_prep_channel_resources(plchan, txd);
if (ret)
@@ -1356,7 +1367,6 @@
struct pl08x_dma_chan *plchan = to_pl08x_chan(chan);
struct pl08x_driver_data *pl08x = plchan->host;
struct pl08x_txd *txd;
- u8 src_buses, dst_buses;
int ret;
/*
@@ -1390,42 +1400,22 @@
txd->direction = direction;
txd->len = sgl->length;
- txd->cctl = plchan->cd->cctl &
- ~(PL080_CONTROL_SRC_AHB2 | PL080_CONTROL_DST_AHB2 |
- PL080_CONTROL_SRC_INCR | PL080_CONTROL_DST_INCR |
- PL080_CONTROL_PROT_MASK);
-
- /* Access the cell in privileged mode, non-bufferable, non-cacheable */
- txd->cctl |= PL080_CONTROL_PROT_SYS;
-
if (direction == DMA_TO_DEVICE) {
txd->ccfg |= PL080_FLOW_MEM2PER << PL080_CONFIG_FLOW_CONTROL_SHIFT;
- txd->cctl |= PL080_CONTROL_SRC_INCR;
+ txd->cctl = plchan->dst_cctl;
txd->src_addr = sgl->dma_address;
- if (plchan->runtime_addr)
- txd->dst_addr = plchan->runtime_addr;
- else
- txd->dst_addr = plchan->cd->addr;
- src_buses = pl08x->mem_buses;
- dst_buses = plchan->cd->periph_buses;
+ txd->dst_addr = plchan->dst_addr;
} else if (direction == DMA_FROM_DEVICE) {
txd->ccfg |= PL080_FLOW_PER2MEM << PL080_CONFIG_FLOW_CONTROL_SHIFT;
- txd->cctl |= PL080_CONTROL_DST_INCR;
- if (plchan->runtime_addr)
- txd->src_addr = plchan->runtime_addr;
- else
- txd->src_addr = plchan->cd->addr;
+ txd->cctl = plchan->src_cctl;
+ txd->src_addr = plchan->src_addr;
txd->dst_addr = sgl->dma_address;
- src_buses = plchan->cd->periph_buses;
- dst_buses = pl08x->mem_buses;
} else {
dev_err(&pl08x->adev->dev,
"%s direction unsupported\n", __func__);
return NULL;
}
- txd->cctl |= pl08x_select_bus(pl08x, src_buses, dst_buses);
-
ret = pl08x_prep_channel_resources(plchan, txd);
if (ret)
return NULL;
@@ -1676,6 +1666,20 @@
return mask ? IRQ_HANDLED : IRQ_NONE;
}
+static void pl08x_dma_slave_init(struct pl08x_dma_chan *chan)
+{
+ u32 cctl = pl08x_cctl(chan->cd->cctl);
+
+ chan->slave = true;
+ chan->name = chan->cd->bus_id;
+ chan->src_addr = chan->cd->addr;
+ chan->dst_addr = chan->cd->addr;
+ chan->src_cctl = cctl | PL080_CONTROL_DST_INCR |
+ pl08x_select_bus(chan->cd->periph_buses, chan->host->mem_buses);
+ chan->dst_cctl = cctl | PL080_CONTROL_SRC_INCR |
+ pl08x_select_bus(chan->host->mem_buses, chan->cd->periph_buses);
+}
+
/*
* Initialise the DMAC memcpy/slave channels.
* Make a local wrapper to hold required data
@@ -1707,9 +1711,8 @@
chan->state = PL08X_CHAN_IDLE;
if (slave) {
- chan->slave = true;
- chan->name = pl08x->pd->slave_channels[i].bus_id;
chan->cd = &pl08x->pd->slave_channels[i];
+ pl08x_dma_slave_init(chan);
} else {
chan->cd = &pl08x->pd->memcpy_channel;
chan->name = kasprintf(GFP_KERNEL, "memcpy%d", i);
diff --git a/drivers/dma/at_hdmac.c b/drivers/dma/at_hdmac.c
index 36144f8..6a483ea 100644
--- a/drivers/dma/at_hdmac.c
+++ b/drivers/dma/at_hdmac.c
@@ -1216,7 +1216,7 @@
atdma->dma_common.cap_mask = pdata->cap_mask;
atdma->all_chan_mask = (1 << pdata->nr_channels) - 1;
- size = io->end - io->start + 1;
+ size = resource_size(io);
if (!request_mem_region(io->start, size, pdev->dev.driver->name)) {
err = -EBUSY;
goto err_kfree;
@@ -1362,7 +1362,7 @@
atdma->regs = NULL;
io = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- release_mem_region(io->start, io->end - io->start + 1);
+ release_mem_region(io->start, resource_size(io));
kfree(atdma);
diff --git a/drivers/dma/coh901318.c b/drivers/dma/coh901318.c
index a92d95e..4234f41 100644
--- a/drivers/dma/coh901318.c
+++ b/drivers/dma/coh901318.c
@@ -41,6 +41,8 @@
struct coh901318_lli *lli;
enum dma_data_direction dir;
unsigned long flags;
+ u32 head_config;
+ u32 head_ctrl;
};
struct coh901318_base {
@@ -661,6 +663,9 @@
coh901318_desc_submit(cohc, cohd);
+ /* Program the transaction head */
+ coh901318_set_conf(cohc, cohd->head_config);
+ coh901318_set_ctrl(cohc, cohd->head_ctrl);
coh901318_prep_linked_list(cohc, cohd->lli);
/* start dma job on this channel */
@@ -1091,8 +1096,6 @@
} else
goto err_direction;
- coh901318_set_conf(cohc, config);
-
/* The dma only supports transmitting packages up to
* MAX_DMA_PACKET_SIZE. Calculate to total number of
* dma elemts required to send the entire sg list
@@ -1129,16 +1132,18 @@
if (ret)
goto err_lli_fill;
- /*
- * Set the default ctrl for the channel to the one from the lli,
- * things may have changed due to odd buffer alignment etc.
- */
- coh901318_set_ctrl(cohc, lli->control);
COH_DBG(coh901318_list_print(cohc, lli));
/* Pick a descriptor to handle this transfer */
cohd = coh901318_desc_get(cohc);
+ cohd->head_config = config;
+ /*
+ * Set the default head ctrl for the channel to the one from the
+ * lli, things may have changed due to odd buffer alignment
+ * etc.
+ */
+ cohd->head_ctrl = lli->control;
cohd->dir = direction;
cohd->flags = flags;
cohd->desc.tx_submit = coh901318_tx_submit;
diff --git a/drivers/dma/dmaengine.c b/drivers/dma/dmaengine.c
index 48694c3..b48967b 100644
--- a/drivers/dma/dmaengine.c
+++ b/drivers/dma/dmaengine.c
@@ -62,9 +62,9 @@
#include <linux/slab.h>
static DEFINE_MUTEX(dma_list_mutex);
+static DEFINE_IDR(dma_idr);
static LIST_HEAD(dma_device_list);
static long dmaengine_ref_count;
-static struct idr dma_idr;
/* --- sysfs implementation --- */
@@ -510,8 +510,8 @@
dma_chan_name(chan));
list_del_rcu(&device->global_node);
} else if (err)
- pr_err("dmaengine: failed to get %s: (%d)\n",
- dma_chan_name(chan), err);
+ pr_debug("dmaengine: failed to get %s: (%d)\n",
+ dma_chan_name(chan), err);
else
break;
if (--device->privatecnt == 0)
@@ -1050,8 +1050,6 @@
static int __init dma_bus_init(void)
{
- idr_init(&dma_idr);
- mutex_init(&dma_list_mutex);
return class_register(&dma_devclass);
}
arch_initcall(dma_bus_init);
diff --git a/drivers/dma/ep93xx_dma.c b/drivers/dma/ep93xx_dma.c
index 0766c1e..5d7a49b 100644
--- a/drivers/dma/ep93xx_dma.c
+++ b/drivers/dma/ep93xx_dma.c
@@ -902,7 +902,7 @@
*
* Returns a valid DMA descriptor or %NULL in case of failure.
*/
-struct dma_async_tx_descriptor *
+static struct dma_async_tx_descriptor *
ep93xx_dma_prep_dma_memcpy(struct dma_chan *chan, dma_addr_t dest,
dma_addr_t src, size_t len, unsigned long flags)
{
diff --git a/drivers/dma/imx-sdma.c b/drivers/dma/imx-sdma.c
index 1eb60de..7bd7e98 100644
--- a/drivers/dma/imx-sdma.c
+++ b/drivers/dma/imx-sdma.c
@@ -1305,8 +1305,10 @@
goto err_request_irq;
sdma->script_addrs = kzalloc(sizeof(*sdma->script_addrs), GFP_KERNEL);
- if (!sdma->script_addrs)
+ if (!sdma->script_addrs) {
+ ret = -ENOMEM;
goto err_alloc;
+ }
if (of_id)
pdev->id_entry = of_id->data;
diff --git a/drivers/dma/intel_mid_dma.c b/drivers/dma/intel_mid_dma.c
index f653517..8a3fdd8 100644
--- a/drivers/dma/intel_mid_dma.c
+++ b/drivers/dma/intel_mid_dma.c
@@ -1351,7 +1351,6 @@
return -EAGAIN;
}
device->state = SUSPENDED;
- pci_set_drvdata(pci, device);
pci_save_state(pci);
pci_disable_device(pci);
pci_set_power_state(pci, PCI_D3hot);
@@ -1380,7 +1379,6 @@
}
device->state = RUNNING;
iowrite32(REG_BIT0, device->dma_base + DMA_CFG);
- pci_set_drvdata(pci, device);
return 0;
}
diff --git a/drivers/dma/ioat/dma_v3.c b/drivers/dma/ioat/dma_v3.c
index d845dc4..f519c93 100644
--- a/drivers/dma/ioat/dma_v3.c
+++ b/drivers/dma/ioat/dma_v3.c
@@ -73,10 +73,10 @@
/* provide a lookup table for setting the source address in the base or
* extended descriptor of an xor or pq descriptor
*/
-static const u8 xor_idx_to_desc __read_mostly = 0xd0;
-static const u8 xor_idx_to_field[] __read_mostly = { 1, 4, 5, 6, 7, 0, 1, 2 };
-static const u8 pq_idx_to_desc __read_mostly = 0xf8;
-static const u8 pq_idx_to_field[] __read_mostly = { 1, 4, 5, 0, 1, 2, 4, 5 };
+static const u8 xor_idx_to_desc = 0xe0;
+static const u8 xor_idx_to_field[] = { 1, 4, 5, 6, 7, 0, 1, 2 };
+static const u8 pq_idx_to_desc = 0xf8;
+static const u8 pq_idx_to_field[] = { 1, 4, 5, 0, 1, 2, 4, 5 };
static dma_addr_t xor_get_src(struct ioat_raw_descriptor *descs[2], int idx)
{
diff --git a/drivers/dma/ioat/pci.c b/drivers/dma/ioat/pci.c
index fab37d1..5e3a40f 100644
--- a/drivers/dma/ioat/pci.c
+++ b/drivers/dma/ioat/pci.c
@@ -72,6 +72,17 @@
{ PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_IOAT_JSF8) },
{ PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_IOAT_JSF9) },
+ { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_IOAT_SNB0) },
+ { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_IOAT_SNB1) },
+ { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_IOAT_SNB2) },
+ { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_IOAT_SNB3) },
+ { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_IOAT_SNB4) },
+ { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_IOAT_SNB5) },
+ { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_IOAT_SNB6) },
+ { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_IOAT_SNB7) },
+ { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_IOAT_SNB8) },
+ { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_IOAT_SNB9) },
+
{ 0, }
};
MODULE_DEVICE_TABLE(pci, ioat_pci_tbl);
diff --git a/drivers/dma/ipu/ipu_idmac.c b/drivers/dma/ipu/ipu_idmac.c
index fd7d2b3..6815905 100644
--- a/drivers/dma/ipu/ipu_idmac.c
+++ b/drivers/dma/ipu/ipu_idmac.c
@@ -1706,16 +1706,14 @@
ipu_data.irq_fn, ipu_data.irq_err, ipu_data.irq_base);
/* Remap IPU common registers */
- ipu_data.reg_ipu = ioremap(mem_ipu->start,
- mem_ipu->end - mem_ipu->start + 1);
+ ipu_data.reg_ipu = ioremap(mem_ipu->start, resource_size(mem_ipu));
if (!ipu_data.reg_ipu) {
ret = -ENOMEM;
goto err_ioremap_ipu;
}
/* Remap Image Converter and Image DMA Controller registers */
- ipu_data.reg_ic = ioremap(mem_ic->start,
- mem_ic->end - mem_ic->start + 1);
+ ipu_data.reg_ic = ioremap(mem_ic->start, resource_size(mem_ic));
if (!ipu_data.reg_ic) {
ret = -ENOMEM;
goto err_ioremap_ic;
diff --git a/drivers/dma/mv_xor.c b/drivers/dma/mv_xor.c
index 06f9f27..9a353c2 100644
--- a/drivers/dma/mv_xor.c
+++ b/drivers/dma/mv_xor.c
@@ -1304,7 +1304,8 @@
if (!res)
return -ENODEV;
- msp->xor_base = devm_ioremap(&pdev->dev, res->start, resource_size(res));
+ msp->xor_base = devm_ioremap(&pdev->dev, res->start,
+ resource_size(res));
if (!msp->xor_base)
return -EBUSY;
diff --git a/drivers/dma/mxs-dma.c b/drivers/dma/mxs-dma.c
index 88aad4f..be641cb 100644
--- a/drivers/dma/mxs-dma.c
+++ b/drivers/dma/mxs-dma.c
@@ -327,10 +327,12 @@
memset(mxs_chan->ccw, 0, PAGE_SIZE);
- ret = request_irq(mxs_chan->chan_irq, mxs_dma_int_handler,
- 0, "mxs-dma", mxs_dma);
- if (ret)
- goto err_irq;
+ if (mxs_chan->chan_irq != NO_IRQ) {
+ ret = request_irq(mxs_chan->chan_irq, mxs_dma_int_handler,
+ 0, "mxs-dma", mxs_dma);
+ if (ret)
+ goto err_irq;
+ }
ret = clk_enable(mxs_dma->clk);
if (ret)
@@ -535,6 +537,7 @@
switch (cmd) {
case DMA_TERMINATE_ALL:
mxs_dma_disable_chan(mxs_chan);
+ mxs_dma_reset_chan(mxs_chan);
break;
case DMA_PAUSE:
mxs_dma_pause_chan(mxs_chan);
@@ -707,6 +710,8 @@
}, {
.name = "mxs-dma-apbx",
.driver_data = MXS_DMA_APBX,
+ }, {
+ /* end of list */
}
};
diff --git a/drivers/dma/pch_dma.c b/drivers/dma/pch_dma.c
index ff5b38f..1ac8d4b 100644
--- a/drivers/dma/pch_dma.c
+++ b/drivers/dma/pch_dma.c
@@ -45,7 +45,8 @@
#define DMA_STATUS_MASK_BITS 0x3
#define DMA_STATUS_SHIFT_BITS 16
#define DMA_STATUS_IRQ(x) (0x1 << (x))
-#define DMA_STATUS_ERR(x) (0x1 << ((x) + 8))
+#define DMA_STATUS0_ERR(x) (0x1 << ((x) + 8))
+#define DMA_STATUS2_ERR(x) (0x1 << (x))
#define DMA_DESC_WIDTH_SHIFT_BITS 12
#define DMA_DESC_WIDTH_1_BYTE (0x3 << DMA_DESC_WIDTH_SHIFT_BITS)
@@ -61,6 +62,9 @@
#define MAX_CHAN_NR 8
+#define DMA_MASK_CTL0_MODE 0x33333333
+#define DMA_MASK_CTL2_MODE 0x00003333
+
static unsigned int init_nr_desc_per_channel = 64;
module_param(init_nr_desc_per_channel, uint, 0644);
MODULE_PARM_DESC(init_nr_desc_per_channel,
@@ -133,6 +137,7 @@
#define PCH_DMA_CTL3 0x0C
#define PCH_DMA_STS0 0x10
#define PCH_DMA_STS1 0x14
+#define PCH_DMA_STS2 0x18
#define dma_readl(pd, name) \
readl((pd)->membase + PCH_DMA_##name)
@@ -183,13 +188,19 @@
{
struct pch_dma *pd = to_pd(chan->device);
u32 val;
+ int pos;
+
+ if (chan->chan_id < 8)
+ pos = chan->chan_id;
+ else
+ pos = chan->chan_id + 8;
val = dma_readl(pd, CTL2);
if (enable)
- val |= 0x1 << chan->chan_id;
+ val |= 0x1 << pos;
else
- val &= ~(0x1 << chan->chan_id);
+ val &= ~(0x1 << pos);
dma_writel(pd, CTL2, val);
@@ -202,10 +213,17 @@
struct pch_dma_chan *pd_chan = to_pd_chan(chan);
struct pch_dma *pd = to_pd(chan->device);
u32 val;
+ u32 mask_mode;
+ u32 mask_ctl;
if (chan->chan_id < 8) {
val = dma_readl(pd, CTL0);
+ mask_mode = DMA_CTL0_MODE_MASK_BITS <<
+ (DMA_CTL0_BITS_PER_CH * chan->chan_id);
+ mask_ctl = DMA_MASK_CTL0_MODE & ~(DMA_CTL0_MODE_MASK_BITS <<
+ (DMA_CTL0_BITS_PER_CH * chan->chan_id));
+ val &= mask_mode;
if (pd_chan->dir == DMA_TO_DEVICE)
val |= 0x1 << (DMA_CTL0_BITS_PER_CH * chan->chan_id +
DMA_CTL0_DIR_SHIFT_BITS);
@@ -213,18 +231,24 @@
val &= ~(0x1 << (DMA_CTL0_BITS_PER_CH * chan->chan_id +
DMA_CTL0_DIR_SHIFT_BITS));
+ val |= mask_ctl;
dma_writel(pd, CTL0, val);
} else {
int ch = chan->chan_id - 8; /* ch8-->0 ch9-->1 ... ch11->3 */
val = dma_readl(pd, CTL3);
+ mask_mode = DMA_CTL0_MODE_MASK_BITS <<
+ (DMA_CTL0_BITS_PER_CH * ch);
+ mask_ctl = DMA_MASK_CTL2_MODE & ~(DMA_CTL0_MODE_MASK_BITS <<
+ (DMA_CTL0_BITS_PER_CH * ch));
+ val &= mask_mode;
if (pd_chan->dir == DMA_TO_DEVICE)
val |= 0x1 << (DMA_CTL0_BITS_PER_CH * ch +
DMA_CTL0_DIR_SHIFT_BITS);
else
val &= ~(0x1 << (DMA_CTL0_BITS_PER_CH * ch +
DMA_CTL0_DIR_SHIFT_BITS));
-
+ val |= mask_ctl;
dma_writel(pd, CTL3, val);
}
@@ -236,33 +260,37 @@
{
struct pch_dma *pd = to_pd(chan->device);
u32 val;
+ u32 mask_ctl;
+ u32 mask_dir;
if (chan->chan_id < 8) {
+ mask_ctl = DMA_MASK_CTL0_MODE & ~(DMA_CTL0_MODE_MASK_BITS <<
+ (DMA_CTL0_BITS_PER_CH * chan->chan_id));
+ mask_dir = 1 << (DMA_CTL0_BITS_PER_CH * chan->chan_id +\
+ DMA_CTL0_DIR_SHIFT_BITS);
val = dma_readl(pd, CTL0);
-
- val &= ~(DMA_CTL0_MODE_MASK_BITS <<
- (DMA_CTL0_BITS_PER_CH * chan->chan_id));
+ val &= mask_dir;
val |= mode << (DMA_CTL0_BITS_PER_CH * chan->chan_id);
-
+ val |= mask_ctl;
dma_writel(pd, CTL0, val);
} else {
int ch = chan->chan_id - 8; /* ch8-->0 ch9-->1 ... ch11->3 */
-
+ mask_ctl = DMA_MASK_CTL2_MODE & ~(DMA_CTL0_MODE_MASK_BITS <<
+ (DMA_CTL0_BITS_PER_CH * ch));
+ mask_dir = 1 << (DMA_CTL0_BITS_PER_CH * ch +\
+ DMA_CTL0_DIR_SHIFT_BITS);
val = dma_readl(pd, CTL3);
-
- val &= ~(DMA_CTL0_MODE_MASK_BITS <<
- (DMA_CTL0_BITS_PER_CH * ch));
+ val &= mask_dir;
val |= mode << (DMA_CTL0_BITS_PER_CH * ch);
-
+ val |= mask_ctl;
dma_writel(pd, CTL3, val);
-
}
dev_dbg(chan2dev(chan), "pdc_set_mode: chan %d -> %x\n",
chan->chan_id, val);
}
-static u32 pdc_get_status(struct pch_dma_chan *pd_chan)
+static u32 pdc_get_status0(struct pch_dma_chan *pd_chan)
{
struct pch_dma *pd = to_pd(pd_chan->chan.device);
u32 val;
@@ -272,9 +300,27 @@
DMA_STATUS_BITS_PER_CH * pd_chan->chan.chan_id));
}
+static u32 pdc_get_status2(struct pch_dma_chan *pd_chan)
+{
+ struct pch_dma *pd = to_pd(pd_chan->chan.device);
+ u32 val;
+
+ val = dma_readl(pd, STS2);
+ return DMA_STATUS_MASK_BITS & (val >> (DMA_STATUS_SHIFT_BITS +
+ DMA_STATUS_BITS_PER_CH * (pd_chan->chan.chan_id - 8)));
+}
+
static bool pdc_is_idle(struct pch_dma_chan *pd_chan)
{
- if (pdc_get_status(pd_chan) == DMA_STATUS_IDLE)
+ u32 sts;
+
+ if (pd_chan->chan.chan_id < 8)
+ sts = pdc_get_status0(pd_chan);
+ else
+ sts = pdc_get_status2(pd_chan);
+
+
+ if (sts == DMA_STATUS_IDLE)
return true;
else
return false;
@@ -495,11 +541,11 @@
list_add_tail(&desc->desc_node, &tmp_list);
}
- spin_lock_bh(&pd_chan->lock);
+ spin_lock_irq(&pd_chan->lock);
list_splice(&tmp_list, &pd_chan->free_list);
pd_chan->descs_allocated = i;
pd_chan->completed_cookie = chan->cookie = 1;
- spin_unlock_bh(&pd_chan->lock);
+ spin_unlock_irq(&pd_chan->lock);
pdc_enable_irq(chan, 1);
@@ -517,10 +563,10 @@
BUG_ON(!list_empty(&pd_chan->active_list));
BUG_ON(!list_empty(&pd_chan->queue));
- spin_lock_bh(&pd_chan->lock);
+ spin_lock_irq(&pd_chan->lock);
list_splice_init(&pd_chan->free_list, &tmp_list);
pd_chan->descs_allocated = 0;
- spin_unlock_bh(&pd_chan->lock);
+ spin_unlock_irq(&pd_chan->lock);
list_for_each_entry_safe(desc, _d, &tmp_list, desc_node)
pci_pool_free(pd->pool, desc, desc->txd.phys);
@@ -536,10 +582,10 @@
dma_cookie_t last_completed;
int ret;
- spin_lock_bh(&pd_chan->lock);
+ spin_lock_irq(&pd_chan->lock);
last_completed = pd_chan->completed_cookie;
last_used = chan->cookie;
- spin_unlock_bh(&pd_chan->lock);
+ spin_unlock_irq(&pd_chan->lock);
ret = dma_async_is_complete(cookie, last_completed, last_used);
@@ -654,7 +700,7 @@
if (cmd != DMA_TERMINATE_ALL)
return -ENXIO;
- spin_lock_bh(&pd_chan->lock);
+ spin_lock_irq(&pd_chan->lock);
pdc_set_mode(&pd_chan->chan, DMA_CTL0_DISABLE);
@@ -664,7 +710,7 @@
list_for_each_entry_safe(desc, _d, &list, desc_node)
pdc_chain_complete(pd_chan, desc);
- spin_unlock_bh(&pd_chan->lock);
+ spin_unlock_irq(&pd_chan->lock);
return 0;
}
@@ -693,30 +739,45 @@
struct pch_dma *pd = (struct pch_dma *)devid;
struct pch_dma_chan *pd_chan;
u32 sts0;
+ u32 sts2;
int i;
- int ret = IRQ_NONE;
+ int ret0 = IRQ_NONE;
+ int ret2 = IRQ_NONE;
sts0 = dma_readl(pd, STS0);
+ sts2 = dma_readl(pd, STS2);
dev_dbg(pd->dma.dev, "pd_irq sts0: %x\n", sts0);
for (i = 0; i < pd->dma.chancnt; i++) {
pd_chan = &pd->channels[i];
- if (sts0 & DMA_STATUS_IRQ(i)) {
- if (sts0 & DMA_STATUS_ERR(i))
- set_bit(0, &pd_chan->err_status);
+ if (i < 8) {
+ if (sts0 & DMA_STATUS_IRQ(i)) {
+ if (sts0 & DMA_STATUS0_ERR(i))
+ set_bit(0, &pd_chan->err_status);
- tasklet_schedule(&pd_chan->tasklet);
- ret = IRQ_HANDLED;
+ tasklet_schedule(&pd_chan->tasklet);
+ ret0 = IRQ_HANDLED;
+ }
+ } else {
+ if (sts2 & DMA_STATUS_IRQ(i - 8)) {
+ if (sts2 & DMA_STATUS2_ERR(i))
+ set_bit(0, &pd_chan->err_status);
+
+ tasklet_schedule(&pd_chan->tasklet);
+ ret2 = IRQ_HANDLED;
+ }
}
-
}
/* clear interrupt bits in status register */
- dma_writel(pd, STS0, sts0);
+ if (ret0)
+ dma_writel(pd, STS0, sts0);
+ if (ret2)
+ dma_writel(pd, STS2, sts2);
- return ret;
+ return ret0 | ret2;
}
#ifdef CONFIG_PM
diff --git a/drivers/dma/pl330.c b/drivers/dma/pl330.c
index 6abe1ec..00eee59 100644
--- a/drivers/dma/pl330.c
+++ b/drivers/dma/pl330.c
@@ -82,7 +82,7 @@
spinlock_t pool_lock;
/* Peripheral channels connected to this DMAC */
- struct dma_pl330_chan peripherals[0]; /* keep at end */
+ struct dma_pl330_chan *peripherals; /* keep at end */
};
struct dma_pl330_desc {
@@ -451,8 +451,13 @@
desc->txd.cookie = 0;
async_tx_ack(&desc->txd);
- desc->req.rqtype = peri->rqtype;
- desc->req.peri = peri->peri_id;
+ if (peri) {
+ desc->req.rqtype = peri->rqtype;
+ desc->req.peri = peri->peri_id;
+ } else {
+ desc->req.rqtype = MEMTOMEM;
+ desc->req.peri = 0;
+ }
dma_async_tx_descriptor_init(&desc->txd, &pch->chan);
@@ -529,10 +534,10 @@
struct pl330_info *pi;
int burst;
- if (unlikely(!pch || !len || !peri))
+ if (unlikely(!pch || !len))
return NULL;
- if (peri->rqtype != MEMTOMEM)
+ if (peri && peri->rqtype != MEMTOMEM)
return NULL;
pi = &pch->dmac->pif;
@@ -577,7 +582,7 @@
int i, burst_size;
dma_addr_t addr;
- if (unlikely(!pch || !sgl || !sg_len))
+ if (unlikely(!pch || !sgl || !sg_len || !peri))
return NULL;
/* Make sure the direction is consistent */
@@ -666,17 +671,12 @@
struct dma_device *pd;
struct resource *res;
int i, ret, irq;
+ int num_chan;
pdat = adev->dev.platform_data;
- if (!pdat || !pdat->nr_valid_peri) {
- dev_err(&adev->dev, "platform data missing\n");
- return -ENODEV;
- }
-
/* Allocate a new DMAC and its Channels */
- pdmac = kzalloc(pdat->nr_valid_peri * sizeof(*pch)
- + sizeof(*pdmac), GFP_KERNEL);
+ pdmac = kzalloc(sizeof(*pdmac), GFP_KERNEL);
if (!pdmac) {
dev_err(&adev->dev, "unable to allocate mem\n");
return -ENOMEM;
@@ -685,7 +685,7 @@
pi = &pdmac->pif;
pi->dev = &adev->dev;
pi->pl330_data = NULL;
- pi->mcbufsz = pdat->mcbuf_sz;
+ pi->mcbufsz = pdat ? pdat->mcbuf_sz : 0;
res = &adev->res;
request_mem_region(res->start, resource_size(res), "dma-pl330");
@@ -717,27 +717,35 @@
INIT_LIST_HEAD(&pd->channels);
/* Initialize channel parameters */
- for (i = 0; i < pdat->nr_valid_peri; i++) {
- struct dma_pl330_peri *peri = &pdat->peri[i];
- pch = &pdmac->peripherals[i];
+ num_chan = max(pdat ? pdat->nr_valid_peri : 0, (u8)pi->pcfg.num_chan);
+ pdmac->peripherals = kzalloc(num_chan * sizeof(*pch), GFP_KERNEL);
- switch (peri->rqtype) {
- case MEMTOMEM:
+ for (i = 0; i < num_chan; i++) {
+ pch = &pdmac->peripherals[i];
+ if (pdat) {
+ struct dma_pl330_peri *peri = &pdat->peri[i];
+
+ switch (peri->rqtype) {
+ case MEMTOMEM:
+ dma_cap_set(DMA_MEMCPY, pd->cap_mask);
+ break;
+ case MEMTODEV:
+ case DEVTOMEM:
+ dma_cap_set(DMA_SLAVE, pd->cap_mask);
+ break;
+ default:
+ dev_err(&adev->dev, "DEVTODEV Not Supported\n");
+ continue;
+ }
+ pch->chan.private = peri;
+ } else {
dma_cap_set(DMA_MEMCPY, pd->cap_mask);
- break;
- case MEMTODEV:
- case DEVTOMEM:
- dma_cap_set(DMA_SLAVE, pd->cap_mask);
- break;
- default:
- dev_err(&adev->dev, "DEVTODEV Not Supported\n");
- continue;
+ pch->chan.private = NULL;
}
INIT_LIST_HEAD(&pch->work_list);
spin_lock_init(&pch->lock);
pch->pl330_chid = NULL;
- pch->chan.private = peri;
pch->chan.device = pd;
pch->chan.chan_id = i;
pch->dmac = pdmac;
diff --git a/drivers/dma/shdma.c b/drivers/dma/shdma.c
index 0283300..7f49235 100644
--- a/drivers/dma/shdma.c
+++ b/drivers/dma/shdma.c
@@ -70,12 +70,36 @@
static u16 dmaor_read(struct sh_dmae_device *shdev)
{
- return __raw_readw(shdev->chan_reg + DMAOR / sizeof(u32));
+ u32 __iomem *addr = shdev->chan_reg + DMAOR / sizeof(u32);
+
+ if (shdev->pdata->dmaor_is_32bit)
+ return __raw_readl(addr);
+ else
+ return __raw_readw(addr);
}
static void dmaor_write(struct sh_dmae_device *shdev, u16 data)
{
- __raw_writew(data, shdev->chan_reg + DMAOR / sizeof(u32));
+ u32 __iomem *addr = shdev->chan_reg + DMAOR / sizeof(u32);
+
+ if (shdev->pdata->dmaor_is_32bit)
+ __raw_writel(data, addr);
+ else
+ __raw_writew(data, addr);
+}
+
+static void chcr_write(struct sh_dmae_chan *sh_dc, u32 data)
+{
+ struct sh_dmae_device *shdev = to_sh_dev(sh_dc);
+
+ __raw_writel(data, sh_dc->base + shdev->chcr_offset / sizeof(u32));
+}
+
+static u32 chcr_read(struct sh_dmae_chan *sh_dc)
+{
+ struct sh_dmae_device *shdev = to_sh_dev(sh_dc);
+
+ return __raw_readl(sh_dc->base + shdev->chcr_offset / sizeof(u32));
}
/*
@@ -120,7 +144,7 @@
static bool dmae_is_busy(struct sh_dmae_chan *sh_chan)
{
- u32 chcr = sh_dmae_readl(sh_chan, CHCR);
+ u32 chcr = chcr_read(sh_chan);
if ((chcr & (CHCR_DE | CHCR_TE)) == CHCR_DE)
return true; /* working */
@@ -130,8 +154,7 @@
static unsigned int calc_xmit_shift(struct sh_dmae_chan *sh_chan, u32 chcr)
{
- struct sh_dmae_device *shdev = container_of(sh_chan->common.device,
- struct sh_dmae_device, common);
+ struct sh_dmae_device *shdev = to_sh_dev(sh_chan);
struct sh_dmae_pdata *pdata = shdev->pdata;
int cnt = ((chcr & pdata->ts_low_mask) >> pdata->ts_low_shift) |
((chcr & pdata->ts_high_mask) >> pdata->ts_high_shift);
@@ -144,8 +167,7 @@
static u32 log2size_to_chcr(struct sh_dmae_chan *sh_chan, int l2size)
{
- struct sh_dmae_device *shdev = container_of(sh_chan->common.device,
- struct sh_dmae_device, common);
+ struct sh_dmae_device *shdev = to_sh_dev(sh_chan);
struct sh_dmae_pdata *pdata = shdev->pdata;
int i;
@@ -169,18 +191,23 @@
static void dmae_start(struct sh_dmae_chan *sh_chan)
{
- u32 chcr = sh_dmae_readl(sh_chan, CHCR);
+ struct sh_dmae_device *shdev = to_sh_dev(sh_chan);
+ u32 chcr = chcr_read(sh_chan);
- chcr |= CHCR_DE | CHCR_IE;
- sh_dmae_writel(sh_chan, chcr & ~CHCR_TE, CHCR);
+ if (shdev->pdata->needs_tend_set)
+ sh_dmae_writel(sh_chan, 0xFFFFFFFF, TEND);
+
+ chcr |= CHCR_DE | shdev->chcr_ie_bit;
+ chcr_write(sh_chan, chcr & ~CHCR_TE);
}
static void dmae_halt(struct sh_dmae_chan *sh_chan)
{
- u32 chcr = sh_dmae_readl(sh_chan, CHCR);
+ struct sh_dmae_device *shdev = to_sh_dev(sh_chan);
+ u32 chcr = chcr_read(sh_chan);
- chcr &= ~(CHCR_DE | CHCR_TE | CHCR_IE);
- sh_dmae_writel(sh_chan, chcr, CHCR);
+ chcr &= ~(CHCR_DE | CHCR_TE | shdev->chcr_ie_bit);
+ chcr_write(sh_chan, chcr);
}
static void dmae_init(struct sh_dmae_chan *sh_chan)
@@ -192,7 +219,7 @@
u32 chcr = DM_INC | SM_INC | 0x400 | log2size_to_chcr(sh_chan,
LOG2_DEFAULT_XFER_SIZE);
sh_chan->xmit_shift = calc_xmit_shift(sh_chan, chcr);
- sh_dmae_writel(sh_chan, chcr, CHCR);
+ chcr_write(sh_chan, chcr);
}
static int dmae_set_chcr(struct sh_dmae_chan *sh_chan, u32 val)
@@ -202,23 +229,25 @@
return -EBUSY;
sh_chan->xmit_shift = calc_xmit_shift(sh_chan, val);
- sh_dmae_writel(sh_chan, val, CHCR);
+ chcr_write(sh_chan, val);
return 0;
}
static int dmae_set_dmars(struct sh_dmae_chan *sh_chan, u16 val)
{
- struct sh_dmae_device *shdev = container_of(sh_chan->common.device,
- struct sh_dmae_device, common);
+ struct sh_dmae_device *shdev = to_sh_dev(sh_chan);
struct sh_dmae_pdata *pdata = shdev->pdata;
const struct sh_dmae_channel *chan_pdata = &pdata->channel[sh_chan->id];
u16 __iomem *addr = shdev->dmars;
- int shift = chan_pdata->dmars_bit;
+ unsigned int shift = chan_pdata->dmars_bit;
if (dmae_is_busy(sh_chan))
return -EBUSY;
+ if (pdata->no_dmars)
+ return 0;
+
/* in the case of a missing DMARS resource use first memory window */
if (!addr)
addr = (u16 __iomem *)shdev->chan_reg;
@@ -296,9 +325,7 @@
static const struct sh_dmae_slave_config *sh_dmae_find_slave(
struct sh_dmae_chan *sh_chan, struct sh_dmae_slave *param)
{
- struct dma_device *dma_dev = sh_chan->common.device;
- struct sh_dmae_device *shdev = container_of(dma_dev,
- struct sh_dmae_device, common);
+ struct sh_dmae_device *shdev = to_sh_dev(sh_chan);
struct sh_dmae_pdata *pdata = shdev->pdata;
int i;
@@ -771,10 +798,8 @@
spin_lock_bh(&sh_chan->desc_lock);
/* DMA work check */
- if (dmae_is_busy(sh_chan)) {
- spin_unlock_bh(&sh_chan->desc_lock);
- return;
- }
+ if (dmae_is_busy(sh_chan))
+ goto sh_chan_xfer_ld_queue_end;
/* Find the first not transferred descriptor */
list_for_each_entry(desc, &sh_chan->ld_queue, node)
@@ -788,6 +813,7 @@
break;
}
+sh_chan_xfer_ld_queue_end:
spin_unlock_bh(&sh_chan->desc_lock);
}
@@ -846,7 +872,7 @@
spin_lock(&sh_chan->desc_lock);
- chcr = sh_dmae_readl(sh_chan, CHCR);
+ chcr = chcr_read(sh_chan);
if (chcr & CHCR_TE) {
/* DMA stop */
@@ -1144,6 +1170,16 @@
/* platform data */
shdev->pdata = pdata;
+ if (pdata->chcr_offset)
+ shdev->chcr_offset = pdata->chcr_offset;
+ else
+ shdev->chcr_offset = CHCR;
+
+ if (pdata->chcr_ie_bit)
+ shdev->chcr_ie_bit = pdata->chcr_ie_bit;
+ else
+ shdev->chcr_ie_bit = CHCR_IE;
+
platform_set_drvdata(pdev, shdev);
pm_runtime_enable(&pdev->dev);
diff --git a/drivers/dma/shdma.h b/drivers/dma/shdma.h
index 5ae9fc5..dc56576 100644
--- a/drivers/dma/shdma.h
+++ b/drivers/dma/shdma.h
@@ -47,10 +47,14 @@
struct list_head node;
u32 __iomem *chan_reg;
u16 __iomem *dmars;
+ unsigned int chcr_offset;
+ u32 chcr_ie_bit;
};
#define to_sh_chan(chan) container_of(chan, struct sh_dmae_chan, common)
#define to_sh_desc(lh) container_of(lh, struct sh_desc, node)
#define tx_to_sh_desc(tx) container_of(tx, struct sh_desc, async_tx)
+#define to_sh_dev(chan) container_of(chan->common.device,\
+ struct sh_dmae_device, common)
#endif /* __DMA_SHDMA_H */
diff --git a/drivers/dma/ste_dma40.c b/drivers/dma/ste_dma40.c
index 29d1add..cd3a7c7 100644
--- a/drivers/dma/ste_dma40.c
+++ b/drivers/dma/ste_dma40.c
@@ -14,6 +14,7 @@
#include <linux/clk.h>
#include <linux/delay.h>
#include <linux/err.h>
+#include <linux/amba/bus.h>
#include <plat/ste_dma40.h>
@@ -45,9 +46,6 @@
#define D40_ALLOC_PHY (1 << 30)
#define D40_ALLOC_LOG_FREE 0
-/* Hardware designer of the block */
-#define D40_HW_DESIGNER 0x8
-
/**
* enum 40_command - The different commands and/or statuses.
*
@@ -186,6 +184,8 @@
* @log_def: Default logical channel settings.
* @lcla: Space for one dst src pair for logical channel transfers.
* @lcpa: Pointer to dst and src lcpa settings.
+ * @runtime_addr: runtime configured address.
+ * @runtime_direction: runtime configured direction.
*
* This struct can either "be" a logical or a physical channel.
*/
@@ -200,6 +200,7 @@
struct dma_chan chan;
struct tasklet_struct tasklet;
struct list_head client;
+ struct list_head pending_queue;
struct list_head active;
struct list_head queue;
struct stedma40_chan_cfg dma_cfg;
@@ -645,7 +646,20 @@
static void d40_desc_queue(struct d40_chan *d40c, struct d40_desc *desc)
{
- list_add_tail(&desc->node, &d40c->queue);
+ list_add_tail(&desc->node, &d40c->pending_queue);
+}
+
+static struct d40_desc *d40_first_pending(struct d40_chan *d40c)
+{
+ struct d40_desc *d;
+
+ if (list_empty(&d40c->pending_queue))
+ return NULL;
+
+ d = list_first_entry(&d40c->pending_queue,
+ struct d40_desc,
+ node);
+ return d;
}
static struct d40_desc *d40_first_queued(struct d40_chan *d40c)
@@ -802,6 +816,11 @@
d40_desc_free(d40c, d40d);
}
+ /* Release pending descriptors */
+ while ((d40d = d40_first_pending(d40c))) {
+ d40_desc_remove(d40d);
+ d40_desc_free(d40c, d40d);
+ }
d40c->pending_tx = 0;
d40c->busy = false;
@@ -2092,7 +2111,7 @@
struct scatterlist *sg;
int i;
- sg = kcalloc(periods + 1, sizeof(struct scatterlist), GFP_KERNEL);
+ sg = kcalloc(periods + 1, sizeof(struct scatterlist), GFP_NOWAIT);
for (i = 0; i < periods; i++) {
sg_dma_address(&sg[i]) = dma_addr;
sg_dma_len(&sg[i]) = period_len;
@@ -2152,24 +2171,87 @@
spin_lock_irqsave(&d40c->lock, flags);
- /* Busy means that pending jobs are already being processed */
+ list_splice_tail_init(&d40c->pending_queue, &d40c->queue);
+
+ /* Busy means that queued jobs are already being processed */
if (!d40c->busy)
(void) d40_queue_start(d40c);
spin_unlock_irqrestore(&d40c->lock, flags);
}
+static int
+dma40_config_to_halfchannel(struct d40_chan *d40c,
+ struct stedma40_half_channel_info *info,
+ enum dma_slave_buswidth width,
+ u32 maxburst)
+{
+ enum stedma40_periph_data_width addr_width;
+ int psize;
+
+ switch (width) {
+ case DMA_SLAVE_BUSWIDTH_1_BYTE:
+ addr_width = STEDMA40_BYTE_WIDTH;
+ break;
+ case DMA_SLAVE_BUSWIDTH_2_BYTES:
+ addr_width = STEDMA40_HALFWORD_WIDTH;
+ break;
+ case DMA_SLAVE_BUSWIDTH_4_BYTES:
+ addr_width = STEDMA40_WORD_WIDTH;
+ break;
+ case DMA_SLAVE_BUSWIDTH_8_BYTES:
+ addr_width = STEDMA40_DOUBLEWORD_WIDTH;
+ break;
+ default:
+ dev_err(d40c->base->dev,
+ "illegal peripheral address width "
+ "requested (%d)\n",
+ width);
+ return -EINVAL;
+ }
+
+ if (chan_is_logical(d40c)) {
+ if (maxburst >= 16)
+ psize = STEDMA40_PSIZE_LOG_16;
+ else if (maxburst >= 8)
+ psize = STEDMA40_PSIZE_LOG_8;
+ else if (maxburst >= 4)
+ psize = STEDMA40_PSIZE_LOG_4;
+ else
+ psize = STEDMA40_PSIZE_LOG_1;
+ } else {
+ if (maxburst >= 16)
+ psize = STEDMA40_PSIZE_PHY_16;
+ else if (maxburst >= 8)
+ psize = STEDMA40_PSIZE_PHY_8;
+ else if (maxburst >= 4)
+ psize = STEDMA40_PSIZE_PHY_4;
+ else
+ psize = STEDMA40_PSIZE_PHY_1;
+ }
+
+ info->data_width = addr_width;
+ info->psize = psize;
+ info->flow_ctrl = STEDMA40_NO_FLOW_CTRL;
+
+ return 0;
+}
+
/* Runtime reconfiguration extension */
-static void d40_set_runtime_config(struct dma_chan *chan,
- struct dma_slave_config *config)
+static int d40_set_runtime_config(struct dma_chan *chan,
+ struct dma_slave_config *config)
{
struct d40_chan *d40c = container_of(chan, struct d40_chan, chan);
struct stedma40_chan_cfg *cfg = &d40c->dma_cfg;
- enum dma_slave_buswidth config_addr_width;
+ enum dma_slave_buswidth src_addr_width, dst_addr_width;
dma_addr_t config_addr;
- u32 config_maxburst;
- enum stedma40_periph_data_width addr_width;
- int psize;
+ u32 src_maxburst, dst_maxburst;
+ int ret;
+
+ src_addr_width = config->src_addr_width;
+ src_maxburst = config->src_maxburst;
+ dst_addr_width = config->dst_addr_width;
+ dst_maxburst = config->dst_maxburst;
if (config->direction == DMA_FROM_DEVICE) {
dma_addr_t dev_addr_rx =
@@ -2188,8 +2270,11 @@
cfg->dir);
cfg->dir = STEDMA40_PERIPH_TO_MEM;
- config_addr_width = config->src_addr_width;
- config_maxburst = config->src_maxburst;
+ /* Configure the memory side */
+ if (dst_addr_width == DMA_SLAVE_BUSWIDTH_UNDEFINED)
+ dst_addr_width = src_addr_width;
+ if (dst_maxburst == 0)
+ dst_maxburst = src_maxburst;
} else if (config->direction == DMA_TO_DEVICE) {
dma_addr_t dev_addr_tx =
@@ -2208,68 +2293,39 @@
cfg->dir);
cfg->dir = STEDMA40_MEM_TO_PERIPH;
- config_addr_width = config->dst_addr_width;
- config_maxburst = config->dst_maxburst;
-
+ /* Configure the memory side */
+ if (src_addr_width == DMA_SLAVE_BUSWIDTH_UNDEFINED)
+ src_addr_width = dst_addr_width;
+ if (src_maxburst == 0)
+ src_maxburst = dst_maxburst;
} else {
dev_err(d40c->base->dev,
"unrecognized channel direction %d\n",
config->direction);
- return;
+ return -EINVAL;
}
- switch (config_addr_width) {
- case DMA_SLAVE_BUSWIDTH_1_BYTE:
- addr_width = STEDMA40_BYTE_WIDTH;
- break;
- case DMA_SLAVE_BUSWIDTH_2_BYTES:
- addr_width = STEDMA40_HALFWORD_WIDTH;
- break;
- case DMA_SLAVE_BUSWIDTH_4_BYTES:
- addr_width = STEDMA40_WORD_WIDTH;
- break;
- case DMA_SLAVE_BUSWIDTH_8_BYTES:
- addr_width = STEDMA40_DOUBLEWORD_WIDTH;
- break;
- default:
+ if (src_maxburst * src_addr_width != dst_maxburst * dst_addr_width) {
dev_err(d40c->base->dev,
- "illegal peripheral address width "
- "requested (%d)\n",
- config->src_addr_width);
- return;
+ "src/dst width/maxburst mismatch: %d*%d != %d*%d\n",
+ src_maxburst,
+ src_addr_width,
+ dst_maxburst,
+ dst_addr_width);
+ return -EINVAL;
}
- if (chan_is_logical(d40c)) {
- if (config_maxburst >= 16)
- psize = STEDMA40_PSIZE_LOG_16;
- else if (config_maxburst >= 8)
- psize = STEDMA40_PSIZE_LOG_8;
- else if (config_maxburst >= 4)
- psize = STEDMA40_PSIZE_LOG_4;
- else
- psize = STEDMA40_PSIZE_LOG_1;
- } else {
- if (config_maxburst >= 16)
- psize = STEDMA40_PSIZE_PHY_16;
- else if (config_maxburst >= 8)
- psize = STEDMA40_PSIZE_PHY_8;
- else if (config_maxburst >= 4)
- psize = STEDMA40_PSIZE_PHY_4;
- else if (config_maxburst >= 2)
- psize = STEDMA40_PSIZE_PHY_2;
- else
- psize = STEDMA40_PSIZE_PHY_1;
- }
+ ret = dma40_config_to_halfchannel(d40c, &cfg->src_info,
+ src_addr_width,
+ src_maxburst);
+ if (ret)
+ return ret;
- /* Set up all the endpoint configs */
- cfg->src_info.data_width = addr_width;
- cfg->src_info.psize = psize;
- cfg->src_info.big_endian = false;
- cfg->src_info.flow_ctrl = STEDMA40_NO_FLOW_CTRL;
- cfg->dst_info.data_width = addr_width;
- cfg->dst_info.psize = psize;
- cfg->dst_info.big_endian = false;
- cfg->dst_info.flow_ctrl = STEDMA40_NO_FLOW_CTRL;
+ ret = dma40_config_to_halfchannel(d40c, &cfg->dst_info,
+ dst_addr_width,
+ dst_maxburst);
+ if (ret)
+ return ret;
/* Fill in register values */
if (chan_is_logical(d40c))
@@ -2282,12 +2338,14 @@
d40c->runtime_addr = config_addr;
d40c->runtime_direction = config->direction;
dev_dbg(d40c->base->dev,
- "configured channel %s for %s, data width %d, "
- "maxburst %d bytes, LE, no flow control\n",
+ "configured channel %s for %s, data width %d/%d, "
+ "maxburst %d/%d elements, LE, no flow control\n",
dma_chan_name(chan),
(config->direction == DMA_FROM_DEVICE) ? "RX" : "TX",
- config_addr_width,
- config_maxburst);
+ src_addr_width, dst_addr_width,
+ src_maxburst, dst_maxburst);
+
+ return 0;
}
static int d40_control(struct dma_chan *chan, enum dma_ctrl_cmd cmd,
@@ -2308,9 +2366,8 @@
case DMA_RESUME:
return d40_resume(d40c);
case DMA_SLAVE_CONFIG:
- d40_set_runtime_config(chan,
+ return d40_set_runtime_config(chan,
(struct dma_slave_config *) arg);
- return 0;
default:
break;
}
@@ -2341,6 +2398,7 @@
INIT_LIST_HEAD(&d40c->active);
INIT_LIST_HEAD(&d40c->queue);
+ INIT_LIST_HEAD(&d40c->pending_queue);
INIT_LIST_HEAD(&d40c->client);
tasklet_init(&d40c->tasklet, dma_tasklet,
@@ -2502,25 +2560,6 @@
static struct d40_base * __init d40_hw_detect_init(struct platform_device *pdev)
{
- static const struct d40_reg_val dma_id_regs[] = {
- /* Peripheral Id */
- { .reg = D40_DREG_PERIPHID0, .val = 0x0040},
- { .reg = D40_DREG_PERIPHID1, .val = 0x0000},
- /*
- * D40_DREG_PERIPHID2 Depends on HW revision:
- * DB8500ed has 0x0008,
- * ? has 0x0018,
- * DB8500v1 has 0x0028
- * DB8500v2 has 0x0038
- */
- { .reg = D40_DREG_PERIPHID3, .val = 0x0000},
-
- /* PCell Id */
- { .reg = D40_DREG_CELLID0, .val = 0x000d},
- { .reg = D40_DREG_CELLID1, .val = 0x00f0},
- { .reg = D40_DREG_CELLID2, .val = 0x0005},
- { .reg = D40_DREG_CELLID3, .val = 0x00b1}
- };
struct stedma40_platform_data *plat_data;
struct clk *clk = NULL;
void __iomem *virtbase = NULL;
@@ -2529,8 +2568,9 @@
int num_log_chans = 0;
int num_phy_chans;
int i;
- u32 val;
- u32 rev;
+ u32 pid;
+ u32 cid;
+ u8 rev;
clk = clk_get(&pdev->dev, NULL);
@@ -2554,32 +2594,32 @@
if (!virtbase)
goto failure;
- /* HW version check */
- for (i = 0; i < ARRAY_SIZE(dma_id_regs); i++) {
- if (dma_id_regs[i].val !=
- readl(virtbase + dma_id_regs[i].reg)) {
- d40_err(&pdev->dev,
- "Unknown hardware! Expected 0x%x at 0x%x but got 0x%x\n",
- dma_id_regs[i].val,
- dma_id_regs[i].reg,
- readl(virtbase + dma_id_regs[i].reg));
- goto failure;
- }
- }
+ /* This is just a regular AMBA PrimeCell ID actually */
+ for (pid = 0, i = 0; i < 4; i++)
+ pid |= (readl(virtbase + resource_size(res) - 0x20 + 4 * i)
+ & 255) << (i * 8);
+ for (cid = 0, i = 0; i < 4; i++)
+ cid |= (readl(virtbase + resource_size(res) - 0x10 + 4 * i)
+ & 255) << (i * 8);
- /* Get silicon revision and designer */
- val = readl(virtbase + D40_DREG_PERIPHID2);
-
- if ((val & D40_DREG_PERIPHID2_DESIGNER_MASK) !=
- D40_HW_DESIGNER) {
- d40_err(&pdev->dev, "Unknown designer! Got %x wanted %x\n",
- val & D40_DREG_PERIPHID2_DESIGNER_MASK,
- D40_HW_DESIGNER);
+ if (cid != AMBA_CID) {
+ d40_err(&pdev->dev, "Unknown hardware! No PrimeCell ID\n");
goto failure;
}
-
- rev = (val & D40_DREG_PERIPHID2_REV_MASK) >>
- D40_DREG_PERIPHID2_REV_POS;
+ if (AMBA_MANF_BITS(pid) != AMBA_VENDOR_ST) {
+ d40_err(&pdev->dev, "Unknown designer! Got %x wanted %x\n",
+ AMBA_MANF_BITS(pid),
+ AMBA_VENDOR_ST);
+ goto failure;
+ }
+ /*
+ * HW revision:
+ * DB8500ed has revision 0
+ * ? has revision 1
+ * DB8500v1 has revision 2
+ * DB8500v2 has revision 3
+ */
+ rev = AMBA_REV_BITS(pid);
/* The number of physical channels on this HW */
num_phy_chans = 4 * (readl(virtbase + D40_DREG_ICFG) & 0x7) + 4;
diff --git a/drivers/dma/ste_dma40_ll.h b/drivers/dma/ste_dma40_ll.h
index 195ee65..b44c4551 100644
--- a/drivers/dma/ste_dma40_ll.h
+++ b/drivers/dma/ste_dma40_ll.h
@@ -184,9 +184,6 @@
#define D40_DREG_PERIPHID0 0xFE0
#define D40_DREG_PERIPHID1 0xFE4
#define D40_DREG_PERIPHID2 0xFE8
-#define D40_DREG_PERIPHID2_REV_POS 4
-#define D40_DREG_PERIPHID2_REV_MASK (0xf << D40_DREG_PERIPHID2_REV_POS)
-#define D40_DREG_PERIPHID2_DESIGNER_MASK 0xf
#define D40_DREG_PERIPHID3 0xFEC
#define D40_DREG_CELLID0 0xFF0
#define D40_DREG_CELLID1 0xFF4
diff --git a/drivers/edac/i7core_edac.c b/drivers/edac/i7core_edac.c
index 04f1e7c..f6cf448 100644
--- a/drivers/edac/i7core_edac.c
+++ b/drivers/edac/i7core_edac.c
@@ -1670,7 +1670,7 @@
char *type, *optype, *err, *msg;
unsigned long error = m->status & 0x1ff0000l;
u32 optypenum = (m->status >> 4) & 0x07;
- u32 core_err_cnt = (m->status >> 38) && 0x7fff;
+ u32 core_err_cnt = (m->status >> 38) & 0x7fff;
u32 dimm = (m->misc >> 16) & 0x3;
u32 channel = (m->misc >> 18) & 0x3;
u32 syndrome = m->misc >> 32;
diff --git a/drivers/eisa/pci_eisa.c b/drivers/eisa/pci_eisa.c
index 30da70d..cdae207 100644
--- a/drivers/eisa/pci_eisa.c
+++ b/drivers/eisa/pci_eisa.c
@@ -45,13 +45,13 @@
return 0;
}
-static struct pci_device_id __initdata pci_eisa_pci_tbl[] = {
+static struct pci_device_id pci_eisa_pci_tbl[] = {
{ PCI_ANY_ID, PCI_ANY_ID, PCI_ANY_ID, PCI_ANY_ID,
PCI_CLASS_BRIDGE_EISA << 8, 0xffff00, 0 },
{ 0, }
};
-static struct pci_driver __initdata pci_eisa_driver = {
+static struct pci_driver __refdata pci_eisa_driver = {
.name = "pci_eisa",
.id_table = pci_eisa_pci_tbl,
.probe = pci_eisa_init,
diff --git a/drivers/firewire/core-cdev.c b/drivers/firewire/core-cdev.c
index e6ad3bb..4799393 100644
--- a/drivers/firewire/core-cdev.c
+++ b/drivers/firewire/core-cdev.c
@@ -216,15 +216,33 @@
struct fw_cdev_event_phy_packet phy_packet;
};
-static inline void __user *u64_to_uptr(__u64 value)
+#ifdef CONFIG_COMPAT
+static void __user *u64_to_uptr(u64 value)
+{
+ if (is_compat_task())
+ return compat_ptr(value);
+ else
+ return (void __user *)(unsigned long)value;
+}
+
+static u64 uptr_to_u64(void __user *ptr)
+{
+ if (is_compat_task())
+ return ptr_to_compat(ptr);
+ else
+ return (u64)(unsigned long)ptr;
+}
+#else
+static inline void __user *u64_to_uptr(u64 value)
{
return (void __user *)(unsigned long)value;
}
-static inline __u64 uptr_to_u64(void __user *ptr)
+static inline u64 uptr_to_u64(void __user *ptr)
{
- return (__u64)(unsigned long)ptr;
+ return (u64)(unsigned long)ptr;
}
+#endif /* CONFIG_COMPAT */
static int fw_device_op_open(struct inode *inode, struct file *file)
{
diff --git a/drivers/firewire/ohci.c b/drivers/firewire/ohci.c
index bcf792f..57cd3a4 100644
--- a/drivers/firewire/ohci.c
+++ b/drivers/firewire/ohci.c
@@ -2179,8 +2179,13 @@
ohci_driver_name, ohci)) {
fw_error("Failed to allocate interrupt %d.\n", dev->irq);
pci_disable_msi(dev);
- dma_free_coherent(ohci->card.device, CONFIG_ROM_SIZE,
- ohci->config_rom, ohci->config_rom_bus);
+
+ if (config_rom) {
+ dma_free_coherent(ohci->card.device, CONFIG_ROM_SIZE,
+ ohci->next_config_rom,
+ ohci->next_config_rom_bus);
+ ohci->next_config_rom = NULL;
+ }
return -EIO;
}
diff --git a/drivers/firmware/efivars.c b/drivers/firmware/efivars.c
index 5f29aaf..eb80b54 100644
--- a/drivers/firmware/efivars.c
+++ b/drivers/firmware/efivars.c
@@ -78,6 +78,7 @@
#include <linux/kobject.h>
#include <linux/device.h>
#include <linux/slab.h>
+#include <linux/pstore.h>
#include <asm/uaccess.h>
@@ -89,6 +90,8 @@
MODULE_LICENSE("GPL");
MODULE_VERSION(EFIVARS_VERSION);
+#define DUMP_NAME_LEN 52
+
/*
* The maximum size of VariableName + Data = 1024
* Therefore, it's reasonable to save that much
@@ -119,6 +122,10 @@
ssize_t (*store)(struct efivar_entry *entry, const char *buf, size_t count);
};
+#define PSTORE_EFI_ATTRIBUTES \
+ (EFI_VARIABLE_NON_VOLATILE | \
+ EFI_VARIABLE_BOOTSERVICE_ACCESS | \
+ EFI_VARIABLE_RUNTIME_ACCESS)
#define EFIVAR_ATTR(_name, _mode, _show, _store) \
struct efivar_attribute efivar_attr_##_name = { \
@@ -141,23 +148,61 @@
/* Return the number of unicode characters in data */
static unsigned long
-utf8_strlen(efi_char16_t *data, unsigned long maxlength)
+utf16_strnlen(efi_char16_t *s, size_t maxlength)
{
unsigned long length = 0;
- while (*data++ != 0 && length < maxlength)
+ while (*s++ != 0 && length < maxlength)
length++;
return length;
}
+static inline unsigned long
+utf16_strlen(efi_char16_t *s)
+{
+ return utf16_strnlen(s, ~0UL);
+}
+
/*
* Return the number of bytes is the length of this string
* Note: this is NOT the same as the number of unicode characters
*/
static inline unsigned long
-utf8_strsize(efi_char16_t *data, unsigned long maxlength)
+utf16_strsize(efi_char16_t *data, unsigned long maxlength)
{
- return utf8_strlen(data, maxlength/sizeof(efi_char16_t)) * sizeof(efi_char16_t);
+ return utf16_strnlen(data, maxlength/sizeof(efi_char16_t)) * sizeof(efi_char16_t);
+}
+
+static inline int
+utf16_strncmp(const efi_char16_t *a, const efi_char16_t *b, size_t len)
+{
+ while (1) {
+ if (len == 0)
+ return 0;
+ if (*a < *b)
+ return -1;
+ if (*a > *b)
+ return 1;
+ if (*a == 0) /* implies *b == 0 */
+ return 0;
+ a++;
+ b++;
+ len--;
+ }
+}
+
+static efi_status_t
+get_var_data_locked(struct efivars *efivars, struct efi_variable *var)
+{
+ efi_status_t status;
+
+ var->DataSize = 1024;
+ status = efivars->ops->get_variable(var->VariableName,
+ &var->VendorGuid,
+ &var->Attributes,
+ &var->DataSize,
+ var->Data);
+ return status;
}
static efi_status_t
@@ -166,13 +211,9 @@
efi_status_t status;
spin_lock(&efivars->lock);
- var->DataSize = 1024;
- status = efivars->ops->get_variable(var->VariableName,
- &var->VendorGuid,
- &var->Attributes,
- &var->DataSize,
- var->Data);
+ status = get_var_data_locked(efivars, var);
spin_unlock(&efivars->lock);
+
if (status != EFI_SUCCESS) {
printk(KERN_WARNING "efivars: get_variable() failed 0x%lx!\n",
status);
@@ -387,12 +428,180 @@
.default_attrs = def_attrs,
};
+static struct pstore_info efi_pstore_info;
+
static inline void
efivar_unregister(struct efivar_entry *var)
{
kobject_put(&var->kobj);
}
+#ifdef CONFIG_PSTORE
+
+static int efi_pstore_open(struct pstore_info *psi)
+{
+ struct efivars *efivars = psi->data;
+
+ spin_lock(&efivars->lock);
+ efivars->walk_entry = list_first_entry(&efivars->list,
+ struct efivar_entry, list);
+ return 0;
+}
+
+static int efi_pstore_close(struct pstore_info *psi)
+{
+ struct efivars *efivars = psi->data;
+
+ spin_unlock(&efivars->lock);
+ return 0;
+}
+
+static ssize_t efi_pstore_read(u64 *id, enum pstore_type_id *type,
+ struct timespec *timespec, struct pstore_info *psi)
+{
+ efi_guid_t vendor = LINUX_EFI_CRASH_GUID;
+ struct efivars *efivars = psi->data;
+ char name[DUMP_NAME_LEN];
+ int i;
+ unsigned int part, size;
+ unsigned long time;
+
+ while (&efivars->walk_entry->list != &efivars->list) {
+ if (!efi_guidcmp(efivars->walk_entry->var.VendorGuid,
+ vendor)) {
+ for (i = 0; i < DUMP_NAME_LEN; i++) {
+ name[i] = efivars->walk_entry->var.VariableName[i];
+ }
+ if (sscanf(name, "dump-type%u-%u-%lu", type, &part, &time) == 3) {
+ *id = part;
+ timespec->tv_sec = time;
+ timespec->tv_nsec = 0;
+ get_var_data_locked(efivars, &efivars->walk_entry->var);
+ size = efivars->walk_entry->var.DataSize;
+ memcpy(psi->buf, efivars->walk_entry->var.Data, size);
+ efivars->walk_entry = list_entry(efivars->walk_entry->list.next,
+ struct efivar_entry, list);
+ return size;
+ }
+ }
+ efivars->walk_entry = list_entry(efivars->walk_entry->list.next,
+ struct efivar_entry, list);
+ }
+ return 0;
+}
+
+static u64 efi_pstore_write(enum pstore_type_id type, unsigned int part,
+ size_t size, struct pstore_info *psi)
+{
+ char name[DUMP_NAME_LEN];
+ char stub_name[DUMP_NAME_LEN];
+ efi_char16_t efi_name[DUMP_NAME_LEN];
+ efi_guid_t vendor = LINUX_EFI_CRASH_GUID;
+ struct efivars *efivars = psi->data;
+ struct efivar_entry *entry, *found = NULL;
+ int i;
+
+ sprintf(stub_name, "dump-type%u-%u-", type, part);
+ sprintf(name, "%s%lu", stub_name, get_seconds());
+
+ spin_lock(&efivars->lock);
+
+ for (i = 0; i < DUMP_NAME_LEN; i++)
+ efi_name[i] = stub_name[i];
+
+ /*
+ * Clean up any entries with the same name
+ */
+
+ list_for_each_entry(entry, &efivars->list, list) {
+ get_var_data_locked(efivars, &entry->var);
+
+ if (efi_guidcmp(entry->var.VendorGuid, vendor))
+ continue;
+ if (utf16_strncmp(entry->var.VariableName, efi_name,
+ utf16_strlen(efi_name)))
+ continue;
+ /* Needs to be a prefix */
+ if (entry->var.VariableName[utf16_strlen(efi_name)] == 0)
+ continue;
+
+ /* found */
+ found = entry;
+ efivars->ops->set_variable(entry->var.VariableName,
+ &entry->var.VendorGuid,
+ PSTORE_EFI_ATTRIBUTES,
+ 0, NULL);
+ }
+
+ if (found)
+ list_del(&found->list);
+
+ for (i = 0; i < DUMP_NAME_LEN; i++)
+ efi_name[i] = name[i];
+
+ efivars->ops->set_variable(efi_name, &vendor, PSTORE_EFI_ATTRIBUTES,
+ size, psi->buf);
+
+ spin_unlock(&efivars->lock);
+
+ if (found)
+ efivar_unregister(found);
+
+ if (size)
+ efivar_create_sysfs_entry(efivars,
+ utf16_strsize(efi_name,
+ DUMP_NAME_LEN * 2),
+ efi_name, &vendor);
+
+ return part;
+};
+
+static int efi_pstore_erase(enum pstore_type_id type, u64 id,
+ struct pstore_info *psi)
+{
+ efi_pstore_write(type, id, 0, psi);
+
+ return 0;
+}
+#else
+static int efi_pstore_open(struct pstore_info *psi)
+{
+ return 0;
+}
+
+static int efi_pstore_close(struct pstore_info *psi)
+{
+ return 0;
+}
+
+static ssize_t efi_pstore_read(u64 *id, enum pstore_type_id *type,
+ struct timespec *time, struct pstore_info *psi)
+{
+ return -1;
+}
+
+static u64 efi_pstore_write(enum pstore_type_id type, unsigned int part,
+ size_t size, struct pstore_info *psi)
+{
+ return 0;
+}
+
+static int efi_pstore_erase(enum pstore_type_id type, u64 id,
+ struct pstore_info *psi)
+{
+ return 0;
+}
+#endif
+
+static struct pstore_info efi_pstore_info = {
+ .owner = THIS_MODULE,
+ .name = "efi",
+ .open = efi_pstore_open,
+ .close = efi_pstore_close,
+ .read = efi_pstore_read,
+ .write = efi_pstore_write,
+ .erase = efi_pstore_erase,
+};
static ssize_t efivar_create(struct file *filp, struct kobject *kobj,
struct bin_attribute *bin_attr,
@@ -414,8 +623,8 @@
* Does this variable already exist?
*/
list_for_each_entry_safe(search_efivar, n, &efivars->list, list) {
- strsize1 = utf8_strsize(search_efivar->var.VariableName, 1024);
- strsize2 = utf8_strsize(new_var->VariableName, 1024);
+ strsize1 = utf16_strsize(search_efivar->var.VariableName, 1024);
+ strsize2 = utf16_strsize(new_var->VariableName, 1024);
if (strsize1 == strsize2 &&
!memcmp(&(search_efivar->var.VariableName),
new_var->VariableName, strsize1) &&
@@ -447,8 +656,8 @@
/* Create the entry in sysfs. Locking is not required here */
status = efivar_create_sysfs_entry(efivars,
- utf8_strsize(new_var->VariableName,
- 1024),
+ utf16_strsize(new_var->VariableName,
+ 1024),
new_var->VariableName,
&new_var->VendorGuid);
if (status) {
@@ -477,8 +686,8 @@
* Does this variable already exist?
*/
list_for_each_entry_safe(search_efivar, n, &efivars->list, list) {
- strsize1 = utf8_strsize(search_efivar->var.VariableName, 1024);
- strsize2 = utf8_strsize(del_var->VariableName, 1024);
+ strsize1 = utf16_strsize(search_efivar->var.VariableName, 1024);
+ strsize2 = utf16_strsize(del_var->VariableName, 1024);
if (strsize1 == strsize2 &&
!memcmp(&(search_efivar->var.VariableName),
del_var->VariableName, strsize1) &&
@@ -763,6 +972,16 @@
if (error)
unregister_efivars(efivars);
+ efivars->efi_pstore_info = efi_pstore_info;
+
+ efivars->efi_pstore_info.buf = kmalloc(4096, GFP_KERNEL);
+ if (efivars->efi_pstore_info.buf) {
+ efivars->efi_pstore_info.bufsize = 1024;
+ efivars->efi_pstore_info.data = efivars;
+ mutex_init(&efivars->efi_pstore_info.buf_mutex);
+ pstore_register(&efivars->efi_pstore_info);
+ }
+
out:
kfree(variable_name);
diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig
index 3634986..d539efd 100644
--- a/drivers/gpio/Kconfig
+++ b/drivers/gpio/Kconfig
@@ -103,6 +103,22 @@
def_bool y
depends on PPC_MPC52xx
+config GPIO_MSM_V1
+ tristate "Qualcomm MSM GPIO v1"
+ depends on GPIOLIB && ARCH_MSM
+ help
+ Say yes here to support the GPIO interface on ARM v6 based
+ Qualcomm MSM chips. Most of the pins on the MSM can be
+ selected for GPIO, and are controlled by this driver.
+
+config GPIO_MSM_V2
+ tristate "Qualcomm MSM GPIO v2"
+ depends on GPIOLIB && ARCH_MSM
+ help
+ Say yes here to support the GPIO interface on ARM v7 based
+ Qualcomm MSM chips. Most of the pins on the MSM can be
+ selected for GPIO, and are controlled by this driver.
+
config GPIO_MXC
def_bool y
depends on ARCH_MXC
@@ -280,6 +296,12 @@
This enables support for the GPIOs found on the TC3589X
I/O Expander.
+config GPIO_TPS65912
+ tristate "TI TPS65912 GPIO"
+ depends on (MFD_TPS65912_I2C || MFD_TPS65912_SPI)
+ help
+ This driver supports TPS65912 gpio chip
+
config GPIO_TWL4030
tristate "TWL4030, TWL5030, and TPS659x0 GPIOs"
depends on TWL4030_CORE
diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile
index 7207112..9588948 100644
--- a/drivers/gpio/Makefile
+++ b/drivers/gpio/Makefile
@@ -27,6 +27,8 @@
obj-$(CONFIG_GPIO_MCP23S08) += gpio-mcp23s08.o
obj-$(CONFIG_GPIO_ML_IOH) += gpio-ml-ioh.o
obj-$(CONFIG_GPIO_MPC5200) += gpio-mpc5200.o
+obj-$(CONFIG_GPIO_MSM_V1) += gpio-msm-v1.o
+obj-$(CONFIG_GPIO_MSM_V2) += gpio-msm-v2.o
obj-$(CONFIG_GPIO_MXC) += gpio-mxc.o
obj-$(CONFIG_GPIO_MXS) += gpio-mxs.o
obj-$(CONFIG_PLAT_NOMADIK) += gpio-nomadik.o
@@ -48,6 +50,7 @@
obj-$(CONFIG_ARCH_TEGRA) += gpio-tegra.o
obj-$(CONFIG_GPIO_TIMBERDALE) += gpio-timberdale.o
obj-$(CONFIG_GPIO_TPS65910) += gpio-tps65910.o
+obj-$(CONFIG_GPIO_TPS65912) += gpio-tps65912.o
obj-$(CONFIG_GPIO_TWL4030) += gpio-twl4030.o
obj-$(CONFIG_MACH_U300) += gpio-u300.o
obj-$(CONFIG_GPIO_UCB1400) += gpio-ucb1400.o
diff --git a/drivers/gpio/gpio-ab8500.c b/drivers/gpio/gpio-ab8500.c
index ed795e6..050c05d 100644
--- a/drivers/gpio/gpio-ab8500.c
+++ b/drivers/gpio/gpio-ab8500.c
@@ -516,5 +516,5 @@
MODULE_AUTHOR("BIBEK BASU <bibek.basu@stericsson.com>");
MODULE_DESCRIPTION("Driver allows to use AB8500 unused pins to be used as GPIO");
-MODULE_ALIAS("AB8500 GPIO driver");
+MODULE_ALIAS("platform:ab8500-gpio");
MODULE_LICENSE("GPL v2");
diff --git a/drivers/gpio/gpio-msm-v1.c b/drivers/gpio/gpio-msm-v1.c
new file mode 100644
index 0000000..52a4d42
--- /dev/null
+++ b/drivers/gpio/gpio-msm-v1.c
@@ -0,0 +1,636 @@
+/*
+ * Copyright (C) 2007 Google, Inc.
+ * Copyright (c) 2009-2011, Code Aurora Forum. All rights reserved.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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.
+ *
+ */
+
+#include <linux/bitops.h>
+#include <linux/gpio.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/irq.h>
+#include <linux/module.h>
+#include <mach/cpu.h>
+#include <mach/msm_gpiomux.h>
+#include <mach/msm_iomap.h>
+
+/* see 80-VA736-2 Rev C pp 695-751
+**
+** These are actually the *shadow* gpio registers, since the
+** real ones (which allow full access) are only available to the
+** ARM9 side of the world.
+**
+** Since the _BASE need to be page-aligned when we're mapping them
+** to virtual addresses, adjust for the additional offset in these
+** macros.
+*/
+
+#define MSM_GPIO1_REG(off) (MSM_GPIO1_BASE + (off))
+#define MSM_GPIO2_REG(off) (MSM_GPIO2_BASE + 0x400 + (off))
+#define MSM_GPIO1_SHADOW_REG(off) (MSM_GPIO1_BASE + 0x800 + (off))
+#define MSM_GPIO2_SHADOW_REG(off) (MSM_GPIO2_BASE + 0xC00 + (off))
+
+/*
+ * MSM7X00 registers
+ */
+/* output value */
+#define MSM7X00_GPIO_OUT_0 MSM_GPIO1_SHADOW_REG(0x00) /* gpio 15-0 */
+#define MSM7X00_GPIO_OUT_1 MSM_GPIO2_SHADOW_REG(0x00) /* gpio 42-16 */
+#define MSM7X00_GPIO_OUT_2 MSM_GPIO1_SHADOW_REG(0x04) /* gpio 67-43 */
+#define MSM7X00_GPIO_OUT_3 MSM_GPIO1_SHADOW_REG(0x08) /* gpio 94-68 */
+#define MSM7X00_GPIO_OUT_4 MSM_GPIO1_SHADOW_REG(0x0C) /* gpio 106-95 */
+#define MSM7X00_GPIO_OUT_5 MSM_GPIO1_SHADOW_REG(0x50) /* gpio 107-121 */
+
+/* same pin map as above, output enable */
+#define MSM7X00_GPIO_OE_0 MSM_GPIO1_SHADOW_REG(0x10)
+#define MSM7X00_GPIO_OE_1 MSM_GPIO2_SHADOW_REG(0x08)
+#define MSM7X00_GPIO_OE_2 MSM_GPIO1_SHADOW_REG(0x14)
+#define MSM7X00_GPIO_OE_3 MSM_GPIO1_SHADOW_REG(0x18)
+#define MSM7X00_GPIO_OE_4 MSM_GPIO1_SHADOW_REG(0x1C)
+#define MSM7X00_GPIO_OE_5 MSM_GPIO1_SHADOW_REG(0x54)
+
+/* same pin map as above, input read */
+#define MSM7X00_GPIO_IN_0 MSM_GPIO1_SHADOW_REG(0x34)
+#define MSM7X00_GPIO_IN_1 MSM_GPIO2_SHADOW_REG(0x20)
+#define MSM7X00_GPIO_IN_2 MSM_GPIO1_SHADOW_REG(0x38)
+#define MSM7X00_GPIO_IN_3 MSM_GPIO1_SHADOW_REG(0x3C)
+#define MSM7X00_GPIO_IN_4 MSM_GPIO1_SHADOW_REG(0x40)
+#define MSM7X00_GPIO_IN_5 MSM_GPIO1_SHADOW_REG(0x44)
+
+/* same pin map as above, 1=edge 0=level interrup */
+#define MSM7X00_GPIO_INT_EDGE_0 MSM_GPIO1_SHADOW_REG(0x60)
+#define MSM7X00_GPIO_INT_EDGE_1 MSM_GPIO2_SHADOW_REG(0x50)
+#define MSM7X00_GPIO_INT_EDGE_2 MSM_GPIO1_SHADOW_REG(0x64)
+#define MSM7X00_GPIO_INT_EDGE_3 MSM_GPIO1_SHADOW_REG(0x68)
+#define MSM7X00_GPIO_INT_EDGE_4 MSM_GPIO1_SHADOW_REG(0x6C)
+#define MSM7X00_GPIO_INT_EDGE_5 MSM_GPIO1_SHADOW_REG(0xC0)
+
+/* same pin map as above, 1=positive 0=negative */
+#define MSM7X00_GPIO_INT_POS_0 MSM_GPIO1_SHADOW_REG(0x70)
+#define MSM7X00_GPIO_INT_POS_1 MSM_GPIO2_SHADOW_REG(0x58)
+#define MSM7X00_GPIO_INT_POS_2 MSM_GPIO1_SHADOW_REG(0x74)
+#define MSM7X00_GPIO_INT_POS_3 MSM_GPIO1_SHADOW_REG(0x78)
+#define MSM7X00_GPIO_INT_POS_4 MSM_GPIO1_SHADOW_REG(0x7C)
+#define MSM7X00_GPIO_INT_POS_5 MSM_GPIO1_SHADOW_REG(0xBC)
+
+/* same pin map as above, interrupt enable */
+#define MSM7X00_GPIO_INT_EN_0 MSM_GPIO1_SHADOW_REG(0x80)
+#define MSM7X00_GPIO_INT_EN_1 MSM_GPIO2_SHADOW_REG(0x60)
+#define MSM7X00_GPIO_INT_EN_2 MSM_GPIO1_SHADOW_REG(0x84)
+#define MSM7X00_GPIO_INT_EN_3 MSM_GPIO1_SHADOW_REG(0x88)
+#define MSM7X00_GPIO_INT_EN_4 MSM_GPIO1_SHADOW_REG(0x8C)
+#define MSM7X00_GPIO_INT_EN_5 MSM_GPIO1_SHADOW_REG(0xB8)
+
+/* same pin map as above, write 1 to clear interrupt */
+#define MSM7X00_GPIO_INT_CLEAR_0 MSM_GPIO1_SHADOW_REG(0x90)
+#define MSM7X00_GPIO_INT_CLEAR_1 MSM_GPIO2_SHADOW_REG(0x68)
+#define MSM7X00_GPIO_INT_CLEAR_2 MSM_GPIO1_SHADOW_REG(0x94)
+#define MSM7X00_GPIO_INT_CLEAR_3 MSM_GPIO1_SHADOW_REG(0x98)
+#define MSM7X00_GPIO_INT_CLEAR_4 MSM_GPIO1_SHADOW_REG(0x9C)
+#define MSM7X00_GPIO_INT_CLEAR_5 MSM_GPIO1_SHADOW_REG(0xB4)
+
+/* same pin map as above, 1=interrupt pending */
+#define MSM7X00_GPIO_INT_STATUS_0 MSM_GPIO1_SHADOW_REG(0xA0)
+#define MSM7X00_GPIO_INT_STATUS_1 MSM_GPIO2_SHADOW_REG(0x70)
+#define MSM7X00_GPIO_INT_STATUS_2 MSM_GPIO1_SHADOW_REG(0xA4)
+#define MSM7X00_GPIO_INT_STATUS_3 MSM_GPIO1_SHADOW_REG(0xA8)
+#define MSM7X00_GPIO_INT_STATUS_4 MSM_GPIO1_SHADOW_REG(0xAC)
+#define MSM7X00_GPIO_INT_STATUS_5 MSM_GPIO1_SHADOW_REG(0xB0)
+
+/*
+ * QSD8X50 registers
+ */
+/* output value */
+#define QSD8X50_GPIO_OUT_0 MSM_GPIO1_SHADOW_REG(0x00) /* gpio 15-0 */
+#define QSD8X50_GPIO_OUT_1 MSM_GPIO2_SHADOW_REG(0x00) /* gpio 42-16 */
+#define QSD8X50_GPIO_OUT_2 MSM_GPIO1_SHADOW_REG(0x04) /* gpio 67-43 */
+#define QSD8X50_GPIO_OUT_3 MSM_GPIO1_SHADOW_REG(0x08) /* gpio 94-68 */
+#define QSD8X50_GPIO_OUT_4 MSM_GPIO1_SHADOW_REG(0x0C) /* gpio 103-95 */
+#define QSD8X50_GPIO_OUT_5 MSM_GPIO1_SHADOW_REG(0x10) /* gpio 121-104 */
+#define QSD8X50_GPIO_OUT_6 MSM_GPIO1_SHADOW_REG(0x14) /* gpio 152-122 */
+#define QSD8X50_GPIO_OUT_7 MSM_GPIO1_SHADOW_REG(0x18) /* gpio 164-153 */
+
+/* same pin map as above, output enable */
+#define QSD8X50_GPIO_OE_0 MSM_GPIO1_SHADOW_REG(0x20)
+#define QSD8X50_GPIO_OE_1 MSM_GPIO2_SHADOW_REG(0x08)
+#define QSD8X50_GPIO_OE_2 MSM_GPIO1_SHADOW_REG(0x24)
+#define QSD8X50_GPIO_OE_3 MSM_GPIO1_SHADOW_REG(0x28)
+#define QSD8X50_GPIO_OE_4 MSM_GPIO1_SHADOW_REG(0x2C)
+#define QSD8X50_GPIO_OE_5 MSM_GPIO1_SHADOW_REG(0x30)
+#define QSD8X50_GPIO_OE_6 MSM_GPIO1_SHADOW_REG(0x34)
+#define QSD8X50_GPIO_OE_7 MSM_GPIO1_SHADOW_REG(0x38)
+
+/* same pin map as above, input read */
+#define QSD8X50_GPIO_IN_0 MSM_GPIO1_SHADOW_REG(0x50)
+#define QSD8X50_GPIO_IN_1 MSM_GPIO2_SHADOW_REG(0x20)
+#define QSD8X50_GPIO_IN_2 MSM_GPIO1_SHADOW_REG(0x54)
+#define QSD8X50_GPIO_IN_3 MSM_GPIO1_SHADOW_REG(0x58)
+#define QSD8X50_GPIO_IN_4 MSM_GPIO1_SHADOW_REG(0x5C)
+#define QSD8X50_GPIO_IN_5 MSM_GPIO1_SHADOW_REG(0x60)
+#define QSD8X50_GPIO_IN_6 MSM_GPIO1_SHADOW_REG(0x64)
+#define QSD8X50_GPIO_IN_7 MSM_GPIO1_SHADOW_REG(0x68)
+
+/* same pin map as above, 1=edge 0=level interrup */
+#define QSD8X50_GPIO_INT_EDGE_0 MSM_GPIO1_SHADOW_REG(0x70)
+#define QSD8X50_GPIO_INT_EDGE_1 MSM_GPIO2_SHADOW_REG(0x50)
+#define QSD8X50_GPIO_INT_EDGE_2 MSM_GPIO1_SHADOW_REG(0x74)
+#define QSD8X50_GPIO_INT_EDGE_3 MSM_GPIO1_SHADOW_REG(0x78)
+#define QSD8X50_GPIO_INT_EDGE_4 MSM_GPIO1_SHADOW_REG(0x7C)
+#define QSD8X50_GPIO_INT_EDGE_5 MSM_GPIO1_SHADOW_REG(0x80)
+#define QSD8X50_GPIO_INT_EDGE_6 MSM_GPIO1_SHADOW_REG(0x84)
+#define QSD8X50_GPIO_INT_EDGE_7 MSM_GPIO1_SHADOW_REG(0x88)
+
+/* same pin map as above, 1=positive 0=negative */
+#define QSD8X50_GPIO_INT_POS_0 MSM_GPIO1_SHADOW_REG(0x90)
+#define QSD8X50_GPIO_INT_POS_1 MSM_GPIO2_SHADOW_REG(0x58)
+#define QSD8X50_GPIO_INT_POS_2 MSM_GPIO1_SHADOW_REG(0x94)
+#define QSD8X50_GPIO_INT_POS_3 MSM_GPIO1_SHADOW_REG(0x98)
+#define QSD8X50_GPIO_INT_POS_4 MSM_GPIO1_SHADOW_REG(0x9C)
+#define QSD8X50_GPIO_INT_POS_5 MSM_GPIO1_SHADOW_REG(0xA0)
+#define QSD8X50_GPIO_INT_POS_6 MSM_GPIO1_SHADOW_REG(0xA4)
+#define QSD8X50_GPIO_INT_POS_7 MSM_GPIO1_SHADOW_REG(0xA8)
+
+/* same pin map as above, interrupt enable */
+#define QSD8X50_GPIO_INT_EN_0 MSM_GPIO1_SHADOW_REG(0xB0)
+#define QSD8X50_GPIO_INT_EN_1 MSM_GPIO2_SHADOW_REG(0x60)
+#define QSD8X50_GPIO_INT_EN_2 MSM_GPIO1_SHADOW_REG(0xB4)
+#define QSD8X50_GPIO_INT_EN_3 MSM_GPIO1_SHADOW_REG(0xB8)
+#define QSD8X50_GPIO_INT_EN_4 MSM_GPIO1_SHADOW_REG(0xBC)
+#define QSD8X50_GPIO_INT_EN_5 MSM_GPIO1_SHADOW_REG(0xC0)
+#define QSD8X50_GPIO_INT_EN_6 MSM_GPIO1_SHADOW_REG(0xC4)
+#define QSD8X50_GPIO_INT_EN_7 MSM_GPIO1_SHADOW_REG(0xC8)
+
+/* same pin map as above, write 1 to clear interrupt */
+#define QSD8X50_GPIO_INT_CLEAR_0 MSM_GPIO1_SHADOW_REG(0xD0)
+#define QSD8X50_GPIO_INT_CLEAR_1 MSM_GPIO2_SHADOW_REG(0x68)
+#define QSD8X50_GPIO_INT_CLEAR_2 MSM_GPIO1_SHADOW_REG(0xD4)
+#define QSD8X50_GPIO_INT_CLEAR_3 MSM_GPIO1_SHADOW_REG(0xD8)
+#define QSD8X50_GPIO_INT_CLEAR_4 MSM_GPIO1_SHADOW_REG(0xDC)
+#define QSD8X50_GPIO_INT_CLEAR_5 MSM_GPIO1_SHADOW_REG(0xE0)
+#define QSD8X50_GPIO_INT_CLEAR_6 MSM_GPIO1_SHADOW_REG(0xE4)
+#define QSD8X50_GPIO_INT_CLEAR_7 MSM_GPIO1_SHADOW_REG(0xE8)
+
+/* same pin map as above, 1=interrupt pending */
+#define QSD8X50_GPIO_INT_STATUS_0 MSM_GPIO1_SHADOW_REG(0xF0)
+#define QSD8X50_GPIO_INT_STATUS_1 MSM_GPIO2_SHADOW_REG(0x70)
+#define QSD8X50_GPIO_INT_STATUS_2 MSM_GPIO1_SHADOW_REG(0xF4)
+#define QSD8X50_GPIO_INT_STATUS_3 MSM_GPIO1_SHADOW_REG(0xF8)
+#define QSD8X50_GPIO_INT_STATUS_4 MSM_GPIO1_SHADOW_REG(0xFC)
+#define QSD8X50_GPIO_INT_STATUS_5 MSM_GPIO1_SHADOW_REG(0x100)
+#define QSD8X50_GPIO_INT_STATUS_6 MSM_GPIO1_SHADOW_REG(0x104)
+#define QSD8X50_GPIO_INT_STATUS_7 MSM_GPIO1_SHADOW_REG(0x108)
+
+/*
+ * MSM7X30 registers
+ */
+/* output value */
+#define MSM7X30_GPIO_OUT_0 MSM_GPIO1_REG(0x00) /* gpio 15-0 */
+#define MSM7X30_GPIO_OUT_1 MSM_GPIO2_REG(0x00) /* gpio 43-16 */
+#define MSM7X30_GPIO_OUT_2 MSM_GPIO1_REG(0x04) /* gpio 67-44 */
+#define MSM7X30_GPIO_OUT_3 MSM_GPIO1_REG(0x08) /* gpio 94-68 */
+#define MSM7X30_GPIO_OUT_4 MSM_GPIO1_REG(0x0C) /* gpio 106-95 */
+#define MSM7X30_GPIO_OUT_5 MSM_GPIO1_REG(0x50) /* gpio 133-107 */
+#define MSM7X30_GPIO_OUT_6 MSM_GPIO1_REG(0xC4) /* gpio 150-134 */
+#define MSM7X30_GPIO_OUT_7 MSM_GPIO1_REG(0x214) /* gpio 181-151 */
+
+/* same pin map as above, output enable */
+#define MSM7X30_GPIO_OE_0 MSM_GPIO1_REG(0x10)
+#define MSM7X30_GPIO_OE_1 MSM_GPIO2_REG(0x08)
+#define MSM7X30_GPIO_OE_2 MSM_GPIO1_REG(0x14)
+#define MSM7X30_GPIO_OE_3 MSM_GPIO1_REG(0x18)
+#define MSM7X30_GPIO_OE_4 MSM_GPIO1_REG(0x1C)
+#define MSM7X30_GPIO_OE_5 MSM_GPIO1_REG(0x54)
+#define MSM7X30_GPIO_OE_6 MSM_GPIO1_REG(0xC8)
+#define MSM7X30_GPIO_OE_7 MSM_GPIO1_REG(0x218)
+
+/* same pin map as above, input read */
+#define MSM7X30_GPIO_IN_0 MSM_GPIO1_REG(0x34)
+#define MSM7X30_GPIO_IN_1 MSM_GPIO2_REG(0x20)
+#define MSM7X30_GPIO_IN_2 MSM_GPIO1_REG(0x38)
+#define MSM7X30_GPIO_IN_3 MSM_GPIO1_REG(0x3C)
+#define MSM7X30_GPIO_IN_4 MSM_GPIO1_REG(0x40)
+#define MSM7X30_GPIO_IN_5 MSM_GPIO1_REG(0x44)
+#define MSM7X30_GPIO_IN_6 MSM_GPIO1_REG(0xCC)
+#define MSM7X30_GPIO_IN_7 MSM_GPIO1_REG(0x21C)
+
+/* same pin map as above, 1=edge 0=level interrup */
+#define MSM7X30_GPIO_INT_EDGE_0 MSM_GPIO1_REG(0x60)
+#define MSM7X30_GPIO_INT_EDGE_1 MSM_GPIO2_REG(0x50)
+#define MSM7X30_GPIO_INT_EDGE_2 MSM_GPIO1_REG(0x64)
+#define MSM7X30_GPIO_INT_EDGE_3 MSM_GPIO1_REG(0x68)
+#define MSM7X30_GPIO_INT_EDGE_4 MSM_GPIO1_REG(0x6C)
+#define MSM7X30_GPIO_INT_EDGE_5 MSM_GPIO1_REG(0xC0)
+#define MSM7X30_GPIO_INT_EDGE_6 MSM_GPIO1_REG(0xD0)
+#define MSM7X30_GPIO_INT_EDGE_7 MSM_GPIO1_REG(0x240)
+
+/* same pin map as above, 1=positive 0=negative */
+#define MSM7X30_GPIO_INT_POS_0 MSM_GPIO1_REG(0x70)
+#define MSM7X30_GPIO_INT_POS_1 MSM_GPIO2_REG(0x58)
+#define MSM7X30_GPIO_INT_POS_2 MSM_GPIO1_REG(0x74)
+#define MSM7X30_GPIO_INT_POS_3 MSM_GPIO1_REG(0x78)
+#define MSM7X30_GPIO_INT_POS_4 MSM_GPIO1_REG(0x7C)
+#define MSM7X30_GPIO_INT_POS_5 MSM_GPIO1_REG(0xBC)
+#define MSM7X30_GPIO_INT_POS_6 MSM_GPIO1_REG(0xD4)
+#define MSM7X30_GPIO_INT_POS_7 MSM_GPIO1_REG(0x228)
+
+/* same pin map as above, interrupt enable */
+#define MSM7X30_GPIO_INT_EN_0 MSM_GPIO1_REG(0x80)
+#define MSM7X30_GPIO_INT_EN_1 MSM_GPIO2_REG(0x60)
+#define MSM7X30_GPIO_INT_EN_2 MSM_GPIO1_REG(0x84)
+#define MSM7X30_GPIO_INT_EN_3 MSM_GPIO1_REG(0x88)
+#define MSM7X30_GPIO_INT_EN_4 MSM_GPIO1_REG(0x8C)
+#define MSM7X30_GPIO_INT_EN_5 MSM_GPIO1_REG(0xB8)
+#define MSM7X30_GPIO_INT_EN_6 MSM_GPIO1_REG(0xD8)
+#define MSM7X30_GPIO_INT_EN_7 MSM_GPIO1_REG(0x22C)
+
+/* same pin map as above, write 1 to clear interrupt */
+#define MSM7X30_GPIO_INT_CLEAR_0 MSM_GPIO1_REG(0x90)
+#define MSM7X30_GPIO_INT_CLEAR_1 MSM_GPIO2_REG(0x68)
+#define MSM7X30_GPIO_INT_CLEAR_2 MSM_GPIO1_REG(0x94)
+#define MSM7X30_GPIO_INT_CLEAR_3 MSM_GPIO1_REG(0x98)
+#define MSM7X30_GPIO_INT_CLEAR_4 MSM_GPIO1_REG(0x9C)
+#define MSM7X30_GPIO_INT_CLEAR_5 MSM_GPIO1_REG(0xB4)
+#define MSM7X30_GPIO_INT_CLEAR_6 MSM_GPIO1_REG(0xDC)
+#define MSM7X30_GPIO_INT_CLEAR_7 MSM_GPIO1_REG(0x230)
+
+/* same pin map as above, 1=interrupt pending */
+#define MSM7X30_GPIO_INT_STATUS_0 MSM_GPIO1_REG(0xA0)
+#define MSM7X30_GPIO_INT_STATUS_1 MSM_GPIO2_REG(0x70)
+#define MSM7X30_GPIO_INT_STATUS_2 MSM_GPIO1_REG(0xA4)
+#define MSM7X30_GPIO_INT_STATUS_3 MSM_GPIO1_REG(0xA8)
+#define MSM7X30_GPIO_INT_STATUS_4 MSM_GPIO1_REG(0xAC)
+#define MSM7X30_GPIO_INT_STATUS_5 MSM_GPIO1_REG(0xB0)
+#define MSM7X30_GPIO_INT_STATUS_6 MSM_GPIO1_REG(0xE0)
+#define MSM7X30_GPIO_INT_STATUS_7 MSM_GPIO1_REG(0x234)
+
+#define FIRST_GPIO_IRQ MSM_GPIO_TO_INT(0)
+
+#define MSM_GPIO_BANK(soc, bank, first, last) \
+ { \
+ .regs = { \
+ .out = soc##_GPIO_OUT_##bank, \
+ .in = soc##_GPIO_IN_##bank, \
+ .int_status = soc##_GPIO_INT_STATUS_##bank, \
+ .int_clear = soc##_GPIO_INT_CLEAR_##bank, \
+ .int_en = soc##_GPIO_INT_EN_##bank, \
+ .int_edge = soc##_GPIO_INT_EDGE_##bank, \
+ .int_pos = soc##_GPIO_INT_POS_##bank, \
+ .oe = soc##_GPIO_OE_##bank, \
+ }, \
+ .chip = { \
+ .base = (first), \
+ .ngpio = (last) - (first) + 1, \
+ .get = msm_gpio_get, \
+ .set = msm_gpio_set, \
+ .direction_input = msm_gpio_direction_input, \
+ .direction_output = msm_gpio_direction_output, \
+ .to_irq = msm_gpio_to_irq, \
+ .request = msm_gpio_request, \
+ .free = msm_gpio_free, \
+ } \
+ }
+
+#define MSM_GPIO_BROKEN_INT_CLEAR 1
+
+struct msm_gpio_regs {
+ void __iomem *out;
+ void __iomem *in;
+ void __iomem *int_status;
+ void __iomem *int_clear;
+ void __iomem *int_en;
+ void __iomem *int_edge;
+ void __iomem *int_pos;
+ void __iomem *oe;
+};
+
+struct msm_gpio_chip {
+ spinlock_t lock;
+ struct gpio_chip chip;
+ struct msm_gpio_regs regs;
+#if MSM_GPIO_BROKEN_INT_CLEAR
+ unsigned int_status_copy;
+#endif
+ unsigned int both_edge_detect;
+ unsigned int int_enable[2]; /* 0: awake, 1: sleep */
+};
+
+static int msm_gpio_write(struct msm_gpio_chip *msm_chip,
+ unsigned offset, unsigned on)
+{
+ unsigned mask = BIT(offset);
+ unsigned val;
+
+ val = readl(msm_chip->regs.out);
+ if (on)
+ writel(val | mask, msm_chip->regs.out);
+ else
+ writel(val & ~mask, msm_chip->regs.out);
+ return 0;
+}
+
+static void msm_gpio_update_both_edge_detect(struct msm_gpio_chip *msm_chip)
+{
+ int loop_limit = 100;
+ unsigned pol, val, val2, intstat;
+ do {
+ val = readl(msm_chip->regs.in);
+ pol = readl(msm_chip->regs.int_pos);
+ pol = (pol & ~msm_chip->both_edge_detect) |
+ (~val & msm_chip->both_edge_detect);
+ writel(pol, msm_chip->regs.int_pos);
+ intstat = readl(msm_chip->regs.int_status);
+ val2 = readl(msm_chip->regs.in);
+ if (((val ^ val2) & msm_chip->both_edge_detect & ~intstat) == 0)
+ return;
+ } while (loop_limit-- > 0);
+ printk(KERN_ERR "msm_gpio_update_both_edge_detect, "
+ "failed to reach stable state %x != %x\n", val, val2);
+}
+
+static int msm_gpio_clear_detect_status(struct msm_gpio_chip *msm_chip,
+ unsigned offset)
+{
+ unsigned bit = BIT(offset);
+
+#if MSM_GPIO_BROKEN_INT_CLEAR
+ /* Save interrupts that already triggered before we loose them. */
+ /* Any interrupt that triggers between the read of int_status */
+ /* and the write to int_clear will still be lost though. */
+ msm_chip->int_status_copy |= readl(msm_chip->regs.int_status);
+ msm_chip->int_status_copy &= ~bit;
+#endif
+ writel(bit, msm_chip->regs.int_clear);
+ msm_gpio_update_both_edge_detect(msm_chip);
+ return 0;
+}
+
+static int msm_gpio_direction_input(struct gpio_chip *chip, unsigned offset)
+{
+ struct msm_gpio_chip *msm_chip;
+ unsigned long irq_flags;
+
+ msm_chip = container_of(chip, struct msm_gpio_chip, chip);
+ spin_lock_irqsave(&msm_chip->lock, irq_flags);
+ writel(readl(msm_chip->regs.oe) & ~BIT(offset), msm_chip->regs.oe);
+ spin_unlock_irqrestore(&msm_chip->lock, irq_flags);
+ return 0;
+}
+
+static int
+msm_gpio_direction_output(struct gpio_chip *chip, unsigned offset, int value)
+{
+ struct msm_gpio_chip *msm_chip;
+ unsigned long irq_flags;
+
+ msm_chip = container_of(chip, struct msm_gpio_chip, chip);
+ spin_lock_irqsave(&msm_chip->lock, irq_flags);
+ msm_gpio_write(msm_chip, offset, value);
+ writel(readl(msm_chip->regs.oe) | BIT(offset), msm_chip->regs.oe);
+ spin_unlock_irqrestore(&msm_chip->lock, irq_flags);
+ return 0;
+}
+
+static int msm_gpio_get(struct gpio_chip *chip, unsigned offset)
+{
+ struct msm_gpio_chip *msm_chip;
+
+ msm_chip = container_of(chip, struct msm_gpio_chip, chip);
+ return (readl(msm_chip->regs.in) & (1U << offset)) ? 1 : 0;
+}
+
+static void msm_gpio_set(struct gpio_chip *chip, unsigned offset, int value)
+{
+ struct msm_gpio_chip *msm_chip;
+ unsigned long irq_flags;
+
+ msm_chip = container_of(chip, struct msm_gpio_chip, chip);
+ spin_lock_irqsave(&msm_chip->lock, irq_flags);
+ msm_gpio_write(msm_chip, offset, value);
+ spin_unlock_irqrestore(&msm_chip->lock, irq_flags);
+}
+
+static int msm_gpio_to_irq(struct gpio_chip *chip, unsigned offset)
+{
+ return MSM_GPIO_TO_INT(chip->base + offset);
+}
+
+#ifdef CONFIG_MSM_GPIOMUX
+static int msm_gpio_request(struct gpio_chip *chip, unsigned offset)
+{
+ return msm_gpiomux_get(chip->base + offset);
+}
+
+static void msm_gpio_free(struct gpio_chip *chip, unsigned offset)
+{
+ msm_gpiomux_put(chip->base + offset);
+}
+#else
+#define msm_gpio_request NULL
+#define msm_gpio_free NULL
+#endif
+
+static struct msm_gpio_chip *msm_gpio_chips;
+static int msm_gpio_count;
+
+static struct msm_gpio_chip msm_gpio_chips_msm7x01[] = {
+ MSM_GPIO_BANK(MSM7X00, 0, 0, 15),
+ MSM_GPIO_BANK(MSM7X00, 1, 16, 42),
+ MSM_GPIO_BANK(MSM7X00, 2, 43, 67),
+ MSM_GPIO_BANK(MSM7X00, 3, 68, 94),
+ MSM_GPIO_BANK(MSM7X00, 4, 95, 106),
+ MSM_GPIO_BANK(MSM7X00, 5, 107, 121),
+};
+
+static struct msm_gpio_chip msm_gpio_chips_msm7x30[] = {
+ MSM_GPIO_BANK(MSM7X30, 0, 0, 15),
+ MSM_GPIO_BANK(MSM7X30, 1, 16, 43),
+ MSM_GPIO_BANK(MSM7X30, 2, 44, 67),
+ MSM_GPIO_BANK(MSM7X30, 3, 68, 94),
+ MSM_GPIO_BANK(MSM7X30, 4, 95, 106),
+ MSM_GPIO_BANK(MSM7X30, 5, 107, 133),
+ MSM_GPIO_BANK(MSM7X30, 6, 134, 150),
+ MSM_GPIO_BANK(MSM7X30, 7, 151, 181),
+};
+
+static struct msm_gpio_chip msm_gpio_chips_qsd8x50[] = {
+ MSM_GPIO_BANK(QSD8X50, 0, 0, 15),
+ MSM_GPIO_BANK(QSD8X50, 1, 16, 42),
+ MSM_GPIO_BANK(QSD8X50, 2, 43, 67),
+ MSM_GPIO_BANK(QSD8X50, 3, 68, 94),
+ MSM_GPIO_BANK(QSD8X50, 4, 95, 103),
+ MSM_GPIO_BANK(QSD8X50, 5, 104, 121),
+ MSM_GPIO_BANK(QSD8X50, 6, 122, 152),
+ MSM_GPIO_BANK(QSD8X50, 7, 153, 164),
+};
+
+static void msm_gpio_irq_ack(struct irq_data *d)
+{
+ unsigned long irq_flags;
+ struct msm_gpio_chip *msm_chip = irq_data_get_irq_chip_data(d);
+ spin_lock_irqsave(&msm_chip->lock, irq_flags);
+ msm_gpio_clear_detect_status(msm_chip,
+ d->irq - gpio_to_irq(msm_chip->chip.base));
+ spin_unlock_irqrestore(&msm_chip->lock, irq_flags);
+}
+
+static void msm_gpio_irq_mask(struct irq_data *d)
+{
+ unsigned long irq_flags;
+ struct msm_gpio_chip *msm_chip = irq_data_get_irq_chip_data(d);
+ unsigned offset = d->irq - gpio_to_irq(msm_chip->chip.base);
+
+ spin_lock_irqsave(&msm_chip->lock, irq_flags);
+ /* level triggered interrupts are also latched */
+ if (!(readl(msm_chip->regs.int_edge) & BIT(offset)))
+ msm_gpio_clear_detect_status(msm_chip, offset);
+ msm_chip->int_enable[0] &= ~BIT(offset);
+ writel(msm_chip->int_enable[0], msm_chip->regs.int_en);
+ spin_unlock_irqrestore(&msm_chip->lock, irq_flags);
+}
+
+static void msm_gpio_irq_unmask(struct irq_data *d)
+{
+ unsigned long irq_flags;
+ struct msm_gpio_chip *msm_chip = irq_data_get_irq_chip_data(d);
+ unsigned offset = d->irq - gpio_to_irq(msm_chip->chip.base);
+
+ spin_lock_irqsave(&msm_chip->lock, irq_flags);
+ /* level triggered interrupts are also latched */
+ if (!(readl(msm_chip->regs.int_edge) & BIT(offset)))
+ msm_gpio_clear_detect_status(msm_chip, offset);
+ msm_chip->int_enable[0] |= BIT(offset);
+ writel(msm_chip->int_enable[0], msm_chip->regs.int_en);
+ spin_unlock_irqrestore(&msm_chip->lock, irq_flags);
+}
+
+static int msm_gpio_irq_set_wake(struct irq_data *d, unsigned int on)
+{
+ unsigned long irq_flags;
+ struct msm_gpio_chip *msm_chip = irq_data_get_irq_chip_data(d);
+ unsigned offset = d->irq - gpio_to_irq(msm_chip->chip.base);
+
+ spin_lock_irqsave(&msm_chip->lock, irq_flags);
+
+ if (on)
+ msm_chip->int_enable[1] |= BIT(offset);
+ else
+ msm_chip->int_enable[1] &= ~BIT(offset);
+
+ spin_unlock_irqrestore(&msm_chip->lock, irq_flags);
+ return 0;
+}
+
+static int msm_gpio_irq_set_type(struct irq_data *d, unsigned int flow_type)
+{
+ unsigned long irq_flags;
+ struct msm_gpio_chip *msm_chip = irq_data_get_irq_chip_data(d);
+ unsigned offset = d->irq - gpio_to_irq(msm_chip->chip.base);
+ unsigned val, mask = BIT(offset);
+
+ spin_lock_irqsave(&msm_chip->lock, irq_flags);
+ val = readl(msm_chip->regs.int_edge);
+ if (flow_type & IRQ_TYPE_EDGE_BOTH) {
+ writel(val | mask, msm_chip->regs.int_edge);
+ __irq_set_handler_locked(d->irq, handle_edge_irq);
+ } else {
+ writel(val & ~mask, msm_chip->regs.int_edge);
+ __irq_set_handler_locked(d->irq, handle_level_irq);
+ }
+ if ((flow_type & IRQ_TYPE_EDGE_BOTH) == IRQ_TYPE_EDGE_BOTH) {
+ msm_chip->both_edge_detect |= mask;
+ msm_gpio_update_both_edge_detect(msm_chip);
+ } else {
+ msm_chip->both_edge_detect &= ~mask;
+ val = readl(msm_chip->regs.int_pos);
+ if (flow_type & (IRQF_TRIGGER_RISING | IRQF_TRIGGER_HIGH))
+ writel(val | mask, msm_chip->regs.int_pos);
+ else
+ writel(val & ~mask, msm_chip->regs.int_pos);
+ }
+ spin_unlock_irqrestore(&msm_chip->lock, irq_flags);
+ return 0;
+}
+
+static void msm_gpio_irq_handler(unsigned int irq, struct irq_desc *desc)
+{
+ int i, j, mask;
+ unsigned val;
+
+ for (i = 0; i < msm_gpio_count; i++) {
+ struct msm_gpio_chip *msm_chip = &msm_gpio_chips[i];
+ val = readl(msm_chip->regs.int_status);
+ val &= msm_chip->int_enable[0];
+ while (val) {
+ mask = val & -val;
+ j = fls(mask) - 1;
+ /* printk("%s %08x %08x bit %d gpio %d irq %d\n",
+ __func__, v, m, j, msm_chip->chip.start + j,
+ FIRST_GPIO_IRQ + msm_chip->chip.start + j); */
+ val &= ~mask;
+ generic_handle_irq(FIRST_GPIO_IRQ +
+ msm_chip->chip.base + j);
+ }
+ }
+ desc->irq_data.chip->irq_ack(&desc->irq_data);
+}
+
+static struct irq_chip msm_gpio_irq_chip = {
+ .name = "msmgpio",
+ .irq_ack = msm_gpio_irq_ack,
+ .irq_mask = msm_gpio_irq_mask,
+ .irq_unmask = msm_gpio_irq_unmask,
+ .irq_set_wake = msm_gpio_irq_set_wake,
+ .irq_set_type = msm_gpio_irq_set_type,
+};
+
+static int __init msm_init_gpio(void)
+{
+ int i, j = 0;
+
+ if (cpu_is_msm7x01()) {
+ msm_gpio_chips = msm_gpio_chips_msm7x01;
+ msm_gpio_count = ARRAY_SIZE(msm_gpio_chips_msm7x01);
+ } else if (cpu_is_msm7x30()) {
+ msm_gpio_chips = msm_gpio_chips_msm7x30;
+ msm_gpio_count = ARRAY_SIZE(msm_gpio_chips_msm7x30);
+ } else if (cpu_is_qsd8x50()) {
+ msm_gpio_chips = msm_gpio_chips_qsd8x50;
+ msm_gpio_count = ARRAY_SIZE(msm_gpio_chips_qsd8x50);
+ } else {
+ return 0;
+ }
+
+ for (i = FIRST_GPIO_IRQ; i < FIRST_GPIO_IRQ + NR_GPIO_IRQS; i++) {
+ if (i - FIRST_GPIO_IRQ >=
+ msm_gpio_chips[j].chip.base +
+ msm_gpio_chips[j].chip.ngpio)
+ j++;
+ irq_set_chip_data(i, &msm_gpio_chips[j]);
+ irq_set_chip_and_handler(i, &msm_gpio_irq_chip,
+ handle_edge_irq);
+ set_irq_flags(i, IRQF_VALID);
+ }
+
+ for (i = 0; i < msm_gpio_count; i++) {
+ spin_lock_init(&msm_gpio_chips[i].lock);
+ writel(0, msm_gpio_chips[i].regs.int_en);
+ gpiochip_add(&msm_gpio_chips[i].chip);
+ }
+
+ irq_set_chained_handler(INT_GPIO_GROUP1, msm_gpio_irq_handler);
+ irq_set_chained_handler(INT_GPIO_GROUP2, msm_gpio_irq_handler);
+ irq_set_irq_wake(INT_GPIO_GROUP1, 1);
+ irq_set_irq_wake(INT_GPIO_GROUP2, 2);
+ return 0;
+}
+
+postcore_initcall(msm_init_gpio);
diff --git a/arch/arm/mach-msm/gpio-v2.c b/drivers/gpio/gpio-msm-v2.c
similarity index 98%
rename from arch/arm/mach-msm/gpio-v2.c
rename to drivers/gpio/gpio-msm-v2.c
index cc9c4fd..5cb1227 100644
--- a/arch/arm/mach-msm/gpio-v2.c
+++ b/drivers/gpio/gpio-msm-v2.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2010, Code Aurora Forum. All rights reserved.
+/* Copyright (c) 2010-2011, Code Aurora Forum. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -30,8 +30,8 @@
#include <asm/mach/irq.h>
+#include <mach/msm_gpiomux.h>
#include <mach/msm_iomap.h>
-#include "gpiomux.h"
/* Bits of interest in the GPIO_IN_OUT register.
*/
diff --git a/drivers/gpio/gpio-tps65912.c b/drivers/gpio/gpio-tps65912.c
new file mode 100644
index 0000000..79e66c0
--- /dev/null
+++ b/drivers/gpio/gpio-tps65912.c
@@ -0,0 +1,156 @@
+/*
+ * Copyright 2011 Texas Instruments Inc.
+ *
+ * Author: Margarita Olaya <magi@slimlogic.co.uk>
+ *
+ * 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 driver is based on wm8350 implementation.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/errno.h>
+#include <linux/gpio.h>
+#include <linux/mfd/core.h>
+#include <linux/platform_device.h>
+#include <linux/seq_file.h>
+#include <linux/slab.h>
+#include <linux/mfd/tps65912.h>
+
+struct tps65912_gpio_data {
+ struct tps65912 *tps65912;
+ struct gpio_chip gpio_chip;
+};
+
+static int tps65912_gpio_get(struct gpio_chip *gc, unsigned offset)
+{
+ struct tps65912 *tps65912 = container_of(gc, struct tps65912, gpio);
+ int val;
+
+ val = tps65912_reg_read(tps65912, TPS65912_GPIO1 + offset);
+
+ if (val & GPIO_STS_MASK)
+ return 1;
+
+ return 0;
+}
+
+static void tps65912_gpio_set(struct gpio_chip *gc, unsigned offset,
+ int value)
+{
+ struct tps65912 *tps65912 = container_of(gc, struct tps65912, gpio);
+
+ if (value)
+ tps65912_set_bits(tps65912, TPS65912_GPIO1 + offset,
+ GPIO_SET_MASK);
+ else
+ tps65912_clear_bits(tps65912, TPS65912_GPIO1 + offset,
+ GPIO_SET_MASK);
+}
+
+static int tps65912_gpio_output(struct gpio_chip *gc, unsigned offset,
+ int value)
+{
+ struct tps65912 *tps65912 = container_of(gc, struct tps65912, gpio);
+
+ /* Set the initial value */
+ tps65912_gpio_set(gc, offset, value);
+
+ return tps65912_set_bits(tps65912, TPS65912_GPIO1 + offset,
+ GPIO_CFG_MASK);
+}
+
+static int tps65912_gpio_input(struct gpio_chip *gc, unsigned offset)
+{
+ struct tps65912 *tps65912 = container_of(gc, struct tps65912, gpio);
+
+ return tps65912_clear_bits(tps65912, TPS65912_GPIO1 + offset,
+ GPIO_CFG_MASK);
+
+}
+
+static struct gpio_chip template_chip = {
+ .label = "tps65912",
+ .owner = THIS_MODULE,
+ .direction_input = tps65912_gpio_input,
+ .direction_output = tps65912_gpio_output,
+ .get = tps65912_gpio_get,
+ .set = tps65912_gpio_set,
+ .can_sleep = 1,
+ .ngpio = 5,
+ .base = -1,
+};
+
+static int __devinit tps65912_gpio_probe(struct platform_device *pdev)
+{
+ struct tps65912 *tps65912 = dev_get_drvdata(pdev->dev.parent);
+ struct tps65912_board *pdata = tps65912->dev->platform_data;
+ struct tps65912_gpio_data *tps65912_gpio;
+ int ret;
+
+ tps65912_gpio = kzalloc(sizeof(*tps65912_gpio), GFP_KERNEL);
+ if (tps65912_gpio == NULL)
+ return -ENOMEM;
+
+ tps65912_gpio->tps65912 = tps65912;
+ tps65912_gpio->gpio_chip = template_chip;
+ tps65912_gpio->gpio_chip.dev = &pdev->dev;
+ if (pdata && pdata->gpio_base)
+ tps65912_gpio->gpio_chip.base = pdata->gpio_base;
+
+ ret = gpiochip_add(&tps65912_gpio->gpio_chip);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "Failed to register gpiochip, %d\n", ret);
+ goto err;
+ }
+
+ platform_set_drvdata(pdev, tps65912_gpio);
+
+ return ret;
+
+err:
+ kfree(tps65912_gpio);
+ return ret;
+}
+
+static int __devexit tps65912_gpio_remove(struct platform_device *pdev)
+{
+ struct tps65912_gpio_data *tps65912_gpio = platform_get_drvdata(pdev);
+ int ret;
+
+ ret = gpiochip_remove(&tps65912_gpio->gpio_chip);
+ if (ret == 0)
+ kfree(tps65912_gpio);
+
+ return ret;
+}
+
+static struct platform_driver tps65912_gpio_driver = {
+ .driver = {
+ .name = "tps65912-gpio",
+ .owner = THIS_MODULE,
+ },
+ .probe = tps65912_gpio_probe,
+ .remove = __devexit_p(tps65912_gpio_remove),
+};
+
+static int __init tps65912_gpio_init(void)
+{
+ return platform_driver_register(&tps65912_gpio_driver);
+}
+subsys_initcall(tps65912_gpio_init);
+
+static void __exit tps65912_gpio_exit(void)
+{
+ platform_driver_unregister(&tps65912_gpio_driver);
+}
+module_exit(tps65912_gpio_exit);
+
+MODULE_AUTHOR("Margarita Olaya Cabrera <magi@slimlogic.co.uk>");
+MODULE_DESCRIPTION("GPIO interface for TPS65912 PMICs");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:tps65912-gpio");
diff --git a/drivers/gpu/drm/drm_debugfs.c b/drivers/gpu/drm/drm_debugfs.c
index 9d8c892..9d2668a 100644
--- a/drivers/gpu/drm/drm_debugfs.c
+++ b/drivers/gpu/drm/drm_debugfs.c
@@ -90,7 +90,6 @@
struct drm_device *dev = minor->dev;
struct dentry *ent;
struct drm_info_node *tmp;
- char name[64];
int i, ret;
for (i = 0; i < count; i++) {
@@ -108,6 +107,9 @@
ent = debugfs_create_file(files[i].name, S_IFREG | S_IRUGO,
root, tmp, &drm_debugfs_fops);
if (!ent) {
+ char name[64];
+ strncpy(name, root->d_name.name,
+ min(root->d_name.len, 64U));
DRM_ERROR("Cannot create /sys/kernel/debug/dri/%s/%s\n",
name, files[i].name);
kfree(tmp);
diff --git a/drivers/gpu/drm/drm_edid.c b/drivers/gpu/drm/drm_edid.c
index 756af4d..7425e5c 100644
--- a/drivers/gpu/drm/drm_edid.c
+++ b/drivers/gpu/drm/drm_edid.c
@@ -127,6 +127,23 @@
0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00
};
+ /*
+ * Sanity check the header of the base EDID block. Return 8 if the header
+ * is perfect, down to 0 if it's totally wrong.
+ */
+int drm_edid_header_is_valid(const u8 *raw_edid)
+{
+ int i, score = 0;
+
+ for (i = 0; i < sizeof(edid_header); i++)
+ if (raw_edid[i] == edid_header[i])
+ score++;
+
+ return score;
+}
+EXPORT_SYMBOL(drm_edid_header_is_valid);
+
+
/*
* Sanity check the EDID block (base or extension). Return 0 if the block
* doesn't check out, or 1 if it's valid.
@@ -139,12 +156,7 @@
struct edid *edid = (struct edid *)raw_edid;
if (raw_edid[0] == 0x00) {
- int score = 0;
-
- for (i = 0; i < sizeof(edid_header); i++)
- if (raw_edid[i] == edid_header[i])
- score++;
-
+ int score = drm_edid_header_is_valid(raw_edid);
if (score == 8) ;
else if (score >= 6) {
DRM_DEBUG("Fixing EDID header, your hardware may be failing\n");
@@ -1439,6 +1451,8 @@
static void drm_add_display_info(struct edid *edid,
struct drm_display_info *info)
{
+ u8 *edid_ext;
+
info->width_mm = edid->width_cm * 10;
info->height_mm = edid->height_cm * 10;
@@ -1483,6 +1497,13 @@
info->color_formats = DRM_COLOR_FORMAT_YCRCB444;
if (info->color_formats & DRM_EDID_FEATURE_RGB_YCRCB422)
info->color_formats = DRM_COLOR_FORMAT_YCRCB422;
+
+ /* Get data from CEA blocks if present */
+ edid_ext = drm_find_cea_extension(edid);
+ if (!edid_ext)
+ return;
+
+ info->cea_rev = edid_ext[1];
}
/**
diff --git a/drivers/gpu/drm/drm_irq.c b/drivers/gpu/drm/drm_irq.c
index 2022a5c..3830e9e 100644
--- a/drivers/gpu/drm/drm_irq.c
+++ b/drivers/gpu/drm/drm_irq.c
@@ -291,11 +291,14 @@
if (!dev->irq_enabled)
return;
- if (state)
- dev->driver->irq_uninstall(dev);
- else {
- dev->driver->irq_preinstall(dev);
- dev->driver->irq_postinstall(dev);
+ if (state) {
+ if (dev->driver->irq_uninstall)
+ dev->driver->irq_uninstall(dev);
+ } else {
+ if (dev->driver->irq_preinstall)
+ dev->driver->irq_preinstall(dev);
+ if (dev->driver->irq_postinstall)
+ dev->driver->irq_postinstall(dev);
}
}
@@ -338,7 +341,8 @@
DRM_DEBUG("irq=%d\n", drm_dev_to_irq(dev));
/* Before installing handler */
- dev->driver->irq_preinstall(dev);
+ if (dev->driver->irq_preinstall)
+ dev->driver->irq_preinstall(dev);
/* Install handler */
if (drm_core_check_feature(dev, DRIVER_IRQ_SHARED))
@@ -363,11 +367,16 @@
vga_client_register(dev->pdev, (void *)dev, drm_irq_vgaarb_nokms, NULL);
/* After installing handler */
- ret = dev->driver->irq_postinstall(dev);
+ if (dev->driver->irq_postinstall)
+ ret = dev->driver->irq_postinstall(dev);
+
if (ret < 0) {
mutex_lock(&dev->struct_mutex);
dev->irq_enabled = 0;
mutex_unlock(&dev->struct_mutex);
+ if (!drm_core_check_feature(dev, DRIVER_MODESET))
+ vga_client_register(dev->pdev, NULL, NULL, NULL);
+ free_irq(drm_dev_to_irq(dev), dev);
}
return ret;
@@ -413,7 +422,8 @@
if (!drm_core_check_feature(dev, DRIVER_MODESET))
vga_client_register(dev->pdev, NULL, NULL, NULL);
- dev->driver->irq_uninstall(dev);
+ if (dev->driver->irq_uninstall)
+ dev->driver->irq_uninstall(dev);
free_irq(drm_dev_to_irq(dev), dev);
diff --git a/drivers/gpu/drm/i915/i915_debugfs.c b/drivers/gpu/drm/i915/i915_debugfs.c
index e2662497..3c395a5 100644
--- a/drivers/gpu/drm/i915/i915_debugfs.c
+++ b/drivers/gpu/drm/i915/i915_debugfs.c
@@ -499,7 +499,7 @@
seq_printf(m, "Interrupts received: %d\n",
atomic_read(&dev_priv->irq_received));
for (i = 0; i < I915_NUM_RINGS; i++) {
- if (IS_GEN6(dev)) {
+ if (IS_GEN6(dev) || IS_GEN7(dev)) {
seq_printf(m, "Graphics Interrupt mask (%s): %08x\n",
dev_priv->ring[i].name,
I915_READ_IMR(&dev_priv->ring[i]));
@@ -1338,6 +1338,155 @@
.llseek = default_llseek,
};
+static int
+i915_max_freq_open(struct inode *inode,
+ struct file *filp)
+{
+ filp->private_data = inode->i_private;
+ return 0;
+}
+
+static ssize_t
+i915_max_freq_read(struct file *filp,
+ char __user *ubuf,
+ size_t max,
+ loff_t *ppos)
+{
+ struct drm_device *dev = filp->private_data;
+ drm_i915_private_t *dev_priv = dev->dev_private;
+ char buf[80];
+ int len;
+
+ len = snprintf(buf, sizeof (buf),
+ "max freq: %d\n", dev_priv->max_delay * 50);
+
+ if (len > sizeof (buf))
+ len = sizeof (buf);
+
+ return simple_read_from_buffer(ubuf, max, ppos, buf, len);
+}
+
+static ssize_t
+i915_max_freq_write(struct file *filp,
+ const char __user *ubuf,
+ size_t cnt,
+ loff_t *ppos)
+{
+ struct drm_device *dev = filp->private_data;
+ struct drm_i915_private *dev_priv = dev->dev_private;
+ char buf[20];
+ int val = 1;
+
+ if (cnt > 0) {
+ if (cnt > sizeof (buf) - 1)
+ return -EINVAL;
+
+ if (copy_from_user(buf, ubuf, cnt))
+ return -EFAULT;
+ buf[cnt] = 0;
+
+ val = simple_strtoul(buf, NULL, 0);
+ }
+
+ DRM_DEBUG_DRIVER("Manually setting max freq to %d\n", val);
+
+ /*
+ * Turbo will still be enabled, but won't go above the set value.
+ */
+ dev_priv->max_delay = val / 50;
+
+ gen6_set_rps(dev, val / 50);
+
+ return cnt;
+}
+
+static const struct file_operations i915_max_freq_fops = {
+ .owner = THIS_MODULE,
+ .open = i915_max_freq_open,
+ .read = i915_max_freq_read,
+ .write = i915_max_freq_write,
+ .llseek = default_llseek,
+};
+
+static int
+i915_cache_sharing_open(struct inode *inode,
+ struct file *filp)
+{
+ filp->private_data = inode->i_private;
+ return 0;
+}
+
+static ssize_t
+i915_cache_sharing_read(struct file *filp,
+ char __user *ubuf,
+ size_t max,
+ loff_t *ppos)
+{
+ struct drm_device *dev = filp->private_data;
+ drm_i915_private_t *dev_priv = dev->dev_private;
+ char buf[80];
+ u32 snpcr;
+ int len;
+
+ mutex_lock(&dev_priv->dev->struct_mutex);
+ snpcr = I915_READ(GEN6_MBCUNIT_SNPCR);
+ mutex_unlock(&dev_priv->dev->struct_mutex);
+
+ len = snprintf(buf, sizeof (buf),
+ "%d\n", (snpcr & GEN6_MBC_SNPCR_MASK) >>
+ GEN6_MBC_SNPCR_SHIFT);
+
+ if (len > sizeof (buf))
+ len = sizeof (buf);
+
+ return simple_read_from_buffer(ubuf, max, ppos, buf, len);
+}
+
+static ssize_t
+i915_cache_sharing_write(struct file *filp,
+ const char __user *ubuf,
+ size_t cnt,
+ loff_t *ppos)
+{
+ struct drm_device *dev = filp->private_data;
+ struct drm_i915_private *dev_priv = dev->dev_private;
+ char buf[20];
+ u32 snpcr;
+ int val = 1;
+
+ if (cnt > 0) {
+ if (cnt > sizeof (buf) - 1)
+ return -EINVAL;
+
+ if (copy_from_user(buf, ubuf, cnt))
+ return -EFAULT;
+ buf[cnt] = 0;
+
+ val = simple_strtoul(buf, NULL, 0);
+ }
+
+ if (val < 0 || val > 3)
+ return -EINVAL;
+
+ DRM_DEBUG_DRIVER("Manually setting uncore sharing to %d\n", val);
+
+ /* Update the cache sharing policy here as well */
+ snpcr = I915_READ(GEN6_MBCUNIT_SNPCR);
+ snpcr &= ~GEN6_MBC_SNPCR_MASK;
+ snpcr |= (val << GEN6_MBC_SNPCR_SHIFT);
+ I915_WRITE(GEN6_MBCUNIT_SNPCR, snpcr);
+
+ return cnt;
+}
+
+static const struct file_operations i915_cache_sharing_fops = {
+ .owner = THIS_MODULE,
+ .open = i915_cache_sharing_open,
+ .read = i915_cache_sharing_read,
+ .write = i915_cache_sharing_write,
+ .llseek = default_llseek,
+};
+
/* As the drm_debugfs_init() routines are called before dev->dev_private is
* allocated we need to hook into the minor for release. */
static int
@@ -1437,6 +1586,36 @@
return drm_add_fake_info_node(minor, ent, &i915_forcewake_fops);
}
+static int i915_max_freq_create(struct dentry *root, struct drm_minor *minor)
+{
+ struct drm_device *dev = minor->dev;
+ struct dentry *ent;
+
+ ent = debugfs_create_file("i915_max_freq",
+ S_IRUGO | S_IWUSR,
+ root, dev,
+ &i915_max_freq_fops);
+ if (IS_ERR(ent))
+ return PTR_ERR(ent);
+
+ return drm_add_fake_info_node(minor, ent, &i915_max_freq_fops);
+}
+
+static int i915_cache_sharing_create(struct dentry *root, struct drm_minor *minor)
+{
+ struct drm_device *dev = minor->dev;
+ struct dentry *ent;
+
+ ent = debugfs_create_file("i915_cache_sharing",
+ S_IRUGO | S_IWUSR,
+ root, dev,
+ &i915_cache_sharing_fops);
+ if (IS_ERR(ent))
+ return PTR_ERR(ent);
+
+ return drm_add_fake_info_node(minor, ent, &i915_cache_sharing_fops);
+}
+
static struct drm_info_list i915_debugfs_list[] = {
{"i915_capabilities", i915_capabilities, 0},
{"i915_gem_objects", i915_gem_object_info, 0},
@@ -1490,6 +1669,12 @@
ret = i915_forcewake_create(minor->debugfs_root, minor);
if (ret)
return ret;
+ ret = i915_max_freq_create(minor->debugfs_root, minor);
+ if (ret)
+ return ret;
+ ret = i915_cache_sharing_create(minor->debugfs_root, minor);
+ if (ret)
+ return ret;
return drm_debugfs_create_files(i915_debugfs_list,
I915_DEBUGFS_ENTRIES,
@@ -1504,6 +1689,10 @@
1, minor);
drm_debugfs_remove_files((struct drm_info_list *) &i915_wedged_fops,
1, minor);
+ drm_debugfs_remove_files((struct drm_info_list *) &i915_max_freq_fops,
+ 1, minor);
+ drm_debugfs_remove_files((struct drm_info_list *) &i915_cache_sharing_fops,
+ 1, minor);
}
#endif /* CONFIG_DEBUG_FS */
diff --git a/drivers/gpu/drm/i915/i915_dma.c b/drivers/gpu/drm/i915/i915_dma.c
index 1271282..8a3942c 100644
--- a/drivers/gpu/drm/i915/i915_dma.c
+++ b/drivers/gpu/drm/i915/i915_dma.c
@@ -61,7 +61,6 @@
static int i915_init_phys_hws(struct drm_device *dev)
{
drm_i915_private_t *dev_priv = dev->dev_private;
- struct intel_ring_buffer *ring = LP_RING(dev_priv);
/* Program Hardware Status Page */
dev_priv->status_page_dmah =
@@ -71,10 +70,9 @@
DRM_ERROR("Can not allocate hardware status page\n");
return -ENOMEM;
}
- ring->status_page.page_addr =
- (void __force __iomem *)dev_priv->status_page_dmah->vaddr;
- memset_io(ring->status_page.page_addr, 0, PAGE_SIZE);
+ memset_io((void __force __iomem *)dev_priv->status_page_dmah->vaddr,
+ 0, PAGE_SIZE);
i915_write_hws_pga(dev);
diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h
index 6867e19..7916bd9 100644
--- a/drivers/gpu/drm/i915/i915_drv.h
+++ b/drivers/gpu/drm/i915/i915_drv.h
@@ -36,6 +36,7 @@
#include <linux/io-mapping.h>
#include <linux/i2c.h>
#include <drm/intel-gtt.h>
+#include <linux/backlight.h>
/* General customization:
*/
@@ -544,6 +545,7 @@
u32 savePIPEB_LINK_M1;
u32 savePIPEB_LINK_N1;
u32 saveMCHBAR_RENDER_STANDBY;
+ u32 savePCH_PORT_HOTPLUG;
struct {
/** Bridge to intel-gtt-ko */
@@ -689,6 +691,7 @@
int child_dev_num;
struct child_device_config *child_dev;
struct drm_connector *int_lvds_connector;
+ struct drm_connector *int_edp_connector;
bool mchbar_need_disable;
@@ -722,6 +725,8 @@
/* list of fbdev register on this device */
struct intel_fbdev *fbdev;
+ struct backlight_device *backlight;
+
struct drm_property *broadcast_rgb_property;
struct drm_property *force_audio_property;
diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c
index d1cd8b8..a546a71 100644
--- a/drivers/gpu/drm/i915/i915_gem.c
+++ b/drivers/gpu/drm/i915/i915_gem.c
@@ -3112,7 +3112,7 @@
if (pipelined != obj->ring) {
ret = i915_gem_object_wait_rendering(obj);
- if (ret)
+ if (ret == -ERESTARTSYS)
return ret;
}
diff --git a/drivers/gpu/drm/i915/i915_irq.c b/drivers/gpu/drm/i915/i915_irq.c
index 23d1ae6..9cbb0cd 100644
--- a/drivers/gpu/drm/i915/i915_irq.c
+++ b/drivers/gpu/drm/i915/i915_irq.c
@@ -306,12 +306,15 @@
struct drm_mode_config *mode_config = &dev->mode_config;
struct intel_encoder *encoder;
+ mutex_lock(&mode_config->mutex);
DRM_DEBUG_KMS("running encoder hotplug functions\n");
list_for_each_entry(encoder, &mode_config->encoder_list, base.head)
if (encoder->hot_plug)
encoder->hot_plug(encoder);
+ mutex_unlock(&mode_config->mutex);
+
/* Just fire off a uevent and let userspace tell us what to do */
drm_helper_hpd_irq_event(dev);
}
@@ -2055,8 +2058,10 @@
dev->driver->get_vblank_counter = gm45_get_vblank_counter;
}
-
- dev->driver->get_vblank_timestamp = i915_get_vblank_timestamp;
+ if (drm_core_check_feature(dev, DRIVER_MODESET))
+ dev->driver->get_vblank_timestamp = i915_get_vblank_timestamp;
+ else
+ dev->driver->get_vblank_timestamp = NULL;
dev->driver->get_scanout_position = i915_get_crtc_scanoutpos;
if (IS_IVYBRIDGE(dev)) {
diff --git a/drivers/gpu/drm/i915/i915_reg.h b/drivers/gpu/drm/i915/i915_reg.h
index 02db299..5baaef4 100644
--- a/drivers/gpu/drm/i915/i915_reg.h
+++ b/drivers/gpu/drm/i915/i915_reg.h
@@ -78,6 +78,14 @@
#define GRDOM_RENDER (1<<2)
#define GRDOM_MEDIA (3<<2)
+#define GEN6_MBCUNIT_SNPCR 0x900c /* for LLC config */
+#define GEN6_MBC_SNPCR_SHIFT 21
+#define GEN6_MBC_SNPCR_MASK (3<<21)
+#define GEN6_MBC_SNPCR_MAX (0<<21)
+#define GEN6_MBC_SNPCR_MED (1<<21)
+#define GEN6_MBC_SNPCR_LOW (2<<21)
+#define GEN6_MBC_SNPCR_MIN (3<<21) /* only 1/16th of the cache is shared */
+
#define GEN6_GDRST 0x941c
#define GEN6_GRDOM_FULL (1 << 0)
#define GEN6_GRDOM_RENDER (1 << 1)
@@ -1310,6 +1318,7 @@
#define ADPA_PIPE_SELECT_MASK (1<<30)
#define ADPA_PIPE_A_SELECT 0
#define ADPA_PIPE_B_SELECT (1<<30)
+#define ADPA_PIPE_SELECT(pipe) ((pipe) << 30)
#define ADPA_USE_VGA_HVPOLARITY (1<<15)
#define ADPA_SETS_HVPOLARITY 0
#define ADPA_VSYNC_CNTL_DISABLE (1<<11)
@@ -1452,6 +1461,7 @@
/* Selects pipe B for LVDS data. Must be set on pre-965. */
#define LVDS_PIPEB_SELECT (1 << 30)
#define LVDS_PIPE_MASK (1 << 30)
+#define LVDS_PIPE(pipe) ((pipe) << 30)
/* LVDS dithering flag on 965/g4x platform */
#define LVDS_ENABLE_DITHER (1 << 25)
/* LVDS sync polarity flags. Set to invert (i.e. negative) */
@@ -1491,9 +1501,6 @@
#define LVDS_B0B3_POWER_DOWN (0 << 2)
#define LVDS_B0B3_POWER_UP (3 << 2)
-#define LVDS_PIPE_ENABLED(V, P) \
- (((V) & (LVDS_PIPE_MASK | LVDS_PORT_EN)) == ((P) << 30 | LVDS_PORT_EN))
-
/* Video Data Island Packet control */
#define VIDEO_DIP_DATA 0x61178
#define VIDEO_DIP_CTL 0x61170
@@ -1506,6 +1513,7 @@
#define VIDEO_DIP_SELECT_AVI (0 << 19)
#define VIDEO_DIP_SELECT_VENDOR (1 << 19)
#define VIDEO_DIP_SELECT_SPD (3 << 19)
+#define VIDEO_DIP_SELECT_MASK (3 << 19)
#define VIDEO_DIP_FREQ_ONCE (0 << 16)
#define VIDEO_DIP_FREQ_VSYNC (1 << 16)
#define VIDEO_DIP_FREQ_2VSYNC (2 << 16)
@@ -2084,9 +2092,6 @@
#define DP_PIPEB_SELECT (1 << 30)
#define DP_PIPE_MASK (1 << 30)
-#define DP_PIPE_ENABLED(V, P) \
- (((V) & (DP_PIPE_MASK | DP_PORT_EN)) == ((P) << 30 | DP_PORT_EN))
-
/* Link training mode - select a suitable mode for each stage */
#define DP_LINK_TRAIN_PAT_1 (0 << 28)
#define DP_LINK_TRAIN_PAT_2 (1 << 28)
@@ -3024,6 +3029,20 @@
#define _TRANSA_DP_LINK_M2 0xe0048
#define _TRANSA_DP_LINK_N2 0xe004c
+/* Per-transcoder DIP controls */
+
+#define _VIDEO_DIP_CTL_A 0xe0200
+#define _VIDEO_DIP_DATA_A 0xe0208
+#define _VIDEO_DIP_GCP_A 0xe0210
+
+#define _VIDEO_DIP_CTL_B 0xe1200
+#define _VIDEO_DIP_DATA_B 0xe1208
+#define _VIDEO_DIP_GCP_B 0xe1210
+
+#define TVIDEO_DIP_CTL(pipe) _PIPE(pipe, _VIDEO_DIP_CTL_A, _VIDEO_DIP_CTL_B)
+#define TVIDEO_DIP_DATA(pipe) _PIPE(pipe, _VIDEO_DIP_DATA_A, _VIDEO_DIP_DATA_B)
+#define TVIDEO_DIP_GCP(pipe) _PIPE(pipe, _VIDEO_DIP_GCP_A, _VIDEO_DIP_GCP_B)
+
#define _TRANS_HTOTAL_B 0xe1000
#define _TRANS_HBLANK_B 0xe1004
#define _TRANS_HSYNC_B 0xe1008
@@ -3076,6 +3095,16 @@
#define TRANS_6BPC (2<<5)
#define TRANS_12BPC (3<<5)
+#define _TRANSA_CHICKEN2 0xf0064
+#define _TRANSB_CHICKEN2 0xf1064
+#define TRANS_CHICKEN2(pipe) _PIPE(pipe, _TRANSA_CHICKEN2, _TRANSB_CHICKEN2)
+#define TRANS_AUTOTRAIN_GEN_STALL_DIS (1<<31)
+
+#define SOUTH_CHICKEN1 0xc2000
+#define FDIA_PHASE_SYNC_SHIFT_OVR 19
+#define FDIA_PHASE_SYNC_SHIFT_EN 18
+#define FDI_PHASE_SYNC_OVR(pipe) (1<<(FDIA_PHASE_SYNC_SHIFT_OVR - ((pipe) * 2)))
+#define FDI_PHASE_SYNC_EN(pipe) (1<<(FDIA_PHASE_SYNC_SHIFT_EN - ((pipe) * 2)))
#define SOUTH_CHICKEN2 0xc2004
#define DPLS_EDP_PPS_FIX_DIS (1<<0)
@@ -3226,14 +3255,12 @@
#define ADPA_CRT_HOTPLUG_VOLREF_475MV (1<<17)
#define ADPA_CRT_HOTPLUG_FORCE_TRIGGER (1<<16)
-#define ADPA_PIPE_ENABLED(V, P) \
- (((V) & (ADPA_TRANS_SELECT_MASK | ADPA_DAC_ENABLE)) == ((P) << 30 | ADPA_DAC_ENABLE))
-
/* or SDVOB */
#define HDMIB 0xe1140
#define PORT_ENABLE (1 << 31)
#define TRANSCODER_A (0)
#define TRANSCODER_B (1 << 30)
+#define TRANSCODER(pipe) ((pipe) << 30)
#define TRANSCODER_MASK (1 << 30)
#define COLOR_FORMAT_8bpc (0)
#define COLOR_FORMAT_12bpc (3 << 26)
@@ -3250,9 +3277,6 @@
#define HSYNC_ACTIVE_HIGH (1 << 3)
#define PORT_DETECTED (1 << 2)
-#define HDMI_PIPE_ENABLED(V, P) \
- (((V) & (TRANSCODER_MASK | PORT_ENABLE)) == ((P) << 30 | PORT_ENABLE))
-
/* PCH SDVOB multiplex with HDMIB */
#define PCH_SDVOB HDMIB
@@ -3319,6 +3343,7 @@
#define PORT_TRANS_B_SEL_CPT (1<<29)
#define PORT_TRANS_C_SEL_CPT (2<<29)
#define PORT_TRANS_SEL_MASK (3<<29)
+#define PORT_TRANS_SEL_CPT(pipe) ((pipe) << 29)
#define TRANS_DP_CTL_A 0xe0300
#define TRANS_DP_CTL_B 0xe1300
diff --git a/drivers/gpu/drm/i915/i915_suspend.c b/drivers/gpu/drm/i915/i915_suspend.c
index 2857586..f107423 100644
--- a/drivers/gpu/drm/i915/i915_suspend.c
+++ b/drivers/gpu/drm/i915/i915_suspend.c
@@ -812,6 +812,7 @@
dev_priv->saveFDI_RXB_IMR = I915_READ(_FDI_RXB_IMR);
dev_priv->saveMCHBAR_RENDER_STANDBY =
I915_READ(RSTDBYCTL);
+ dev_priv->savePCH_PORT_HOTPLUG = I915_READ(PCH_PORT_HOTPLUG);
} else {
dev_priv->saveIER = I915_READ(IER);
dev_priv->saveIMR = I915_READ(IMR);
@@ -863,13 +864,15 @@
I915_WRITE(GTIMR, dev_priv->saveGTIMR);
I915_WRITE(_FDI_RXA_IMR, dev_priv->saveFDI_RXA_IMR);
I915_WRITE(_FDI_RXB_IMR, dev_priv->saveFDI_RXB_IMR);
+ I915_WRITE(PCH_PORT_HOTPLUG, dev_priv->savePCH_PORT_HOTPLUG);
} else {
I915_WRITE(IER, dev_priv->saveIER);
I915_WRITE(IMR, dev_priv->saveIMR);
}
mutex_unlock(&dev->struct_mutex);
- intel_init_clock_gating(dev);
+ if (drm_core_check_feature(dev, DRIVER_MODESET))
+ intel_init_clock_gating(dev);
if (IS_IRONLAKE_M(dev)) {
ironlake_enable_drps(dev);
diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c
index 393a399..ee1d701 100644
--- a/drivers/gpu/drm/i915/intel_display.c
+++ b/drivers/gpu/drm/i915/intel_display.c
@@ -980,11 +980,76 @@
pipe_name(pipe));
}
+static bool dp_pipe_enabled(struct drm_i915_private *dev_priv,
+ enum pipe pipe, u32 port_sel, u32 val)
+{
+ if ((val & DP_PORT_EN) == 0)
+ return false;
+
+ if (HAS_PCH_CPT(dev_priv->dev)) {
+ u32 trans_dp_ctl_reg = TRANS_DP_CTL(pipe);
+ u32 trans_dp_ctl = I915_READ(trans_dp_ctl_reg);
+ if ((trans_dp_ctl & TRANS_DP_PORT_SEL_MASK) != port_sel)
+ return false;
+ } else {
+ if ((val & DP_PIPE_MASK) != (pipe << 30))
+ return false;
+ }
+ return true;
+}
+
+static bool hdmi_pipe_enabled(struct drm_i915_private *dev_priv,
+ enum pipe pipe, u32 val)
+{
+ if ((val & PORT_ENABLE) == 0)
+ return false;
+
+ if (HAS_PCH_CPT(dev_priv->dev)) {
+ if ((val & PORT_TRANS_SEL_MASK) != PORT_TRANS_SEL_CPT(pipe))
+ return false;
+ } else {
+ if ((val & TRANSCODER_MASK) != TRANSCODER(pipe))
+ return false;
+ }
+ return true;
+}
+
+static bool lvds_pipe_enabled(struct drm_i915_private *dev_priv,
+ enum pipe pipe, u32 val)
+{
+ if ((val & LVDS_PORT_EN) == 0)
+ return false;
+
+ if (HAS_PCH_CPT(dev_priv->dev)) {
+ if ((val & PORT_TRANS_SEL_MASK) != PORT_TRANS_SEL_CPT(pipe))
+ return false;
+ } else {
+ if ((val & LVDS_PIPE_MASK) != LVDS_PIPE(pipe))
+ return false;
+ }
+ return true;
+}
+
+static bool adpa_pipe_enabled(struct drm_i915_private *dev_priv,
+ enum pipe pipe, u32 val)
+{
+ if ((val & ADPA_DAC_ENABLE) == 0)
+ return false;
+ if (HAS_PCH_CPT(dev_priv->dev)) {
+ if ((val & PORT_TRANS_SEL_MASK) != PORT_TRANS_SEL_CPT(pipe))
+ return false;
+ } else {
+ if ((val & ADPA_PIPE_SELECT_MASK) != ADPA_PIPE_SELECT(pipe))
+ return false;
+ }
+ return true;
+}
+
static void assert_pch_dp_disabled(struct drm_i915_private *dev_priv,
- enum pipe pipe, int reg)
+ enum pipe pipe, int reg, u32 port_sel)
{
u32 val = I915_READ(reg);
- WARN(DP_PIPE_ENABLED(val, pipe),
+ WARN(dp_pipe_enabled(dev_priv, pipe, port_sel, val),
"PCH DP (0x%08x) enabled on transcoder %c, should be disabled\n",
reg, pipe_name(pipe));
}
@@ -993,7 +1058,7 @@
enum pipe pipe, int reg)
{
u32 val = I915_READ(reg);
- WARN(HDMI_PIPE_ENABLED(val, pipe),
+ WARN(hdmi_pipe_enabled(dev_priv, val, pipe),
"PCH DP (0x%08x) enabled on transcoder %c, should be disabled\n",
reg, pipe_name(pipe));
}
@@ -1004,19 +1069,19 @@
int reg;
u32 val;
- assert_pch_dp_disabled(dev_priv, pipe, PCH_DP_B);
- assert_pch_dp_disabled(dev_priv, pipe, PCH_DP_C);
- assert_pch_dp_disabled(dev_priv, pipe, PCH_DP_D);
+ assert_pch_dp_disabled(dev_priv, pipe, PCH_DP_B, TRANS_DP_PORT_SEL_B);
+ assert_pch_dp_disabled(dev_priv, pipe, PCH_DP_C, TRANS_DP_PORT_SEL_C);
+ assert_pch_dp_disabled(dev_priv, pipe, PCH_DP_D, TRANS_DP_PORT_SEL_D);
reg = PCH_ADPA;
val = I915_READ(reg);
- WARN(ADPA_PIPE_ENABLED(val, pipe),
+ WARN(adpa_pipe_enabled(dev_priv, val, pipe),
"PCH VGA enabled on transcoder %c, should be disabled\n",
pipe_name(pipe));
reg = PCH_LVDS;
val = I915_READ(reg);
- WARN(LVDS_PIPE_ENABLED(val, pipe),
+ WARN(lvds_pipe_enabled(dev_priv, val, pipe),
"PCH LVDS enabled on transcoder %c, should be disabled\n",
pipe_name(pipe));
@@ -1276,6 +1341,17 @@
intel_wait_for_pipe_off(dev_priv->dev, pipe);
}
+/*
+ * Plane regs are double buffered, going from enabled->disabled needs a
+ * trigger in order to latch. The display address reg provides this.
+ */
+static void intel_flush_display_plane(struct drm_i915_private *dev_priv,
+ enum plane plane)
+{
+ I915_WRITE(DSPADDR(plane), I915_READ(DSPADDR(plane)));
+ I915_WRITE(DSPSURF(plane), I915_READ(DSPSURF(plane)));
+}
+
/**
* intel_enable_plane - enable a display plane on a given pipe
* @dev_priv: i915 private structure
@@ -1299,20 +1375,10 @@
return;
I915_WRITE(reg, val | DISPLAY_PLANE_ENABLE);
+ intel_flush_display_plane(dev_priv, plane);
intel_wait_for_vblank(dev_priv->dev, pipe);
}
-/*
- * Plane regs are double buffered, going from enabled->disabled needs a
- * trigger in order to latch. The display address reg provides this.
- */
-static void intel_flush_display_plane(struct drm_i915_private *dev_priv,
- enum plane plane)
-{
- u32 reg = DSPADDR(plane);
- I915_WRITE(reg, I915_READ(reg));
-}
-
/**
* intel_disable_plane - disable a display plane
* @dev_priv: i915 private structure
@@ -1338,19 +1404,24 @@
}
static void disable_pch_dp(struct drm_i915_private *dev_priv,
- enum pipe pipe, int reg)
+ enum pipe pipe, int reg, u32 port_sel)
{
u32 val = I915_READ(reg);
- if (DP_PIPE_ENABLED(val, pipe))
+ if (dp_pipe_enabled(dev_priv, pipe, port_sel, val)) {
+ DRM_DEBUG_KMS("Disabling pch dp %x on pipe %d\n", reg, pipe);
I915_WRITE(reg, val & ~DP_PORT_EN);
+ }
}
static void disable_pch_hdmi(struct drm_i915_private *dev_priv,
enum pipe pipe, int reg)
{
u32 val = I915_READ(reg);
- if (HDMI_PIPE_ENABLED(val, pipe))
+ if (hdmi_pipe_enabled(dev_priv, val, pipe)) {
+ DRM_DEBUG_KMS("Disabling pch HDMI %x on pipe %d\n",
+ reg, pipe);
I915_WRITE(reg, val & ~PORT_ENABLE);
+ }
}
/* Disable any ports connected to this transcoder */
@@ -1362,18 +1433,19 @@
val = I915_READ(PCH_PP_CONTROL);
I915_WRITE(PCH_PP_CONTROL, val | PANEL_UNLOCK_REGS);
- disable_pch_dp(dev_priv, pipe, PCH_DP_B);
- disable_pch_dp(dev_priv, pipe, PCH_DP_C);
- disable_pch_dp(dev_priv, pipe, PCH_DP_D);
+ disable_pch_dp(dev_priv, pipe, PCH_DP_B, TRANS_DP_PORT_SEL_B);
+ disable_pch_dp(dev_priv, pipe, PCH_DP_C, TRANS_DP_PORT_SEL_C);
+ disable_pch_dp(dev_priv, pipe, PCH_DP_D, TRANS_DP_PORT_SEL_D);
reg = PCH_ADPA;
val = I915_READ(reg);
- if (ADPA_PIPE_ENABLED(val, pipe))
+ if (adpa_pipe_enabled(dev_priv, val, pipe))
I915_WRITE(reg, val & ~ADPA_DAC_ENABLE);
reg = PCH_LVDS;
val = I915_READ(reg);
- if (LVDS_PIPE_ENABLED(val, pipe)) {
+ if (lvds_pipe_enabled(dev_priv, val, pipe)) {
+ DRM_DEBUG_KMS("disable lvds on pipe %d val 0x%08x\n", pipe, val);
I915_WRITE(reg, val & ~LVDS_PORT_EN);
POSTING_READ(reg);
udelay(100);
@@ -2096,7 +2168,7 @@
/* no fb bound */
if (!crtc->fb) {
- DRM_DEBUG_KMS("No FB bound\n");
+ DRM_ERROR("No FB bound\n");
return 0;
}
@@ -2105,6 +2177,7 @@
case 1:
break;
default:
+ DRM_ERROR("no plane for crtc\n");
return -EINVAL;
}
@@ -2114,6 +2187,7 @@
NULL);
if (ret != 0) {
mutex_unlock(&dev->struct_mutex);
+ DRM_ERROR("pin & fence failed\n");
return ret;
}
@@ -2142,6 +2216,7 @@
if (ret) {
i915_gem_object_unpin(to_intel_framebuffer(crtc->fb)->obj);
mutex_unlock(&dev->struct_mutex);
+ DRM_ERROR("failed to update base address\n");
return ret;
}
@@ -2248,6 +2323,18 @@
FDI_FE_ERRC_ENABLE);
}
+static void cpt_phase_pointer_enable(struct drm_device *dev, int pipe)
+{
+ struct drm_i915_private *dev_priv = dev->dev_private;
+ u32 flags = I915_READ(SOUTH_CHICKEN1);
+
+ flags |= FDI_PHASE_SYNC_OVR(pipe);
+ I915_WRITE(SOUTH_CHICKEN1, flags); /* once to unlock... */
+ flags |= FDI_PHASE_SYNC_EN(pipe);
+ I915_WRITE(SOUTH_CHICKEN1, flags); /* then again to enable */
+ POSTING_READ(SOUTH_CHICKEN1);
+}
+
/* The FDI link training functions for ILK/Ibexpeak. */
static void ironlake_fdi_link_train(struct drm_crtc *crtc)
{
@@ -2398,6 +2485,9 @@
POSTING_READ(reg);
udelay(150);
+ if (HAS_PCH_CPT(dev))
+ cpt_phase_pointer_enable(dev, pipe);
+
for (i = 0; i < 4; i++ ) {
reg = FDI_TX_CTL(pipe);
temp = I915_READ(reg);
@@ -2514,6 +2604,9 @@
POSTING_READ(reg);
udelay(150);
+ if (HAS_PCH_CPT(dev))
+ cpt_phase_pointer_enable(dev, pipe);
+
for (i = 0; i < 4; i++ ) {
reg = FDI_TX_CTL(pipe);
temp = I915_READ(reg);
@@ -2623,6 +2716,17 @@
}
}
+static void cpt_phase_pointer_disable(struct drm_device *dev, int pipe)
+{
+ struct drm_i915_private *dev_priv = dev->dev_private;
+ u32 flags = I915_READ(SOUTH_CHICKEN1);
+
+ flags &= ~(FDI_PHASE_SYNC_EN(pipe));
+ I915_WRITE(SOUTH_CHICKEN1, flags); /* once to disable... */
+ flags &= ~(FDI_PHASE_SYNC_OVR(pipe));
+ I915_WRITE(SOUTH_CHICKEN1, flags); /* then again to lock */
+ POSTING_READ(SOUTH_CHICKEN1);
+}
static void ironlake_fdi_disable(struct drm_crtc *crtc)
{
struct drm_device *dev = crtc->dev;
@@ -2652,6 +2756,8 @@
I915_WRITE(FDI_RX_CHICKEN(pipe),
I915_READ(FDI_RX_CHICKEN(pipe) &
~FDI_RX_PHASE_SYNC_POINTER_EN));
+ } else if (HAS_PCH_CPT(dev)) {
+ cpt_phase_pointer_disable(dev, pipe);
}
/* still set train pattern 1 */
@@ -2862,14 +2968,18 @@
I915_WRITE(PF_WIN_SZ(pipe), dev_priv->pch_pf_size);
}
+ /*
+ * On ILK+ LUT must be loaded before the pipe is running but with
+ * clocks enabled
+ */
+ intel_crtc_load_lut(crtc);
+
intel_enable_pipe(dev_priv, pipe, is_pch_port);
intel_enable_plane(dev_priv, plane, pipe);
if (is_pch_port)
ironlake_pch_enable(crtc);
- intel_crtc_load_lut(crtc);
-
mutex_lock(&dev->struct_mutex);
intel_update_fbc(dev);
mutex_unlock(&dev->struct_mutex);
@@ -4538,7 +4648,9 @@
if (connector->encoder != encoder)
continue;
- if (connector->display_info.bpc < display_bpc) {
+ /* Don't use an invalid EDID bpc value */
+ if (connector->display_info.bpc &&
+ connector->display_info.bpc < display_bpc) {
DRM_DEBUG_DRIVER("clamping display bpc (was %d) to EDID reported max of %d\n", display_bpc, connector->display_info.bpc);
display_bpc = connector->display_info.bpc;
}
@@ -4985,6 +5097,81 @@
return ret;
}
+static void ironlake_update_pch_refclk(struct drm_device *dev)
+{
+ struct drm_i915_private *dev_priv = dev->dev_private;
+ struct drm_mode_config *mode_config = &dev->mode_config;
+ struct drm_crtc *crtc;
+ struct intel_encoder *encoder;
+ struct intel_encoder *has_edp_encoder = NULL;
+ u32 temp;
+ bool has_lvds = false;
+
+ /* We need to take the global config into account */
+ list_for_each_entry(crtc, &mode_config->crtc_list, head) {
+ if (!crtc->enabled)
+ continue;
+
+ list_for_each_entry(encoder, &mode_config->encoder_list,
+ base.head) {
+ if (encoder->base.crtc != crtc)
+ continue;
+
+ switch (encoder->type) {
+ case INTEL_OUTPUT_LVDS:
+ has_lvds = true;
+ case INTEL_OUTPUT_EDP:
+ has_edp_encoder = encoder;
+ break;
+ }
+ }
+ }
+
+ /* Ironlake: try to setup display ref clock before DPLL
+ * enabling. This is only under driver's control after
+ * PCH B stepping, previous chipset stepping should be
+ * ignoring this setting.
+ */
+ temp = I915_READ(PCH_DREF_CONTROL);
+ /* Always enable nonspread source */
+ temp &= ~DREF_NONSPREAD_SOURCE_MASK;
+ temp |= DREF_NONSPREAD_SOURCE_ENABLE;
+ temp &= ~DREF_SSC_SOURCE_MASK;
+ temp |= DREF_SSC_SOURCE_ENABLE;
+ I915_WRITE(PCH_DREF_CONTROL, temp);
+
+ POSTING_READ(PCH_DREF_CONTROL);
+ udelay(200);
+
+ if (has_edp_encoder) {
+ if (intel_panel_use_ssc(dev_priv)) {
+ temp |= DREF_SSC1_ENABLE;
+ I915_WRITE(PCH_DREF_CONTROL, temp);
+
+ POSTING_READ(PCH_DREF_CONTROL);
+ udelay(200);
+ }
+ temp &= ~DREF_CPU_SOURCE_OUTPUT_MASK;
+
+ /* Enable CPU source on CPU attached eDP */
+ if (!intel_encoder_is_pch_edp(&has_edp_encoder->base)) {
+ if (intel_panel_use_ssc(dev_priv))
+ temp |= DREF_CPU_SOURCE_OUTPUT_DOWNSPREAD;
+ else
+ temp |= DREF_CPU_SOURCE_OUTPUT_NONSPREAD;
+ } else {
+ /* Enable SSC on PCH eDP if needed */
+ if (intel_panel_use_ssc(dev_priv)) {
+ DRM_ERROR("enabling SSC on PCH\n");
+ temp |= DREF_SUPERSPREAD_SOURCE_ENABLE;
+ }
+ }
+ I915_WRITE(PCH_DREF_CONTROL, temp);
+ POSTING_READ(PCH_DREF_CONTROL);
+ udelay(200);
+ }
+}
+
static int ironlake_crtc_mode_set(struct drm_crtc *crtc,
struct drm_display_mode *mode,
struct drm_display_mode *adjusted_mode,
@@ -5153,7 +5340,8 @@
temp |= PIPE_12BPC;
break;
default:
- WARN(1, "intel_choose_pipe_bpp returned invalid value\n");
+ WARN(1, "intel_choose_pipe_bpp returned invalid value %d\n",
+ pipe_bpp);
temp |= PIPE_8BPC;
pipe_bpp = 24;
break;
@@ -5179,49 +5367,7 @@
ironlake_compute_m_n(intel_crtc->bpp, lane, target_clock, link_bw,
&m_n);
- /* Ironlake: try to setup display ref clock before DPLL
- * enabling. This is only under driver's control after
- * PCH B stepping, previous chipset stepping should be
- * ignoring this setting.
- */
- temp = I915_READ(PCH_DREF_CONTROL);
- /* Always enable nonspread source */
- temp &= ~DREF_NONSPREAD_SOURCE_MASK;
- temp |= DREF_NONSPREAD_SOURCE_ENABLE;
- temp &= ~DREF_SSC_SOURCE_MASK;
- temp |= DREF_SSC_SOURCE_ENABLE;
- I915_WRITE(PCH_DREF_CONTROL, temp);
-
- POSTING_READ(PCH_DREF_CONTROL);
- udelay(200);
-
- if (has_edp_encoder) {
- if (intel_panel_use_ssc(dev_priv)) {
- temp |= DREF_SSC1_ENABLE;
- I915_WRITE(PCH_DREF_CONTROL, temp);
-
- POSTING_READ(PCH_DREF_CONTROL);
- udelay(200);
- }
- temp &= ~DREF_CPU_SOURCE_OUTPUT_MASK;
-
- /* Enable CPU source on CPU attached eDP */
- if (!intel_encoder_is_pch_edp(&has_edp_encoder->base)) {
- if (intel_panel_use_ssc(dev_priv))
- temp |= DREF_CPU_SOURCE_OUTPUT_DOWNSPREAD;
- else
- temp |= DREF_CPU_SOURCE_OUTPUT_NONSPREAD;
- } else {
- /* Enable SSC on PCH eDP if needed */
- if (intel_panel_use_ssc(dev_priv)) {
- DRM_ERROR("enabling SSC on PCH\n");
- temp |= DREF_SUPERSPREAD_SOURCE_ENABLE;
- }
- }
- I915_WRITE(PCH_DREF_CONTROL, temp);
- POSTING_READ(PCH_DREF_CONTROL);
- udelay(200);
- }
+ ironlake_update_pch_refclk(dev);
fp = clock.n << 16 | clock.m1 << 8 | clock.m2;
if (has_reduced_clock)
@@ -5238,7 +5384,7 @@
} else if (is_sdvo && is_tv)
factor = 20;
- if (clock.m1 < factor * clock.n)
+ if (clock.m < factor * clock.n)
fp |= FP_CB_TUNE;
dpll = 0;
@@ -5516,6 +5662,8 @@
drm_vblank_post_modeset(dev, pipe);
+ intel_crtc->dpms_mode = DRM_MODE_DPMS_ON;
+
return ret;
}
@@ -7714,10 +7862,12 @@
ILK_DPARB_CLK_GATE |
ILK_DPFD_CLK_GATE);
- for_each_pipe(pipe)
+ for_each_pipe(pipe) {
I915_WRITE(DSPCNTR(pipe),
I915_READ(DSPCNTR(pipe)) |
DISPPLANE_TRICKLE_FEED_DISABLE);
+ intel_flush_display_plane(dev_priv, pipe);
+ }
}
static void ivybridge_init_clock_gating(struct drm_device *dev)
@@ -7734,10 +7884,12 @@
I915_WRITE(ILK_DSPCLK_GATE, IVB_VRHUNIT_CLK_GATE);
- for_each_pipe(pipe)
+ for_each_pipe(pipe) {
I915_WRITE(DSPCNTR(pipe),
I915_READ(DSPCNTR(pipe)) |
DISPPLANE_TRICKLE_FEED_DISABLE);
+ intel_flush_display_plane(dev_priv, pipe);
+ }
}
static void g4x_init_clock_gating(struct drm_device *dev)
@@ -7820,6 +7972,7 @@
static void cpt_init_clock_gating(struct drm_device *dev)
{
struct drm_i915_private *dev_priv = dev->dev_private;
+ int pipe;
/*
* On Ibex Peak and Cougar Point, we need to disable clock
@@ -7829,6 +7982,9 @@
I915_WRITE(SOUTH_DSPCLK_GATE_D, PCH_DPLSUNIT_CLOCK_GATE_DISABLE);
I915_WRITE(SOUTH_CHICKEN2, I915_READ(SOUTH_CHICKEN2) |
DPLS_EDP_PPS_FIX_DIS);
+ /* Without this, mode sets may fail silently on FDI */
+ for_each_pipe(pipe)
+ I915_WRITE(TRANS_CHICKEN2(pipe), TRANS_AUTOTRAIN_GEN_STALL_DIS);
}
static void ironlake_teardown_rc6(struct drm_device *dev)
@@ -8178,6 +8334,9 @@
/* Lenovo U160 cannot use SSC on LVDS */
{ 0x0046, 0x17aa, 0x3920, quirk_ssc_force_disable },
+
+ /* Sony Vaio Y cannot use SSC on LVDS */
+ { 0x0046, 0x104d, 0x9076, quirk_ssc_force_disable },
};
static void intel_init_quirks(struct drm_device *dev)
diff --git a/drivers/gpu/drm/i915/intel_dp.c b/drivers/gpu/drm/i915/intel_dp.c
index f797fb5..44fef5e 100644
--- a/drivers/gpu/drm/i915/intel_dp.c
+++ b/drivers/gpu/drm/i915/intel_dp.c
@@ -50,9 +50,10 @@
bool has_audio;
int force_audio;
uint32_t color_range;
+ int dpms_mode;
uint8_t link_bw;
uint8_t lane_count;
- uint8_t dpcd[4];
+ uint8_t dpcd[8];
struct i2c_adapter adapter;
struct i2c_algo_dp_aux_data algo;
bool is_pch_edp;
@@ -316,9 +317,17 @@
else
precharge = 5;
- if (I915_READ(ch_ctl) & DP_AUX_CH_CTL_SEND_BUSY) {
- DRM_ERROR("dp_aux_ch not started status 0x%08x\n",
- I915_READ(ch_ctl));
+ /* Try to wait for any previous AUX channel activity */
+ for (try = 0; try < 3; try++) {
+ status = I915_READ(ch_ctl);
+ if ((status & DP_AUX_CH_CTL_SEND_BUSY) == 0)
+ break;
+ msleep(1);
+ }
+
+ if (try == 3) {
+ WARN(1, "dp_aux_ch not started status 0x%08x\n",
+ I915_READ(ch_ctl));
return -EBUSY;
}
@@ -770,6 +779,7 @@
memset(intel_dp->link_configuration, 0, DP_LINK_CONFIGURATION_SIZE);
intel_dp->link_configuration[0] = intel_dp->link_bw;
intel_dp->link_configuration[1] = intel_dp->lane_count;
+ intel_dp->link_configuration[8] = DP_SET_ANSI_8B10B;
/*
* Check for DPCD version > 1.1 and enhanced framing support
@@ -1011,6 +1021,8 @@
if (is_edp(intel_dp))
ironlake_edp_backlight_on(dev);
+
+ intel_dp->dpms_mode = DRM_MODE_DPMS_ON;
}
static void
@@ -1045,6 +1057,7 @@
if (is_edp(intel_dp))
ironlake_edp_backlight_on(dev);
}
+ intel_dp->dpms_mode = mode;
}
/*
@@ -1334,10 +1347,16 @@
u32 reg;
uint32_t DP = intel_dp->DP;
- /* Enable output, wait for it to become active */
- I915_WRITE(intel_dp->output_reg, intel_dp->DP);
- POSTING_READ(intel_dp->output_reg);
- intel_wait_for_vblank(dev, intel_crtc->pipe);
+ /*
+ * On CPT we have to enable the port in training pattern 1, which
+ * will happen below in intel_dp_set_link_train. Otherwise, enable
+ * the port and wait for it to become active.
+ */
+ if (!HAS_PCH_CPT(dev)) {
+ I915_WRITE(intel_dp->output_reg, intel_dp->DP);
+ POSTING_READ(intel_dp->output_reg);
+ intel_wait_for_vblank(dev, intel_crtc->pipe);
+ }
/* Write the link configuration data */
intel_dp_aux_native_write(intel_dp, DP_LINK_BW_SET,
@@ -1370,7 +1389,8 @@
reg = DP | DP_LINK_TRAIN_PAT_1;
if (!intel_dp_set_link_train(intel_dp, reg,
- DP_TRAINING_PATTERN_1))
+ DP_TRAINING_PATTERN_1 |
+ DP_LINK_SCRAMBLING_DISABLE))
break;
/* Set training pattern 1 */
@@ -1445,7 +1465,8 @@
/* channel eq pattern */
if (!intel_dp_set_link_train(intel_dp, reg,
- DP_TRAINING_PATTERN_2))
+ DP_TRAINING_PATTERN_2 |
+ DP_LINK_SCRAMBLING_DISABLE))
break;
udelay(400);
@@ -1559,6 +1580,18 @@
POSTING_READ(intel_dp->output_reg);
}
+static bool
+intel_dp_get_dpcd(struct intel_dp *intel_dp)
+{
+ if (intel_dp_aux_native_read_retry(intel_dp, 0x000, intel_dp->dpcd,
+ sizeof (intel_dp->dpcd)) &&
+ (intel_dp->dpcd[DP_DPCD_REV] != 0)) {
+ return true;
+ }
+
+ return false;
+}
+
/*
* According to DP spec
* 5.1.2:
@@ -1571,36 +1604,44 @@
static void
intel_dp_check_link_status(struct intel_dp *intel_dp)
{
- int ret;
+ if (intel_dp->dpms_mode != DRM_MODE_DPMS_ON)
+ return;
if (!intel_dp->base.base.crtc)
return;
+ /* Try to read receiver status if the link appears to be up */
if (!intel_dp_get_link_status(intel_dp)) {
intel_dp_link_down(intel_dp);
return;
}
- /* Try to read receiver status if the link appears to be up */
- ret = intel_dp_aux_native_read(intel_dp,
- 0x000, intel_dp->dpcd,
- sizeof (intel_dp->dpcd));
- if (ret != sizeof(intel_dp->dpcd)) {
+ /* Now read the DPCD to see if it's actually running */
+ if (!intel_dp_get_dpcd(intel_dp)) {
intel_dp_link_down(intel_dp);
return;
}
if (!intel_channel_eq_ok(intel_dp)) {
+ DRM_DEBUG_KMS("%s: channel EQ not ok, retraining\n",
+ drm_get_encoder_name(&intel_dp->base.base));
intel_dp_start_link_train(intel_dp);
intel_dp_complete_link_train(intel_dp);
}
}
static enum drm_connector_status
+intel_dp_detect_dpcd(struct intel_dp *intel_dp)
+{
+ if (intel_dp_get_dpcd(intel_dp))
+ return connector_status_connected;
+ return connector_status_disconnected;
+}
+
+static enum drm_connector_status
ironlake_dp_detect(struct intel_dp *intel_dp)
{
enum drm_connector_status status;
- bool ret;
/* Can't disconnect eDP, but you can close the lid... */
if (is_edp(intel_dp)) {
@@ -1610,15 +1651,7 @@
return status;
}
- status = connector_status_disconnected;
- ret = intel_dp_aux_native_read_retry(intel_dp,
- 0x000, intel_dp->dpcd,
- sizeof (intel_dp->dpcd));
- if (ret && intel_dp->dpcd[DP_DPCD_REV] != 0)
- status = connector_status_connected;
- DRM_DEBUG_KMS("DPCD: %hx%hx%hx%hx\n", intel_dp->dpcd[0],
- intel_dp->dpcd[1], intel_dp->dpcd[2], intel_dp->dpcd[3]);
- return status;
+ return intel_dp_detect_dpcd(intel_dp);
}
static enum drm_connector_status
@@ -1626,7 +1659,6 @@
{
struct drm_device *dev = intel_dp->base.base.dev;
struct drm_i915_private *dev_priv = dev->dev_private;
- enum drm_connector_status status;
uint32_t temp, bit;
switch (intel_dp->output_reg) {
@@ -1648,15 +1680,7 @@
if ((temp & bit) == 0)
return connector_status_disconnected;
- status = connector_status_disconnected;
- if (intel_dp_aux_native_read(intel_dp, 0x000, intel_dp->dpcd,
- sizeof (intel_dp->dpcd)) == sizeof (intel_dp->dpcd))
- {
- if (intel_dp->dpcd[DP_DPCD_REV] != 0)
- status = connector_status_connected;
- }
-
- return status;
+ return intel_dp_detect_dpcd(intel_dp);
}
/**
@@ -1679,6 +1703,12 @@
status = ironlake_dp_detect(intel_dp);
else
status = g4x_dp_detect(intel_dp);
+
+ DRM_DEBUG_KMS("DPCD: %02hx%02hx%02hx%02hx%02hx%02hx%02hx%02hx\n",
+ intel_dp->dpcd[0], intel_dp->dpcd[1], intel_dp->dpcd[2],
+ intel_dp->dpcd[3], intel_dp->dpcd[4], intel_dp->dpcd[5],
+ intel_dp->dpcd[6], intel_dp->dpcd[7]);
+
if (status != connector_status_connected)
return status;
@@ -1811,6 +1841,11 @@
static void
intel_dp_destroy (struct drm_connector *connector)
{
+ struct drm_device *dev = connector->dev;
+
+ if (intel_dpd_is_edp(dev))
+ intel_panel_destroy_backlight(dev);
+
drm_sysfs_connector_remove(connector);
drm_connector_cleanup(connector);
kfree(connector);
@@ -1924,6 +1959,7 @@
return;
intel_dp->output_reg = output_reg;
+ intel_dp->dpms_mode = -1;
intel_connector = kzalloc(sizeof(struct intel_connector), GFP_KERNEL);
if (!intel_connector) {
@@ -2000,7 +2036,7 @@
/* Cache some DPCD data in the eDP case */
if (is_edp(intel_dp)) {
- int ret;
+ bool ret;
u32 pp_on, pp_div;
pp_on = I915_READ(PCH_PP_ON_DELAYS);
@@ -2013,11 +2049,9 @@
dev_priv->panel_t12 *= 100; /* t12 in 100ms units */
ironlake_edp_panel_vdd_on(intel_dp);
- ret = intel_dp_aux_native_read(intel_dp, DP_DPCD_REV,
- intel_dp->dpcd,
- sizeof(intel_dp->dpcd));
+ ret = intel_dp_get_dpcd(intel_dp);
ironlake_edp_panel_vdd_off(intel_dp);
- if (ret == sizeof(intel_dp->dpcd)) {
+ if (ret) {
if (intel_dp->dpcd[DP_DPCD_REV] >= 0x11)
dev_priv->no_aux_handshake =
intel_dp->dpcd[DP_MAX_DOWNSPREAD] &
@@ -2043,6 +2077,8 @@
DRM_MODE_TYPE_PREFERRED;
}
}
+ dev_priv->int_edp_connector = connector;
+ intel_panel_setup_backlight(dev);
}
intel_dp_add_properties(intel_dp, connector);
diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h
index 6e990f9..0b2ee9d 100644
--- a/drivers/gpu/drm/i915/intel_drv.h
+++ b/drivers/gpu/drm/i915/intel_drv.h
@@ -178,10 +178,28 @@
#define to_intel_encoder(x) container_of(x, struct intel_encoder, base)
#define to_intel_framebuffer(x) container_of(x, struct intel_framebuffer, base)
+#define DIP_HEADER_SIZE 5
+
#define DIP_TYPE_AVI 0x82
#define DIP_VERSION_AVI 0x2
#define DIP_LEN_AVI 13
+#define DIP_TYPE_SPD 0x3
+#define DIP_VERSION_SPD 0x1
+#define DIP_LEN_SPD 25
+#define DIP_SPD_UNKNOWN 0
+#define DIP_SPD_DSTB 0x1
+#define DIP_SPD_DVDP 0x2
+#define DIP_SPD_DVHS 0x3
+#define DIP_SPD_HDDVR 0x4
+#define DIP_SPD_DVC 0x5
+#define DIP_SPD_DSC 0x6
+#define DIP_SPD_VCD 0x7
+#define DIP_SPD_GAME 0x8
+#define DIP_SPD_PC 0x9
+#define DIP_SPD_BD 0xa
+#define DIP_SPD_SCD 0xb
+
struct dip_infoframe {
uint8_t type; /* HB0 */
uint8_t ver; /* HB1 */
@@ -206,6 +224,11 @@
uint16_t left_bar_end;
uint16_t right_bar_start;
} avi;
+ struct {
+ uint8_t vn[8];
+ uint8_t pd[16];
+ uint8_t sdi;
+ } spd;
uint8_t payload[27];
} __attribute__ ((packed)) body;
} __attribute__((packed));
@@ -274,9 +297,10 @@
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 int 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_panel_destroy_backlight(struct drm_device *dev);
extern enum drm_connector_status intel_panel_detect(struct drm_device *dev);
extern void intel_crtc_load_lut(struct drm_crtc *crtc);
diff --git a/drivers/gpu/drm/i915/intel_hdmi.c b/drivers/gpu/drm/i915/intel_hdmi.c
index 1ed8e69..226ba83 100644
--- a/drivers/gpu/drm/i915/intel_hdmi.c
+++ b/drivers/gpu/drm/i915/intel_hdmi.c
@@ -45,6 +45,8 @@
bool has_hdmi_sink;
bool has_audio;
int force_audio;
+ void (*write_infoframe)(struct drm_encoder *encoder,
+ struct dip_infoframe *frame);
};
static struct intel_hdmi *enc_to_intel_hdmi(struct drm_encoder *encoder)
@@ -58,37 +60,70 @@
struct intel_hdmi, base);
}
-void intel_dip_infoframe_csum(struct dip_infoframe *avi_if)
+void intel_dip_infoframe_csum(struct dip_infoframe *frame)
{
- uint8_t *data = (uint8_t *)avi_if;
+ uint8_t *data = (uint8_t *)frame;
uint8_t sum = 0;
unsigned i;
- avi_if->checksum = 0;
- avi_if->ecc = 0;
+ frame->checksum = 0;
+ frame->ecc = 0;
- for (i = 0; i < sizeof(*avi_if); i++)
+ /* Header isn't part of the checksum */
+ for (i = 5; i < frame->len; i++)
sum += data[i];
- avi_if->checksum = 0x100 - sum;
+ frame->checksum = 0x100 - sum;
}
-static void intel_hdmi_set_avi_infoframe(struct drm_encoder *encoder)
+static u32 intel_infoframe_index(struct dip_infoframe *frame)
{
- struct dip_infoframe avi_if = {
- .type = DIP_TYPE_AVI,
- .ver = DIP_VERSION_AVI,
- .len = DIP_LEN_AVI,
- };
- uint32_t *data = (uint32_t *)&avi_if;
+ u32 flags = 0;
+
+ switch (frame->type) {
+ case DIP_TYPE_AVI:
+ flags |= VIDEO_DIP_SELECT_AVI;
+ break;
+ case DIP_TYPE_SPD:
+ flags |= VIDEO_DIP_SELECT_SPD;
+ break;
+ default:
+ DRM_DEBUG_DRIVER("unknown info frame type %d\n", frame->type);
+ break;
+ }
+
+ return flags;
+}
+
+static u32 intel_infoframe_flags(struct dip_infoframe *frame)
+{
+ u32 flags = 0;
+
+ switch (frame->type) {
+ case DIP_TYPE_AVI:
+ flags |= VIDEO_DIP_ENABLE_AVI | VIDEO_DIP_FREQ_VSYNC;
+ break;
+ case DIP_TYPE_SPD:
+ flags |= VIDEO_DIP_ENABLE_SPD | VIDEO_DIP_FREQ_2VSYNC;
+ break;
+ default:
+ DRM_DEBUG_DRIVER("unknown info frame type %d\n", frame->type);
+ break;
+ }
+
+ return flags;
+}
+
+static void i9xx_write_infoframe(struct drm_encoder *encoder,
+ struct dip_infoframe *frame)
+{
+ uint32_t *data = (uint32_t *)frame;
struct drm_device *dev = encoder->dev;
struct drm_i915_private *dev_priv = dev->dev_private;
struct intel_hdmi *intel_hdmi = enc_to_intel_hdmi(encoder);
- u32 port;
- unsigned i;
+ u32 port, flags, val = I915_READ(VIDEO_DIP_CTL);
+ unsigned i, len = DIP_HEADER_SIZE + frame->len;
- if (!intel_hdmi->has_hdmi_sink)
- return;
/* XXX first guess at handling video port, is this corrent? */
if (intel_hdmi->sdvox_reg == SDVOB)
@@ -98,18 +133,87 @@
else
return;
- I915_WRITE(VIDEO_DIP_CTL, VIDEO_DIP_ENABLE | port |
- VIDEO_DIP_SELECT_AVI | VIDEO_DIP_FREQ_VSYNC);
+ flags = intel_infoframe_index(frame);
- intel_dip_infoframe_csum(&avi_if);
- for (i = 0; i < sizeof(avi_if); i += 4) {
+ val &= ~VIDEO_DIP_SELECT_MASK;
+
+ I915_WRITE(VIDEO_DIP_CTL, val | port | flags);
+
+ for (i = 0; i < len; i += 4) {
I915_WRITE(VIDEO_DIP_DATA, *data);
data++;
}
- I915_WRITE(VIDEO_DIP_CTL, VIDEO_DIP_ENABLE | port |
- VIDEO_DIP_SELECT_AVI | VIDEO_DIP_FREQ_VSYNC |
- VIDEO_DIP_ENABLE_AVI);
+ flags |= intel_infoframe_flags(frame);
+
+ I915_WRITE(VIDEO_DIP_CTL, VIDEO_DIP_ENABLE | val | port | flags);
+}
+
+static void ironlake_write_infoframe(struct drm_encoder *encoder,
+ struct dip_infoframe *frame)
+{
+ uint32_t *data = (uint32_t *)frame;
+ struct drm_device *dev = encoder->dev;
+ struct drm_i915_private *dev_priv = dev->dev_private;
+ struct drm_crtc *crtc = encoder->crtc;
+ struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
+ int reg = TVIDEO_DIP_CTL(intel_crtc->pipe);
+ unsigned i, len = DIP_HEADER_SIZE + frame->len;
+ u32 flags, val = I915_READ(reg);
+
+ intel_wait_for_vblank(dev, intel_crtc->pipe);
+
+ flags = intel_infoframe_index(frame);
+
+ val &= ~VIDEO_DIP_SELECT_MASK;
+
+ I915_WRITE(reg, val | flags);
+
+ for (i = 0; i < len; i += 4) {
+ I915_WRITE(TVIDEO_DIP_DATA(intel_crtc->pipe), *data);
+ data++;
+ }
+
+ flags |= intel_infoframe_flags(frame);
+
+ I915_WRITE(reg, VIDEO_DIP_ENABLE | val | flags);
+}
+static void intel_set_infoframe(struct drm_encoder *encoder,
+ struct dip_infoframe *frame)
+{
+ struct intel_hdmi *intel_hdmi = enc_to_intel_hdmi(encoder);
+
+ if (!intel_hdmi->has_hdmi_sink)
+ return;
+
+ intel_dip_infoframe_csum(frame);
+ intel_hdmi->write_infoframe(encoder, frame);
+}
+
+static void intel_hdmi_set_avi_infoframe(struct drm_encoder *encoder)
+{
+ struct dip_infoframe avi_if = {
+ .type = DIP_TYPE_AVI,
+ .ver = DIP_VERSION_AVI,
+ .len = DIP_LEN_AVI,
+ };
+
+ intel_set_infoframe(encoder, &avi_if);
+}
+
+static void intel_hdmi_set_spd_infoframe(struct drm_encoder *encoder)
+{
+ struct dip_infoframe spd_if;
+
+ memset(&spd_if, 0, sizeof(spd_if));
+ spd_if.type = DIP_TYPE_SPD;
+ spd_if.ver = DIP_VERSION_SPD;
+ spd_if.len = DIP_LEN_SPD;
+ strcpy(spd_if.body.spd.vn, "Intel");
+ strcpy(spd_if.body.spd.pd, "Integrated gfx");
+ spd_if.body.spd.sdi = DIP_SPD_PC;
+
+ intel_set_infoframe(encoder, &spd_if);
}
static void intel_hdmi_mode_set(struct drm_encoder *encoder,
@@ -156,6 +260,7 @@
POSTING_READ(intel_hdmi->sdvox_reg);
intel_hdmi_set_avi_infoframe(encoder);
+ intel_hdmi_set_spd_infoframe(encoder);
}
static void intel_hdmi_dpms(struct drm_encoder *encoder, int mode)
@@ -433,6 +538,11 @@
intel_hdmi->sdvox_reg = sdvox_reg;
+ if (!HAS_PCH_SPLIT(dev))
+ intel_hdmi->write_infoframe = i9xx_write_infoframe;
+ else
+ intel_hdmi->write_infoframe = ironlake_write_infoframe;
+
drm_encoder_helper_add(&intel_encoder->base, &intel_hdmi_helper_funcs);
intel_hdmi_add_properties(intel_hdmi, connector);
diff --git a/drivers/gpu/drm/i915/intel_lvds.c b/drivers/gpu/drm/i915/intel_lvds.c
index b28f7bd..31da77f 100644
--- a/drivers/gpu/drm/i915/intel_lvds.c
+++ b/drivers/gpu/drm/i915/intel_lvds.c
@@ -72,14 +72,16 @@
{
struct drm_device *dev = intel_lvds->base.base.dev;
struct drm_i915_private *dev_priv = dev->dev_private;
- u32 ctl_reg, lvds_reg;
+ u32 ctl_reg, lvds_reg, stat_reg;
if (HAS_PCH_SPLIT(dev)) {
ctl_reg = PCH_PP_CONTROL;
lvds_reg = PCH_LVDS;
+ stat_reg = PCH_PP_STATUS;
} else {
ctl_reg = PP_CONTROL;
lvds_reg = LVDS;
+ stat_reg = PP_STATUS;
}
I915_WRITE(lvds_reg, I915_READ(lvds_reg) | LVDS_PORT_EN);
@@ -94,17 +96,16 @@
DRM_DEBUG_KMS("applying panel-fitter: %x, %x\n",
intel_lvds->pfit_control,
intel_lvds->pfit_pgm_ratios);
- if (wait_for((I915_READ(PP_STATUS) & PP_ON) == 0, 1000)) {
- DRM_ERROR("timed out waiting for panel to power off\n");
- } else {
- I915_WRITE(PFIT_PGM_RATIOS, intel_lvds->pfit_pgm_ratios);
- I915_WRITE(PFIT_CONTROL, intel_lvds->pfit_control);
- intel_lvds->pfit_dirty = false;
- }
+
+ I915_WRITE(PFIT_PGM_RATIOS, intel_lvds->pfit_pgm_ratios);
+ I915_WRITE(PFIT_CONTROL, intel_lvds->pfit_control);
+ intel_lvds->pfit_dirty = false;
}
I915_WRITE(ctl_reg, I915_READ(ctl_reg) | POWER_TARGET_ON);
POSTING_READ(lvds_reg);
+ if (wait_for((I915_READ(stat_reg) & PP_ON) != 0, 1000))
+ DRM_ERROR("timed out waiting for panel to power on\n");
intel_panel_enable_backlight(dev);
}
@@ -113,24 +114,25 @@
{
struct drm_device *dev = intel_lvds->base.base.dev;
struct drm_i915_private *dev_priv = dev->dev_private;
- u32 ctl_reg, lvds_reg;
+ u32 ctl_reg, lvds_reg, stat_reg;
if (HAS_PCH_SPLIT(dev)) {
ctl_reg = PCH_PP_CONTROL;
lvds_reg = PCH_LVDS;
+ stat_reg = PCH_PP_STATUS;
} else {
ctl_reg = PP_CONTROL;
lvds_reg = LVDS;
+ stat_reg = PP_STATUS;
}
intel_panel_disable_backlight(dev);
I915_WRITE(ctl_reg, I915_READ(ctl_reg) & ~POWER_TARGET_ON);
+ if (wait_for((I915_READ(stat_reg) & PP_ON) == 0, 1000))
+ DRM_ERROR("timed out waiting for panel to power off\n");
if (intel_lvds->pfit_control) {
- if (wait_for((I915_READ(PP_STATUS) & PP_ON) == 0, 1000))
- DRM_ERROR("timed out waiting for panel to power off\n");
-
I915_WRITE(PFIT_CONTROL, 0);
intel_lvds->pfit_dirty = true;
}
@@ -398,53 +400,21 @@
static void intel_lvds_prepare(struct drm_encoder *encoder)
{
- struct drm_device *dev = encoder->dev;
- struct drm_i915_private *dev_priv = dev->dev_private;
struct intel_lvds *intel_lvds = to_intel_lvds(encoder);
- /* We try to do the minimum that is necessary in order to unlock
- * the registers for mode setting.
- *
- * On Ironlake, this is quite simple as we just set the unlock key
- * and ignore all subtleties. (This may cause some issues...)
- *
+ /*
* Prior to Ironlake, we must disable the pipe if we want to adjust
* the panel fitter. However at all other times we can just reset
* the registers regardless.
*/
-
- if (HAS_PCH_SPLIT(dev)) {
- I915_WRITE(PCH_PP_CONTROL,
- I915_READ(PCH_PP_CONTROL) | PANEL_UNLOCK_REGS);
- } else if (intel_lvds->pfit_dirty) {
- I915_WRITE(PP_CONTROL,
- (I915_READ(PP_CONTROL) | PANEL_UNLOCK_REGS)
- & ~POWER_TARGET_ON);
- } else {
- I915_WRITE(PP_CONTROL,
- I915_READ(PP_CONTROL) | PANEL_UNLOCK_REGS);
- }
+ if (!HAS_PCH_SPLIT(encoder->dev) && intel_lvds->pfit_dirty)
+ intel_lvds_disable(intel_lvds);
}
static void intel_lvds_commit(struct drm_encoder *encoder)
{
- struct drm_device *dev = encoder->dev;
- struct drm_i915_private *dev_priv = dev->dev_private;
struct intel_lvds *intel_lvds = to_intel_lvds(encoder);
- /* Undo any unlocking done in prepare to prevent accidental
- * adjustment of the registers.
- */
- if (HAS_PCH_SPLIT(dev)) {
- u32 val = I915_READ(PCH_PP_CONTROL);
- if ((val & PANEL_UNLOCK_REGS) == PANEL_UNLOCK_REGS)
- I915_WRITE(PCH_PP_CONTROL, val & 0x3);
- } else {
- u32 val = I915_READ(PP_CONTROL);
- if ((val & PANEL_UNLOCK_REGS) == PANEL_UNLOCK_REGS)
- I915_WRITE(PP_CONTROL, val & 0x3);
- }
-
/* Always do a full power on as we do not know what state
* we were left in.
*/
@@ -582,6 +552,8 @@
struct drm_device *dev = connector->dev;
struct drm_i915_private *dev_priv = dev->dev_private;
+ intel_panel_destroy_backlight(dev);
+
if (dev_priv->lid_notifier.notifier_call)
acpi_lid_notifier_unregister(&dev_priv->lid_notifier);
drm_sysfs_connector_remove(connector);
@@ -690,6 +662,14 @@
},
{
.callback = intel_no_lvds_dmi_callback,
+ .ident = "Dell OptiPlex FX170",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
+ DMI_MATCH(DMI_PRODUCT_NAME, "OptiPlex FX170"),
+ },
+ },
+ {
+ .callback = intel_no_lvds_dmi_callback,
.ident = "AOpen Mini PC",
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "AOpen"),
@@ -1032,6 +1012,19 @@
pwm = I915_READ(BLC_PWM_PCH_CTL1);
pwm |= PWM_PCH_ENABLE;
I915_WRITE(BLC_PWM_PCH_CTL1, pwm);
+ /*
+ * Unlock registers and just
+ * leave them unlocked
+ */
+ I915_WRITE(PCH_PP_CONTROL,
+ I915_READ(PCH_PP_CONTROL) | PANEL_UNLOCK_REGS);
+ } else {
+ /*
+ * Unlock registers and just
+ * leave them unlocked
+ */
+ I915_WRITE(PP_CONTROL,
+ I915_READ(PP_CONTROL) | PANEL_UNLOCK_REGS);
}
dev_priv->lid_notifier.notifier_call = intel_lid_notify;
if (acpi_lid_notifier_register(&dev_priv->lid_notifier)) {
@@ -1041,6 +1034,9 @@
/* keep the LVDS connector */
dev_priv->int_lvds_connector = connector;
drm_sysfs_connector_add(connector);
+
+ intel_panel_setup_backlight(dev);
+
return true;
failed:
diff --git a/drivers/gpu/drm/i915/intel_opregion.c b/drivers/gpu/drm/i915/intel_opregion.c
index b7c5ddb..b8e8158b 100644
--- a/drivers/gpu/drm/i915/intel_opregion.c
+++ b/drivers/gpu/drm/i915/intel_opregion.c
@@ -227,7 +227,6 @@
asle->aslc = asle_stat;
}
-/* Only present on Ironlake+ */
void intel_opregion_gse_intr(struct drm_device *dev)
{
struct drm_i915_private *dev_priv = dev->dev_private;
diff --git a/drivers/gpu/drm/i915/intel_panel.c b/drivers/gpu/drm/i915/intel_panel.c
index a06ff07..a9e0c7b 100644
--- a/drivers/gpu/drm/i915/intel_panel.c
+++ b/drivers/gpu/drm/i915/intel_panel.c
@@ -83,11 +83,15 @@
u32 scaled_height = mode->hdisplay * adjusted_mode->vdisplay;
if (scaled_width > scaled_height) { /* pillar */
width = scaled_height / mode->vdisplay;
+ if (width & 1)
+ width++;
x = (adjusted_mode->hdisplay - width + 1) / 2;
y = 0;
height = adjusted_mode->vdisplay;
} else if (scaled_width < scaled_height) { /* letter */
height = scaled_width / mode->hdisplay;
+ if (height & 1)
+ height++;
y = (adjusted_mode->vdisplay - height + 1) / 2;
x = 0;
width = adjusted_mode->hdisplay;
@@ -273,7 +277,7 @@
dev_priv->backlight_enabled = true;
}
-void intel_panel_setup_backlight(struct drm_device *dev)
+static void intel_panel_init_backlight(struct drm_device *dev)
{
struct drm_i915_private *dev_priv = dev->dev_private;
@@ -305,3 +309,73 @@
return connector_status_unknown;
}
+
+#ifdef CONFIG_BACKLIGHT_CLASS_DEVICE
+static int intel_panel_update_status(struct backlight_device *bd)
+{
+ struct drm_device *dev = bl_get_data(bd);
+ intel_panel_set_backlight(dev, bd->props.brightness);
+ return 0;
+}
+
+static int intel_panel_get_brightness(struct backlight_device *bd)
+{
+ struct drm_device *dev = bl_get_data(bd);
+ return intel_panel_get_backlight(dev);
+}
+
+static const struct backlight_ops intel_panel_bl_ops = {
+ .update_status = intel_panel_update_status,
+ .get_brightness = intel_panel_get_brightness,
+};
+
+int intel_panel_setup_backlight(struct drm_device *dev)
+{
+ struct drm_i915_private *dev_priv = dev->dev_private;
+ struct backlight_properties props;
+ struct drm_connector *connector;
+
+ intel_panel_init_backlight(dev);
+
+ if (dev_priv->int_lvds_connector)
+ connector = dev_priv->int_lvds_connector;
+ else if (dev_priv->int_edp_connector)
+ connector = dev_priv->int_edp_connector;
+ else
+ return -ENODEV;
+
+ props.type = BACKLIGHT_RAW;
+ props.max_brightness = intel_panel_get_max_backlight(dev);
+ dev_priv->backlight =
+ backlight_device_register("intel_backlight",
+ &connector->kdev, dev,
+ &intel_panel_bl_ops, &props);
+
+ if (IS_ERR(dev_priv->backlight)) {
+ DRM_ERROR("Failed to register backlight: %ld\n",
+ PTR_ERR(dev_priv->backlight));
+ dev_priv->backlight = NULL;
+ return -ENODEV;
+ }
+ dev_priv->backlight->props.brightness = intel_panel_get_backlight(dev);
+ return 0;
+}
+
+void intel_panel_destroy_backlight(struct drm_device *dev)
+{
+ struct drm_i915_private *dev_priv = dev->dev_private;
+ if (dev_priv->backlight)
+ backlight_device_unregister(dev_priv->backlight);
+}
+#else
+int intel_panel_setup_backlight(struct drm_device *dev)
+{
+ intel_panel_init_backlight(dev);
+ return 0;
+}
+
+void intel_panel_destroy_backlight(struct drm_device *dev)
+{
+ return;
+}
+#endif
diff --git a/drivers/gpu/drm/i915/intel_ringbuffer.c b/drivers/gpu/drm/i915/intel_ringbuffer.c
index e961568..47b9b27 100644
--- a/drivers/gpu/drm/i915/intel_ringbuffer.c
+++ b/drivers/gpu/drm/i915/intel_ringbuffer.c
@@ -1321,6 +1321,9 @@
ring->get_seqno = pc_render_get_seqno;
}
+ if (!I915_NEED_GFX_HWS(dev))
+ ring->status_page.page_addr = dev_priv->status_page_dmah->vaddr;
+
ring->dev = dev;
INIT_LIST_HEAD(&ring->active_list);
INIT_LIST_HEAD(&ring->request_list);
diff --git a/drivers/gpu/drm/radeon/Makefile b/drivers/gpu/drm/radeon/Makefile
index 3896ef8..9f363e0 100644
--- a/drivers/gpu/drm/radeon/Makefile
+++ b/drivers/gpu/drm/radeon/Makefile
@@ -5,6 +5,7 @@
ccflags-y := -Iinclude/drm
hostprogs-y := mkregtable
+clean-files := rn50_reg_safe.h r100_reg_safe.h r200_reg_safe.h rv515_reg_safe.h r300_reg_safe.h r420_reg_safe.h rs600_reg_safe.h r600_reg_safe.h evergreen_reg_safe.h cayman_reg_safe.h
quiet_cmd_mkregtable = MKREGTABLE $@
cmd_mkregtable = $(obj)/mkregtable $< > $@
diff --git a/drivers/gpu/drm/radeon/atom.c b/drivers/gpu/drm/radeon/atom.c
index ebdb0fd..e88c644 100644
--- a/drivers/gpu/drm/radeon/atom.c
+++ b/drivers/gpu/drm/radeon/atom.c
@@ -1245,6 +1245,9 @@
char name[512];
int i;
+ if (!ctx)
+ return NULL;
+
ctx->card = card;
ctx->bios = bios;
diff --git a/drivers/gpu/drm/radeon/atombios_dp.c b/drivers/gpu/drm/radeon/atombios_dp.c
index 645b84b..7ad43c6 100644
--- a/drivers/gpu/drm/radeon/atombios_dp.c
+++ b/drivers/gpu/drm/radeon/atombios_dp.c
@@ -613,6 +613,18 @@
return true;
}
+bool radeon_dp_needs_link_train(struct radeon_connector *radeon_connector)
+{
+ u8 link_status[DP_LINK_STATUS_SIZE];
+ struct radeon_connector_atom_dig *dig = radeon_connector->con_priv;
+
+ if (!radeon_dp_get_link_status(radeon_connector, link_status))
+ return false;
+ if (dp_channel_eq_ok(link_status, dig->dp_lane_count))
+ return false;
+ return true;
+}
+
struct radeon_dp_link_train_info {
struct radeon_device *rdev;
struct drm_encoder *encoder;
diff --git a/drivers/gpu/drm/radeon/evergreen.c b/drivers/gpu/drm/radeon/evergreen.c
index 14dce9f..fb5fa08 100644
--- a/drivers/gpu/drm/radeon/evergreen.c
+++ b/drivers/gpu/drm/radeon/evergreen.c
@@ -743,7 +743,7 @@
!evergreen_average_bandwidth_vs_available_bandwidth(&wm) ||
!evergreen_check_latency_hiding(&wm) ||
(rdev->disp_priority == 2)) {
- DRM_INFO("force priority to high\n");
+ DRM_DEBUG_KMS("force priority to high\n");
priority_a_cnt |= PRIORITY_ALWAYS_ON;
priority_b_cnt |= PRIORITY_ALWAYS_ON;
}
diff --git a/drivers/gpu/drm/radeon/evergreen_cs.c b/drivers/gpu/drm/radeon/evergreen_cs.c
index 189e865..a134790 100644
--- a/drivers/gpu/drm/radeon/evergreen_cs.c
+++ b/drivers/gpu/drm/radeon/evergreen_cs.c
@@ -428,7 +428,7 @@
last_reg = ARRAY_SIZE(evergreen_reg_safe_bm);
i = (reg >> 7);
- if (i > last_reg) {
+ if (i >= last_reg) {
dev_warn(p->dev, "forbidden register 0x%08x at %d\n", reg, idx);
return -EINVAL;
}
diff --git a/drivers/gpu/drm/radeon/r600_cs.c b/drivers/gpu/drm/radeon/r600_cs.c
index db8ef19..cf83aa0 100644
--- a/drivers/gpu/drm/radeon/r600_cs.c
+++ b/drivers/gpu/drm/radeon/r600_cs.c
@@ -915,12 +915,11 @@
{
struct r600_cs_track *track = (struct r600_cs_track *)p->track;
struct radeon_cs_reloc *reloc;
- u32 last_reg = ARRAY_SIZE(r600_reg_safe_bm);
u32 m, i, tmp, *ib;
int r;
i = (reg >> 7);
- if (i > last_reg) {
+ if (i >= ARRAY_SIZE(r600_reg_safe_bm)) {
dev_warn(p->dev, "forbidden register 0x%08x at %d\n", reg, idx);
return -EINVAL;
}
diff --git a/drivers/gpu/drm/radeon/radeon_combios.c b/drivers/gpu/drm/radeon/radeon_combios.c
index a74217c..e0138b6 100644
--- a/drivers/gpu/drm/radeon/radeon_combios.c
+++ b/drivers/gpu/drm/radeon/radeon_combios.c
@@ -2557,6 +2557,7 @@
u16 offset, misc, misc2 = 0;
u8 rev, blocks, tmp;
int state_index = 0;
+ struct radeon_i2c_bus_rec i2c_bus;
rdev->pm.default_power_state_index = -1;
@@ -2575,7 +2576,6 @@
offset = combios_get_table_offset(dev, COMBIOS_OVERDRIVE_INFO_TABLE);
if (offset) {
u8 thermal_controller = 0, gpio = 0, i2c_addr = 0, clk_bit = 0, data_bit = 0;
- struct radeon_i2c_bus_rec i2c_bus;
rev = RBIOS8(offset);
@@ -2617,6 +2617,25 @@
i2c_new_device(&rdev->pm.i2c_bus->adapter, &info);
}
}
+ } else {
+ /* boards with a thermal chip, but no overdrive table */
+
+ /* Asus 9600xt has an f75375 on the monid bus */
+ if ((dev->pdev->device == 0x4152) &&
+ (dev->pdev->subsystem_vendor == 0x1043) &&
+ (dev->pdev->subsystem_device == 0xc002)) {
+ i2c_bus = combios_setup_i2c_bus(rdev, DDC_MONID, 0, 0);
+ rdev->pm.i2c_bus = radeon_i2c_lookup(rdev, &i2c_bus);
+ if (rdev->pm.i2c_bus) {
+ struct i2c_board_info info = { };
+ const char *name = "f75375";
+ info.addr = 0x28;
+ strlcpy(info.type, name, sizeof(info.type));
+ i2c_new_device(&rdev->pm.i2c_bus->adapter, &info);
+ DRM_INFO("Possible %s thermal controller at 0x%02x\n",
+ name, info.addr);
+ }
+ }
}
if (rdev->flags & RADEON_IS_MOBILITY) {
diff --git a/drivers/gpu/drm/radeon/radeon_connectors.c b/drivers/gpu/drm/radeon/radeon_connectors.c
index 9792d4f..7f65940 100644
--- a/drivers/gpu/drm/radeon/radeon_connectors.c
+++ b/drivers/gpu/drm/radeon/radeon_connectors.c
@@ -60,18 +60,20 @@
radeon_hpd_set_polarity(rdev, radeon_connector->hpd.hpd);
- /* powering up/down the eDP panel generates hpd events which
- * can interfere with modesetting.
- */
- if (connector->connector_type == DRM_MODE_CONNECTOR_eDP)
+ /* if the connector is already off, don't turn it back on */
+ if (connector->dpms != DRM_MODE_DPMS_ON)
return;
- /* pre-r600 did not always have the hpd pins mapped accurately to connectors */
- if (rdev->family >= CHIP_R600) {
- if (radeon_hpd_sense(rdev, radeon_connector->hpd.hpd))
+ /* just deal with DP (not eDP) here. */
+ if (connector->connector_type == DRM_MODE_CONNECTOR_DisplayPort) {
+ int saved_dpms = connector->dpms;
+
+ if (radeon_hpd_sense(rdev, radeon_connector->hpd.hpd) &&
+ radeon_dp_needs_link_train(radeon_connector))
drm_helper_connector_dpms(connector, DRM_MODE_DPMS_ON);
else
drm_helper_connector_dpms(connector, DRM_MODE_DPMS_OFF);
+ connector->dpms = saved_dpms;
}
}
@@ -430,16 +432,63 @@
return 0;
}
+/*
+ * Some integrated ATI Radeon chipset implementations (e. g.
+ * Asus M2A-VM HDMI) may indicate the availability of a DDC,
+ * even when there's no monitor connected. For these connectors
+ * following DDC probe extension will be applied: check also for the
+ * availability of EDID with at least a correct EDID header. Only then,
+ * DDC is assumed to be available. This prevents drm_get_edid() and
+ * drm_edid_block_valid() from periodically dumping data and kernel
+ * errors into the logs and onto the terminal.
+ */
+static bool radeon_connector_needs_extended_probe(struct radeon_device *dev,
+ uint32_t supported_device,
+ int connector_type)
+{
+ /* Asus M2A-VM HDMI board sends data to i2c bus even,
+ * if HDMI add-on card is not plugged in or HDMI is disabled in
+ * BIOS. Valid DDC can only be assumed, if also a valid EDID header
+ * can be retrieved via i2c bus during DDC probe */
+ if ((dev->pdev->device == 0x791e) &&
+ (dev->pdev->subsystem_vendor == 0x1043) &&
+ (dev->pdev->subsystem_device == 0x826d)) {
+ if ((connector_type == DRM_MODE_CONNECTOR_HDMIA) &&
+ (supported_device == ATOM_DEVICE_DFP2_SUPPORT))
+ return true;
+ }
+ /* ECS A740GM-M with ATI RADEON 2100 sends data to i2c bus
+ * for a DVI connector that is not implemented */
+ if ((dev->pdev->device == 0x796e) &&
+ (dev->pdev->subsystem_vendor == 0x1019) &&
+ (dev->pdev->subsystem_device == 0x2615)) {
+ if ((connector_type == DRM_MODE_CONNECTOR_DVID) &&
+ (supported_device == ATOM_DEVICE_DFP2_SUPPORT))
+ return true;
+ }
+
+ /* Default: no EDID header probe required for DDC probing */
+ return false;
+}
+
static void radeon_fixup_lvds_native_mode(struct drm_encoder *encoder,
struct drm_connector *connector)
{
struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder);
struct drm_display_mode *native_mode = &radeon_encoder->native_mode;
+ struct drm_display_mode *t, *mode;
+
+ /* If the EDID preferred mode doesn't match the native mode, use it */
+ list_for_each_entry_safe(mode, t, &connector->probed_modes, head) {
+ if (mode->type & DRM_MODE_TYPE_PREFERRED) {
+ if (mode->hdisplay != native_mode->hdisplay ||
+ mode->vdisplay != native_mode->vdisplay)
+ memcpy(native_mode, mode, sizeof(*mode));
+ }
+ }
/* Try to get native mode details from EDID if necessary */
if (!native_mode->clock) {
- struct drm_display_mode *t, *mode;
-
list_for_each_entry_safe(mode, t, &connector->probed_modes, head) {
if (mode->hdisplay == native_mode->hdisplay &&
mode->vdisplay == native_mode->vdisplay) {
@@ -450,6 +499,7 @@
}
}
}
+
if (!native_mode->clock) {
DRM_DEBUG_KMS("No LVDS native mode details, disabling RMX\n");
radeon_encoder->rmx_type = RMX_OFF;
@@ -661,7 +711,8 @@
ret = connector_status_disconnected;
if (radeon_connector->ddc_bus)
- dret = radeon_ddc_probe(radeon_connector);
+ dret = radeon_ddc_probe(radeon_connector,
+ radeon_connector->requires_extended_probe);
if (dret) {
if (radeon_connector->edid) {
kfree(radeon_connector->edid);
@@ -833,7 +884,8 @@
bool dret = false;
if (radeon_connector->ddc_bus)
- dret = radeon_ddc_probe(radeon_connector);
+ dret = radeon_ddc_probe(radeon_connector,
+ radeon_connector->requires_extended_probe);
if (dret) {
if (radeon_connector->edid) {
kfree(radeon_connector->edid);
@@ -1251,7 +1303,8 @@
if (radeon_dp_getdpcd(radeon_connector))
ret = connector_status_connected;
} else {
- if (radeon_ddc_probe(radeon_connector))
+ if (radeon_ddc_probe(radeon_connector,
+ radeon_connector->requires_extended_probe))
ret = connector_status_connected;
}
}
@@ -1406,6 +1459,9 @@
radeon_connector->shared_ddc = shared_ddc;
radeon_connector->connector_object_id = connector_object_id;
radeon_connector->hpd = *hpd;
+ radeon_connector->requires_extended_probe =
+ radeon_connector_needs_extended_probe(rdev, supported_device,
+ connector_type);
radeon_connector->router = *router;
if (router->ddc_valid || router->cd_valid) {
radeon_connector->router_bus = radeon_i2c_lookup(rdev, &router->i2c_info);
@@ -1752,6 +1808,9 @@
radeon_connector->devices = supported_device;
radeon_connector->connector_object_id = connector_object_id;
radeon_connector->hpd = *hpd;
+ radeon_connector->requires_extended_probe =
+ radeon_connector_needs_extended_probe(rdev, supported_device,
+ connector_type);
switch (connector_type) {
case DRM_MODE_CONNECTOR_VGA:
drm_connector_init(dev, &radeon_connector->base, &radeon_vga_connector_funcs, connector_type);
diff --git a/drivers/gpu/drm/radeon/radeon_device.c b/drivers/gpu/drm/radeon/radeon_device.c
index 7cfaa7e..a3b011b 100644
--- a/drivers/gpu/drm/radeon/radeon_device.c
+++ b/drivers/gpu/drm/radeon/radeon_device.c
@@ -32,6 +32,7 @@
#include <drm/radeon_drm.h>
#include <linux/vgaarb.h>
#include <linux/vga_switcheroo.h>
+#include <linux/efi.h>
#include "radeon_reg.h"
#include "radeon.h"
#include "atom.h"
@@ -348,6 +349,9 @@
{
uint32_t reg;
+ if (efi_enabled && rdev->pdev->subsystem_vendor == PCI_VENDOR_ID_APPLE)
+ return false;
+
/* first check CRTCs */
if (ASIC_IS_DCE41(rdev)) {
reg = RREG32(EVERGREEN_CRTC_CONTROL + EVERGREEN_CRTC0_REGISTER_OFFSET) |
@@ -704,8 +708,9 @@
rdev->gpu_lockup = false;
rdev->accel_working = false;
- DRM_INFO("initializing kernel modesetting (%s 0x%04X:0x%04X).\n",
- radeon_family_name[rdev->family], pdev->vendor, pdev->device);
+ DRM_INFO("initializing kernel modesetting (%s 0x%04X:0x%04X 0x%04X:0x%04X).\n",
+ radeon_family_name[rdev->family], pdev->vendor, pdev->device,
+ pdev->subsystem_vendor, pdev->subsystem_device);
/* mutex initialization are all done here so we
* can recall function without having locking issues */
diff --git a/drivers/gpu/drm/radeon/radeon_display.c b/drivers/gpu/drm/radeon/radeon_display.c
index 28f4655..1a85894 100644
--- a/drivers/gpu/drm/radeon/radeon_display.c
+++ b/drivers/gpu/drm/radeon/radeon_display.c
@@ -751,8 +751,17 @@
if (!radeon_connector->ddc_bus)
return -1;
edid = drm_get_edid(connector, &radeon_connector->ddc_bus->adapter);
+ /* Log EDID retrieval status here. In particular with regard to
+ * connectors with requires_extended_probe flag set, that will prevent
+ * function radeon_dvi_detect() to fetch EDID on this connector,
+ * as long as there is no valid EDID header found */
if (edid) {
+ DRM_INFO("Radeon display connector %s: Found valid EDID",
+ drm_get_connector_name(connector));
kfree(edid);
+ } else {
+ DRM_INFO("Radeon display connector %s: No monitor connected or invalid EDID",
+ drm_get_connector_name(connector));
}
return ret;
}
diff --git a/drivers/gpu/drm/radeon/radeon_drv.c b/drivers/gpu/drm/radeon/radeon_drv.c
index 85f033f..e71d2ed 100644
--- a/drivers/gpu/drm/radeon/radeon_drv.c
+++ b/drivers/gpu/drm/radeon/radeon_drv.c
@@ -50,8 +50,8 @@
* 2.7.0 - fixups for r600 2D tiling support. (no external ABI change), add eg dyn gpr regs
* 2.8.0 - pageflip support, r500 US_FORMAT regs. r500 ARGB2101010 colorbuf, r300->r500 CMASK, clock crystal query
* 2.9.0 - r600 tiling (s3tc,rgtc) working, SET_PREDICATION packet 3 on r600 + eg, backend query
- * 2.10.0 - fusion 2D tiling, initial compute support for the CS checker
- * 2.11.0 - backend map
+ * 2.10.0 - fusion 2D tiling
+ * 2.11.0 - backend map, initial compute support for the CS checker
*/
#define KMS_DRIVER_MAJOR 2
#define KMS_DRIVER_MINOR 11
diff --git a/drivers/gpu/drm/radeon/radeon_encoders.c b/drivers/gpu/drm/radeon/radeon_encoders.c
index b293487..319d85d 100644
--- a/drivers/gpu/drm/radeon/radeon_encoders.c
+++ b/drivers/gpu/drm/radeon/radeon_encoders.c
@@ -2323,6 +2323,9 @@
default:
encoder->possible_crtcs = 0x3;
break;
+ case 4:
+ encoder->possible_crtcs = 0xf;
+ break;
case 6:
encoder->possible_crtcs = 0x3f;
break;
diff --git a/drivers/gpu/drm/radeon/radeon_i2c.c b/drivers/gpu/drm/radeon/radeon_i2c.c
index 781196d..6c111c1 100644
--- a/drivers/gpu/drm/radeon/radeon_i2c.c
+++ b/drivers/gpu/drm/radeon/radeon_i2c.c
@@ -32,17 +32,17 @@
* radeon_ddc_probe
*
*/
-bool radeon_ddc_probe(struct radeon_connector *radeon_connector)
+bool radeon_ddc_probe(struct radeon_connector *radeon_connector, bool requires_extended_probe)
{
- u8 out_buf[] = { 0x0, 0x0};
- u8 buf[2];
+ u8 out = 0x0;
+ u8 buf[8];
int ret;
struct i2c_msg msgs[] = {
{
.addr = 0x50,
.flags = 0,
.len = 1,
- .buf = out_buf,
+ .buf = &out,
},
{
.addr = 0x50,
@@ -52,15 +52,31 @@
}
};
+ /* Read 8 bytes from i2c for extended probe of EDID header */
+ if (requires_extended_probe)
+ msgs[1].len = 8;
+
/* on hw with routers, select right port */
if (radeon_connector->router.ddc_valid)
radeon_router_select_ddc_port(radeon_connector);
ret = i2c_transfer(&radeon_connector->ddc_bus->adapter, msgs, 2);
- if (ret == 2)
- return true;
-
- return false;
+ if (ret != 2)
+ /* Couldn't find an accessible DDC on this connector */
+ return false;
+ if (requires_extended_probe) {
+ /* Probe also for valid EDID header
+ * EDID header starts with:
+ * 0x00,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x00.
+ * Only the first 6 bytes must be valid as
+ * drm_edid_block_valid() can fix the last 2 bytes */
+ if (drm_edid_header_is_valid(buf) < 6) {
+ /* Couldn't find an accessible EDID on this
+ * connector */
+ return false;
+ }
+ }
+ return true;
}
/* bit banging i2c */
diff --git a/drivers/gpu/drm/radeon/radeon_mode.h b/drivers/gpu/drm/radeon/radeon_mode.h
index 6df4e3c..68820f5 100644
--- a/drivers/gpu/drm/radeon/radeon_mode.h
+++ b/drivers/gpu/drm/radeon/radeon_mode.h
@@ -438,6 +438,9 @@
struct radeon_i2c_chan *ddc_bus;
/* some systems have an hdmi and vga port with a shared ddc line */
bool shared_ddc;
+ /* for some Radeon chip families we apply an additional EDID header
+ check as part of the DDC probe */
+ bool requires_extended_probe;
bool use_digital;
/* we need to mind the EDID between detect
and get modes due to analog/digital/tvencoder */
@@ -476,6 +479,7 @@
struct drm_display_mode *mode);
extern void radeon_dp_link_train(struct drm_encoder *encoder,
struct drm_connector *connector);
+extern bool radeon_dp_needs_link_train(struct radeon_connector *radeon_connector);
extern u8 radeon_dp_getsinktype(struct radeon_connector *radeon_connector);
extern bool radeon_dp_getdpcd(struct radeon_connector *radeon_connector);
extern void atombios_dig_encoder_setup(struct drm_encoder *encoder, int action, int panel_mode);
@@ -514,7 +518,8 @@
u8 val);
extern void radeon_router_select_ddc_port(struct radeon_connector *radeon_connector);
extern void radeon_router_select_cd_port(struct radeon_connector *radeon_connector);
-extern bool radeon_ddc_probe(struct radeon_connector *radeon_connector);
+extern bool radeon_ddc_probe(struct radeon_connector *radeon_connector,
+ bool requires_extended_probe);
extern int radeon_ddc_get_modes(struct radeon_connector *radeon_connector);
extern struct drm_encoder *radeon_best_encoder(struct drm_connector *connector);
diff --git a/drivers/hwmon/ibmaem.c b/drivers/hwmon/ibmaem.c
index 1a409c5..c316294 100644
--- a/drivers/hwmon/ibmaem.c
+++ b/drivers/hwmon/ibmaem.c
@@ -432,13 +432,15 @@
aem_send_message(ipmi);
res = wait_for_completion_timeout(&ipmi->read_complete, IPMI_TIMEOUT);
- if (!res)
- return -ETIMEDOUT;
+ if (!res) {
+ res = -ETIMEDOUT;
+ goto out;
+ }
if (ipmi->rx_result || ipmi->rx_msg_len != rs_size ||
memcmp(&rs_resp->id, &system_x_id, sizeof(system_x_id))) {
- kfree(rs_resp);
- return -ENOENT;
+ res = -ENOENT;
+ goto out;
}
switch (size) {
@@ -463,8 +465,11 @@
break;
}
}
+ res = 0;
- return 0;
+out:
+ kfree(rs_resp);
+ return res;
}
/* Update AEM energy registers */
diff --git a/drivers/hwmon/pmbus/lm25066.c b/drivers/hwmon/pmbus/lm25066.c
index d4bc114..ac254fb 100644
--- a/drivers/hwmon/pmbus/lm25066.c
+++ b/drivers/hwmon/pmbus/lm25066.c
@@ -161,6 +161,17 @@
return ret;
}
+static int lm25066_write_byte(struct i2c_client *client, int page, u8 value)
+{
+ if (page > 1)
+ return -EINVAL;
+
+ if (page == 0)
+ return pmbus_write_byte(client, 0, value);
+
+ return 0;
+}
+
static int lm25066_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
@@ -204,6 +215,7 @@
info->read_word_data = lm25066_read_word_data;
info->write_word_data = lm25066_write_word_data;
+ info->write_byte = lm25066_write_byte;
switch (id->driver_data) {
case lm25066:
diff --git a/drivers/hwmon/pmbus/pmbus.h b/drivers/hwmon/pmbus/pmbus.h
index 0808d98..a6ae20f 100644
--- a/drivers/hwmon/pmbus/pmbus.h
+++ b/drivers/hwmon/pmbus/pmbus.h
@@ -325,6 +325,7 @@
int (*read_word_data)(struct i2c_client *client, int page, int reg);
int (*write_word_data)(struct i2c_client *client, int page, int reg,
u16 word);
+ int (*write_byte)(struct i2c_client *client, int page, u8 value);
/*
* The identify function determines supported PMBus functionality.
* This function is only necessary if a chip driver supports multiple
diff --git a/drivers/hwmon/pmbus/pmbus_core.c b/drivers/hwmon/pmbus/pmbus_core.c
index 5c1b6cf..a561c3a 100644
--- a/drivers/hwmon/pmbus/pmbus_core.c
+++ b/drivers/hwmon/pmbus/pmbus_core.c
@@ -182,6 +182,24 @@
}
EXPORT_SYMBOL_GPL(pmbus_write_byte);
+/*
+ * _pmbus_write_byte() is similar to pmbus_write_byte(), but checks if
+ * a device specific mapping funcion exists and calls it if necessary.
+ */
+static int _pmbus_write_byte(struct i2c_client *client, int page, u8 value)
+{
+ struct pmbus_data *data = i2c_get_clientdata(client);
+ const struct pmbus_driver_info *info = data->info;
+ int status;
+
+ if (info->write_byte) {
+ status = info->write_byte(client, page, value);
+ if (status != -ENODATA)
+ return status;
+ }
+ return pmbus_write_byte(client, page, value);
+}
+
int pmbus_write_word_data(struct i2c_client *client, u8 page, u8 reg, u16 word)
{
int rv;
@@ -281,7 +299,7 @@
static void pmbus_clear_fault_page(struct i2c_client *client, int page)
{
- pmbus_write_byte(client, page, PMBUS_CLEAR_FAULTS);
+ _pmbus_write_byte(client, page, PMBUS_CLEAR_FAULTS);
}
void pmbus_clear_faults(struct i2c_client *client)
diff --git a/drivers/ide/cy82c693.c b/drivers/ide/cy82c693.c
index 3be60da..67cbcfa 100644
--- a/drivers/ide/cy82c693.c
+++ b/drivers/ide/cy82c693.c
@@ -141,6 +141,8 @@
pci_write_config_byte(dev, CY82_IDE_SLAVE_IOW, time_16);
pci_write_config_byte(dev, CY82_IDE_SLAVE_8BIT, time_8);
}
+ if (hwif->index > 0)
+ pci_dev_put(dev);
}
static void __devinit init_iops_cy82c693(ide_hwif_t *hwif)
diff --git a/drivers/ide/ide_platform.c b/drivers/ide/ide_platform.c
index 542603b..962693b 100644
--- a/drivers/ide/ide_platform.c
+++ b/drivers/ide/ide_platform.c
@@ -19,6 +19,7 @@
#include <linux/module.h>
#include <linux/ata_platform.h>
#include <linux/platform_device.h>
+#include <linux/interrupt.h>
#include <linux/io.h>
static void __devinit plat_ide_setup_ports(struct ide_hw *hw,
@@ -95,7 +96,10 @@
plat_ide_setup_ports(&hw, base, alt_base, pdata, res_irq->start);
hw.dev = &pdev->dev;
- d.irq_flags = res_irq->flags;
+ d.irq_flags = res_irq->flags & IRQF_TRIGGER_MASK;
+ if (res_irq->flags & IORESOURCE_IRQ_SHAREABLE)
+ d.irq_flags |= IRQF_SHARED;
+
if (mmio)
d.host_flags |= IDE_HFLAG_MMIO;
diff --git a/drivers/infiniband/ulp/ipoib/ipoib_main.c b/drivers/infiniband/ulp/ipoib/ipoib_main.c
index 43f89ba..fe89c46 100644
--- a/drivers/infiniband/ulp/ipoib/ipoib_main.c
+++ b/drivers/infiniband/ulp/ipoib/ipoib_main.c
@@ -717,11 +717,13 @@
{
struct ipoib_dev_priv *priv = netdev_priv(dev);
struct ipoib_neigh *neigh;
- struct neighbour *n;
+ struct neighbour *n = NULL;
unsigned long flags;
- n = dst_get_neighbour(skb_dst(skb));
- if (likely(skb_dst(skb) && n)) {
+ if (likely(skb_dst(skb)))
+ n = dst_get_neighbour(skb_dst(skb));
+
+ if (likely(n)) {
if (unlikely(!*to_ipoib_neigh(n))) {
ipoib_path_lookup(skb, dev);
return NETDEV_TX_OK;
diff --git a/drivers/infiniband/ulp/iser/iscsi_iser.c b/drivers/infiniband/ulp/iser/iscsi_iser.c
index 8db008d..9c61b9c 100644
--- a/drivers/infiniband/ulp/iser/iscsi_iser.c
+++ b/drivers/infiniband/ulp/iser/iscsi_iser.c
@@ -101,13 +101,17 @@
/* verify PDU length */
datalen = ntoh24(hdr->dlength);
- if (datalen != rx_data_len) {
- printk(KERN_ERR "iscsi_iser: datalen %d (hdr) != %d (IB) \n",
- datalen, rx_data_len);
+ if (datalen > rx_data_len || (datalen + 4) < rx_data_len) {
+ iser_err("wrong datalen %d (hdr), %d (IB)\n",
+ datalen, rx_data_len);
rc = ISCSI_ERR_DATALEN;
goto error;
}
+ if (datalen != rx_data_len)
+ iser_dbg("aligned datalen (%d) hdr, %d (IB)\n",
+ datalen, rx_data_len);
+
/* read AHS */
ahslen = hdr->hlength * 4;
diff --git a/drivers/infiniband/ulp/iser/iscsi_iser.h b/drivers/infiniband/ulp/iser/iscsi_iser.h
index 342cbc1..db6f3ce 100644
--- a/drivers/infiniband/ulp/iser/iscsi_iser.h
+++ b/drivers/infiniband/ulp/iser/iscsi_iser.h
@@ -89,7 +89,7 @@
} while (0)
#define SHIFT_4K 12
-#define SIZE_4K (1UL << SHIFT_4K)
+#define SIZE_4K (1ULL << SHIFT_4K)
#define MASK_4K (~(SIZE_4K-1))
/* support up to 512KB in one RDMA */
diff --git a/drivers/infiniband/ulp/iser/iser_initiator.c b/drivers/infiniband/ulp/iser/iser_initiator.c
index 5745b7f..f299de6 100644
--- a/drivers/infiniband/ulp/iser/iser_initiator.c
+++ b/drivers/infiniband/ulp/iser/iser_initiator.c
@@ -412,7 +412,7 @@
memcpy(iser_conn->ib_conn->login_buf, task->data,
task->data_count);
tx_dsg->addr = iser_conn->ib_conn->login_dma;
- tx_dsg->length = data_seg_len;
+ tx_dsg->length = task->data_count;
tx_dsg->lkey = device->mr->lkey;
mdesc->num_sge = 2;
}
diff --git a/drivers/input/keyboard/gpio_keys.c b/drivers/input/keyboard/gpio_keys.c
index ce281d1..67df91a 100644
--- a/drivers/input/keyboard/gpio_keys.c
+++ b/drivers/input/keyboard/gpio_keys.c
@@ -483,7 +483,7 @@
buttons = kzalloc(pdata->nbuttons * (sizeof *buttons), GFP_KERNEL);
if (!buttons)
- return -ENODEV;
+ return -ENOMEM;
pp = NULL;
i = 0;
diff --git a/drivers/input/keyboard/lm8323.c b/drivers/input/keyboard/lm8323.c
index ab0acaf..756348a 100644
--- a/drivers/input/keyboard/lm8323.c
+++ b/drivers/input/keyboard/lm8323.c
@@ -754,8 +754,11 @@
device_remove_file(&client->dev, &dev_attr_disable_kp);
fail2:
while (--pwm >= 0)
- if (lm->pwm[pwm].enabled)
+ if (lm->pwm[pwm].enabled) {
+ device_remove_file(lm->pwm[pwm].cdev.dev,
+ &dev_attr_time);
led_classdev_unregister(&lm->pwm[pwm].cdev);
+ }
fail1:
input_free_device(idev);
kfree(lm);
@@ -775,8 +778,10 @@
device_remove_file(&lm->client->dev, &dev_attr_disable_kp);
for (i = 0; i < 3; i++)
- if (lm->pwm[i].enabled)
+ if (lm->pwm[i].enabled) {
+ device_remove_file(lm->pwm[i].cdev.dev, &dev_attr_time);
led_classdev_unregister(&lm->pwm[i].cdev);
+ }
kfree(lm);
diff --git a/drivers/input/keyboard/tegra-kbc.c b/drivers/input/keyboard/tegra-kbc.c
index da3828f..f270447 100644
--- a/drivers/input/keyboard/tegra-kbc.c
+++ b/drivers/input/keyboard/tegra-kbc.c
@@ -19,6 +19,7 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
+#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/input.h>
#include <linux/platform_device.h>
@@ -37,7 +38,7 @@
#define KBC_ROW_SCAN_DLY 5
/* KBC uses a 32KHz clock so a cycle = 1/32Khz */
-#define KBC_CYCLE_USEC 32
+#define KBC_CYCLE_MS 32
/* KBC Registers */
@@ -647,7 +648,7 @@
debounce_cnt = min(pdata->debounce_cnt, KBC_MAX_DEBOUNCE_CNT);
scan_time_rows = (KBC_ROW_SCAN_TIME + debounce_cnt) * num_rows;
kbc->repoll_dly = KBC_ROW_SCAN_DLY + scan_time_rows + pdata->repeat_cnt;
- kbc->repoll_dly = ((kbc->repoll_dly * KBC_CYCLE_USEC) + 999) / 1000;
+ kbc->repoll_dly = DIV_ROUND_UP(kbc->repoll_dly, KBC_CYCLE_MS);
input_dev->name = pdev->name;
input_dev->id.bustype = BUS_HOST;
diff --git a/drivers/input/misc/kxtj9.c b/drivers/input/misc/kxtj9.c
index c456f63..783597a 100644
--- a/drivers/input/misc/kxtj9.c
+++ b/drivers/input/misc/kxtj9.c
@@ -21,6 +21,7 @@
#include <linux/i2c.h>
#include <linux/input.h>
#include <linux/interrupt.h>
+#include <linux/module.h>
#include <linux/slab.h>
#include <linux/input/kxtj9.h>
#include <linux/input-polldev.h>
diff --git a/drivers/input/misc/mma8450.c b/drivers/input/misc/mma8450.c
index 20f8f92..6c76cf7 100644
--- a/drivers/input/misc/mma8450.c
+++ b/drivers/input/misc/mma8450.c
@@ -24,6 +24,7 @@
#include <linux/delay.h>
#include <linux/i2c.h>
#include <linux/input-polldev.h>
+#include <linux/of_device.h>
#define MMA8450_DRV_NAME "mma8450"
@@ -229,10 +230,17 @@
};
MODULE_DEVICE_TABLE(i2c, mma8450_id);
+static const struct of_device_id mma8450_dt_ids[] = {
+ { .compatible = "fsl,mma8450", },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(i2c, mma8450_dt_ids);
+
static struct i2c_driver mma8450_driver = {
.driver = {
.name = MMA8450_DRV_NAME,
.owner = THIS_MODULE,
+ .of_match_table = mma8450_dt_ids,
},
.probe = mma8450_probe,
.remove = __devexit_p(mma8450_remove),
diff --git a/drivers/input/mouse/hgpk.c b/drivers/input/mouse/hgpk.c
index 95577c1..4d17d9f 100644
--- a/drivers/input/mouse/hgpk.c
+++ b/drivers/input/mouse/hgpk.c
@@ -32,6 +32,7 @@
#define DEBUG
#include <linux/slab.h>
#include <linux/input.h>
+#include <linux/module.h>
#include <linux/serio.h>
#include <linux/libps2.h>
#include <linux/delay.h>
diff --git a/drivers/input/serio/xilinx_ps2.c b/drivers/input/serio/xilinx_ps2.c
index 80baa53..d64c5a4 100644
--- a/drivers/input/serio/xilinx_ps2.c
+++ b/drivers/input/serio/xilinx_ps2.c
@@ -23,7 +23,7 @@
#include <linux/init.h>
#include <linux/list.h>
#include <linux/io.h>
-
+#include <linux/of_address.h>
#include <linux/of_device.h>
#include <linux/of_platform.h>
diff --git a/drivers/input/touchscreen/ad7879.c b/drivers/input/touchscreen/ad7879.c
index bc3b518..131f9d1 100644
--- a/drivers/input/touchscreen/ad7879.c
+++ b/drivers/input/touchscreen/ad7879.c
@@ -249,12 +249,14 @@
static void __ad7879_disable(struct ad7879 *ts)
{
+ u16 reg = (ts->cmd_crtl2 & ~AD7879_PM(-1)) |
+ AD7879_PM(AD7879_PM_SHUTDOWN);
disable_irq(ts->irq);
if (del_timer_sync(&ts->timer))
ad7879_ts_event_release(ts);
- ad7879_write(ts, AD7879_REG_CTRL2, AD7879_PM(AD7879_PM_SHUTDOWN));
+ ad7879_write(ts, AD7879_REG_CTRL2, reg);
}
diff --git a/drivers/md/Kconfig b/drivers/md/Kconfig
index 8420129..f75a66e 100644
--- a/drivers/md/Kconfig
+++ b/drivers/md/Kconfig
@@ -241,12 +241,13 @@
needed for live data migration tools such as 'pvmove'.
config DM_RAID
- tristate "RAID 4/5/6 target (EXPERIMENTAL)"
+ tristate "RAID 1/4/5/6 target (EXPERIMENTAL)"
depends on BLK_DEV_DM && EXPERIMENTAL
+ select MD_RAID1
select MD_RAID456
select BLK_DEV_MD
---help---
- A dm target that supports RAID4, RAID5 and RAID6 mappings
+ A dm target that supports RAID1, RAID4, RAID5 and RAID6 mappings
A RAID-5 set of N drives with a capacity of C MB per drive provides
the capacity of C * (N - 1) MB, and protects against a failure
diff --git a/drivers/md/dm-crypt.c b/drivers/md/dm-crypt.c
index bae6c4e..49da55c 100644
--- a/drivers/md/dm-crypt.c
+++ b/drivers/md/dm-crypt.c
@@ -30,7 +30,6 @@
#include <linux/device-mapper.h>
#define DM_MSG_PREFIX "crypt"
-#define MESG_STR(x) x, sizeof(x)
/*
* context holding the current state of a multi-part conversion
@@ -239,7 +238,7 @@
struct dm_crypt_request *dmreq)
{
memset(iv, 0, cc->iv_size);
- *(u32 *)iv = cpu_to_le32(dmreq->iv_sector & 0xffffffff);
+ *(__le32 *)iv = cpu_to_le32(dmreq->iv_sector & 0xffffffff);
return 0;
}
@@ -248,7 +247,7 @@
struct dm_crypt_request *dmreq)
{
memset(iv, 0, cc->iv_size);
- *(u64 *)iv = cpu_to_le64(dmreq->iv_sector);
+ *(__le64 *)iv = cpu_to_le64(dmreq->iv_sector);
return 0;
}
@@ -415,7 +414,7 @@
struct crypto_cipher *essiv_tfm = this_crypt_config(cc)->iv_private;
memset(iv, 0, cc->iv_size);
- *(u64 *)iv = cpu_to_le64(dmreq->iv_sector);
+ *(__le64 *)iv = cpu_to_le64(dmreq->iv_sector);
crypto_cipher_encrypt_one(essiv_tfm, iv, iv);
return 0;
@@ -1575,11 +1574,17 @@
static int crypt_ctr(struct dm_target *ti, unsigned int argc, char **argv)
{
struct crypt_config *cc;
- unsigned int key_size;
+ unsigned int key_size, opt_params;
unsigned long long tmpll;
int ret;
+ struct dm_arg_set as;
+ const char *opt_string;
- if (argc != 5) {
+ static struct dm_arg _args[] = {
+ {0, 1, "Invalid number of feature args"},
+ };
+
+ if (argc < 5) {
ti->error = "Not enough arguments";
return -EINVAL;
}
@@ -1648,6 +1653,30 @@
}
cc->start = tmpll;
+ argv += 5;
+ argc -= 5;
+
+ /* Optional parameters */
+ if (argc) {
+ as.argc = argc;
+ as.argv = argv;
+
+ ret = dm_read_arg_group(_args, &as, &opt_params, &ti->error);
+ if (ret)
+ goto bad;
+
+ opt_string = dm_shift_arg(&as);
+
+ if (opt_params == 1 && opt_string &&
+ !strcasecmp(opt_string, "allow_discards"))
+ ti->num_discard_requests = 1;
+ else if (opt_params) {
+ ret = -EINVAL;
+ ti->error = "Invalid feature arguments";
+ goto bad;
+ }
+ }
+
ret = -ENOMEM;
cc->io_queue = alloc_workqueue("kcryptd_io",
WQ_NON_REENTRANT|
@@ -1682,9 +1711,16 @@
struct dm_crypt_io *io;
struct crypt_config *cc;
- if (bio->bi_rw & REQ_FLUSH) {
+ /*
+ * If bio is REQ_FLUSH or REQ_DISCARD, just bypass crypt queues.
+ * - for REQ_FLUSH device-mapper core ensures that no IO is in-flight
+ * - for REQ_DISCARD caller must use flush if IO ordering matters
+ */
+ if (unlikely(bio->bi_rw & (REQ_FLUSH | REQ_DISCARD))) {
cc = ti->private;
bio->bi_bdev = cc->dev->bdev;
+ if (bio_sectors(bio))
+ bio->bi_sector = cc->start + dm_target_offset(ti, bio->bi_sector);
return DM_MAPIO_REMAPPED;
}
@@ -1727,6 +1763,10 @@
DMEMIT(" %llu %s %llu", (unsigned long long)cc->iv_offset,
cc->dev->name, (unsigned long long)cc->start);
+
+ if (ti->num_discard_requests)
+ DMEMIT(" 1 allow_discards");
+
break;
}
return 0;
@@ -1770,12 +1810,12 @@
if (argc < 2)
goto error;
- if (!strnicmp(argv[0], MESG_STR("key"))) {
+ if (!strcasecmp(argv[0], "key")) {
if (!test_bit(DM_CRYPT_SUSPENDED, &cc->flags)) {
DMWARN("not suspended during key manipulation.");
return -EINVAL;
}
- if (argc == 3 && !strnicmp(argv[1], MESG_STR("set"))) {
+ if (argc == 3 && !strcasecmp(argv[1], "set")) {
ret = crypt_set_key(cc, argv[2]);
if (ret)
return ret;
@@ -1783,7 +1823,7 @@
ret = cc->iv_gen_ops->init(cc);
return ret;
}
- if (argc == 2 && !strnicmp(argv[1], MESG_STR("wipe"))) {
+ if (argc == 2 && !strcasecmp(argv[1], "wipe")) {
if (cc->iv_gen_ops && cc->iv_gen_ops->wipe) {
ret = cc->iv_gen_ops->wipe(cc);
if (ret)
@@ -1823,7 +1863,7 @@
static struct target_type crypt_target = {
.name = "crypt",
- .version = {1, 10, 0},
+ .version = {1, 11, 0},
.module = THIS_MODULE,
.ctr = crypt_ctr,
.dtr = crypt_dtr,
diff --git a/drivers/md/dm-flakey.c b/drivers/md/dm-flakey.c
index ea79062..89f73ca 100644
--- a/drivers/md/dm-flakey.c
+++ b/drivers/md/dm-flakey.c
@@ -1,6 +1,6 @@
/*
* Copyright (C) 2003 Sistina Software (UK) Limited.
- * Copyright (C) 2004, 2010 Red Hat, Inc. All rights reserved.
+ * Copyright (C) 2004, 2010-2011 Red Hat, Inc. All rights reserved.
*
* This file is released under the GPL.
*/
@@ -15,6 +15,9 @@
#define DM_MSG_PREFIX "flakey"
+#define all_corrupt_bio_flags_match(bio, fc) \
+ (((bio)->bi_rw & (fc)->corrupt_bio_flags) == (fc)->corrupt_bio_flags)
+
/*
* Flakey: Used for testing only, simulates intermittent,
* catastrophic device failure.
@@ -25,60 +28,189 @@
sector_t start;
unsigned up_interval;
unsigned down_interval;
+ unsigned long flags;
+ unsigned corrupt_bio_byte;
+ unsigned corrupt_bio_rw;
+ unsigned corrupt_bio_value;
+ unsigned corrupt_bio_flags;
};
-/*
- * Construct a flakey mapping: <dev_path> <offset> <up interval> <down interval>
- */
-static int flakey_ctr(struct dm_target *ti, unsigned int argc, char **argv)
-{
- struct flakey_c *fc;
- unsigned long long tmp;
+enum feature_flag_bits {
+ DROP_WRITES
+};
- if (argc != 4) {
- ti->error = "dm-flakey: Invalid argument count";
+static int parse_features(struct dm_arg_set *as, struct flakey_c *fc,
+ struct dm_target *ti)
+{
+ int r;
+ unsigned argc;
+ const char *arg_name;
+
+ static struct dm_arg _args[] = {
+ {0, 6, "Invalid number of feature args"},
+ {1, UINT_MAX, "Invalid corrupt bio byte"},
+ {0, 255, "Invalid corrupt value to write into bio byte (0-255)"},
+ {0, UINT_MAX, "Invalid corrupt bio flags mask"},
+ };
+
+ /* No feature arguments supplied. */
+ if (!as->argc)
+ return 0;
+
+ r = dm_read_arg_group(_args, as, &argc, &ti->error);
+ if (r)
+ return r;
+
+ while (argc) {
+ arg_name = dm_shift_arg(as);
+ argc--;
+
+ /*
+ * drop_writes
+ */
+ if (!strcasecmp(arg_name, "drop_writes")) {
+ if (test_and_set_bit(DROP_WRITES, &fc->flags)) {
+ ti->error = "Feature drop_writes duplicated";
+ return -EINVAL;
+ }
+
+ continue;
+ }
+
+ /*
+ * corrupt_bio_byte <Nth_byte> <direction> <value> <bio_flags>
+ */
+ if (!strcasecmp(arg_name, "corrupt_bio_byte")) {
+ if (!argc)
+ ti->error = "Feature corrupt_bio_byte requires parameters";
+
+ r = dm_read_arg(_args + 1, as, &fc->corrupt_bio_byte, &ti->error);
+ if (r)
+ return r;
+ argc--;
+
+ /*
+ * Direction r or w?
+ */
+ arg_name = dm_shift_arg(as);
+ if (!strcasecmp(arg_name, "w"))
+ fc->corrupt_bio_rw = WRITE;
+ else if (!strcasecmp(arg_name, "r"))
+ fc->corrupt_bio_rw = READ;
+ else {
+ ti->error = "Invalid corrupt bio direction (r or w)";
+ return -EINVAL;
+ }
+ argc--;
+
+ /*
+ * Value of byte (0-255) to write in place of correct one.
+ */
+ r = dm_read_arg(_args + 2, as, &fc->corrupt_bio_value, &ti->error);
+ if (r)
+ return r;
+ argc--;
+
+ /*
+ * Only corrupt bios with these flags set.
+ */
+ r = dm_read_arg(_args + 3, as, &fc->corrupt_bio_flags, &ti->error);
+ if (r)
+ return r;
+ argc--;
+
+ continue;
+ }
+
+ ti->error = "Unrecognised flakey feature requested";
return -EINVAL;
}
- fc = kmalloc(sizeof(*fc), GFP_KERNEL);
+ if (test_bit(DROP_WRITES, &fc->flags) && (fc->corrupt_bio_rw == WRITE)) {
+ ti->error = "drop_writes is incompatible with corrupt_bio_byte with the WRITE flag set";
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+/*
+ * Construct a flakey mapping:
+ * <dev_path> <offset> <up interval> <down interval> [<#feature args> [<arg>]*]
+ *
+ * Feature args:
+ * [drop_writes]
+ * [corrupt_bio_byte <Nth_byte> <direction> <value> <bio_flags>]
+ *
+ * Nth_byte starts from 1 for the first byte.
+ * Direction is r for READ or w for WRITE.
+ * bio_flags is ignored if 0.
+ */
+static int flakey_ctr(struct dm_target *ti, unsigned int argc, char **argv)
+{
+ static struct dm_arg _args[] = {
+ {0, UINT_MAX, "Invalid up interval"},
+ {0, UINT_MAX, "Invalid down interval"},
+ };
+
+ int r;
+ struct flakey_c *fc;
+ unsigned long long tmpll;
+ struct dm_arg_set as;
+ const char *devname;
+
+ as.argc = argc;
+ as.argv = argv;
+
+ if (argc < 4) {
+ ti->error = "Invalid argument count";
+ return -EINVAL;
+ }
+
+ fc = kzalloc(sizeof(*fc), GFP_KERNEL);
if (!fc) {
- ti->error = "dm-flakey: Cannot allocate linear context";
+ ti->error = "Cannot allocate linear context";
return -ENOMEM;
}
fc->start_time = jiffies;
- if (sscanf(argv[1], "%llu", &tmp) != 1) {
- ti->error = "dm-flakey: Invalid device sector";
- goto bad;
- }
- fc->start = tmp;
+ devname = dm_shift_arg(&as);
- if (sscanf(argv[2], "%u", &fc->up_interval) != 1) {
- ti->error = "dm-flakey: Invalid up interval";
+ if (sscanf(dm_shift_arg(&as), "%llu", &tmpll) != 1) {
+ ti->error = "Invalid device sector";
goto bad;
}
+ fc->start = tmpll;
- if (sscanf(argv[3], "%u", &fc->down_interval) != 1) {
- ti->error = "dm-flakey: Invalid down interval";
+ r = dm_read_arg(_args, &as, &fc->up_interval, &ti->error);
+ if (r)
goto bad;
- }
+
+ r = dm_read_arg(_args, &as, &fc->down_interval, &ti->error);
+ if (r)
+ goto bad;
if (!(fc->up_interval + fc->down_interval)) {
- ti->error = "dm-flakey: Total (up + down) interval is zero";
+ ti->error = "Total (up + down) interval is zero";
goto bad;
}
if (fc->up_interval + fc->down_interval < fc->up_interval) {
- ti->error = "dm-flakey: Interval overflow";
+ ti->error = "Interval overflow";
goto bad;
}
- if (dm_get_device(ti, argv[0], dm_table_get_mode(ti->table), &fc->dev)) {
- ti->error = "dm-flakey: Device lookup failed";
+ r = parse_features(&as, fc, ti);
+ if (r)
+ goto bad;
+
+ if (dm_get_device(ti, devname, dm_table_get_mode(ti->table), &fc->dev)) {
+ ti->error = "Device lookup failed";
goto bad;
}
ti->num_flush_requests = 1;
+ ti->num_discard_requests = 1;
ti->private = fc;
return 0;
@@ -99,7 +231,7 @@
{
struct flakey_c *fc = ti->private;
- return fc->start + (bi_sector - ti->begin);
+ return fc->start + dm_target_offset(ti, bi_sector);
}
static void flakey_map_bio(struct dm_target *ti, struct bio *bio)
@@ -111,6 +243,25 @@
bio->bi_sector = flakey_map_sector(ti, bio->bi_sector);
}
+static void corrupt_bio_data(struct bio *bio, struct flakey_c *fc)
+{
+ unsigned bio_bytes = bio_cur_bytes(bio);
+ char *data = bio_data(bio);
+
+ /*
+ * Overwrite the Nth byte of the data returned.
+ */
+ if (data && bio_bytes >= fc->corrupt_bio_byte) {
+ data[fc->corrupt_bio_byte - 1] = fc->corrupt_bio_value;
+
+ DMDEBUG("Corrupting data bio=%p by writing %u to byte %u "
+ "(rw=%c bi_rw=%lu bi_sector=%llu cur_bytes=%u)\n",
+ bio, fc->corrupt_bio_value, fc->corrupt_bio_byte,
+ (bio_data_dir(bio) == WRITE) ? 'w' : 'r',
+ bio->bi_rw, (unsigned long long)bio->bi_sector, bio_bytes);
+ }
+}
+
static int flakey_map(struct dm_target *ti, struct bio *bio,
union map_info *map_context)
{
@@ -119,18 +270,71 @@
/* Are we alive ? */
elapsed = (jiffies - fc->start_time) / HZ;
- if (elapsed % (fc->up_interval + fc->down_interval) >= fc->up_interval)
- return -EIO;
+ if (elapsed % (fc->up_interval + fc->down_interval) >= fc->up_interval) {
+ /*
+ * Flag this bio as submitted while down.
+ */
+ map_context->ll = 1;
+ /*
+ * Map reads as normal.
+ */
+ if (bio_data_dir(bio) == READ)
+ goto map_bio;
+
+ /*
+ * Drop writes?
+ */
+ if (test_bit(DROP_WRITES, &fc->flags)) {
+ bio_endio(bio, 0);
+ return DM_MAPIO_SUBMITTED;
+ }
+
+ /*
+ * Corrupt matching writes.
+ */
+ if (fc->corrupt_bio_byte && (fc->corrupt_bio_rw == WRITE)) {
+ if (all_corrupt_bio_flags_match(bio, fc))
+ corrupt_bio_data(bio, fc);
+ goto map_bio;
+ }
+
+ /*
+ * By default, error all I/O.
+ */
+ return -EIO;
+ }
+
+map_bio:
flakey_map_bio(ti, bio);
return DM_MAPIO_REMAPPED;
}
+static int flakey_end_io(struct dm_target *ti, struct bio *bio,
+ int error, union map_info *map_context)
+{
+ struct flakey_c *fc = ti->private;
+ unsigned bio_submitted_while_down = map_context->ll;
+
+ /*
+ * Corrupt successful READs while in down state.
+ * If flags were specified, only corrupt those that match.
+ */
+ if (!error && bio_submitted_while_down &&
+ (bio_data_dir(bio) == READ) && (fc->corrupt_bio_rw == READ) &&
+ all_corrupt_bio_flags_match(bio, fc))
+ corrupt_bio_data(bio, fc);
+
+ return error;
+}
+
static int flakey_status(struct dm_target *ti, status_type_t type,
char *result, unsigned int maxlen)
{
+ unsigned sz = 0;
struct flakey_c *fc = ti->private;
+ unsigned drop_writes;
switch (type) {
case STATUSTYPE_INFO:
@@ -138,9 +342,22 @@
break;
case STATUSTYPE_TABLE:
- snprintf(result, maxlen, "%s %llu %u %u", fc->dev->name,
- (unsigned long long)fc->start, fc->up_interval,
- fc->down_interval);
+ DMEMIT("%s %llu %u %u ", fc->dev->name,
+ (unsigned long long)fc->start, fc->up_interval,
+ fc->down_interval);
+
+ drop_writes = test_bit(DROP_WRITES, &fc->flags);
+ DMEMIT("%u ", drop_writes + (fc->corrupt_bio_byte > 0) * 5);
+
+ if (drop_writes)
+ DMEMIT("drop_writes ");
+
+ if (fc->corrupt_bio_byte)
+ DMEMIT("corrupt_bio_byte %u %c %u %u ",
+ fc->corrupt_bio_byte,
+ (fc->corrupt_bio_rw == WRITE) ? 'w' : 'r',
+ fc->corrupt_bio_value, fc->corrupt_bio_flags);
+
break;
}
return 0;
@@ -177,11 +394,12 @@
static struct target_type flakey_target = {
.name = "flakey",
- .version = {1, 1, 0},
+ .version = {1, 2, 0},
.module = THIS_MODULE,
.ctr = flakey_ctr,
.dtr = flakey_dtr,
.map = flakey_map,
+ .end_io = flakey_end_io,
.status = flakey_status,
.ioctl = flakey_ioctl,
.merge = flakey_merge,
diff --git a/drivers/md/dm-io.c b/drivers/md/dm-io.c
index 2067288..ad2eba4 100644
--- a/drivers/md/dm-io.c
+++ b/drivers/md/dm-io.c
@@ -38,6 +38,8 @@
struct dm_io_client *client;
io_notify_fn callback;
void *context;
+ void *vma_invalidate_address;
+ unsigned long vma_invalidate_size;
} __attribute__((aligned(DM_IO_MAX_REGIONS)));
static struct kmem_cache *_dm_io_cache;
@@ -116,6 +118,10 @@
set_bit(region, &io->error_bits);
if (atomic_dec_and_test(&io->count)) {
+ if (io->vma_invalidate_size)
+ invalidate_kernel_vmap_range(io->vma_invalidate_address,
+ io->vma_invalidate_size);
+
if (io->sleeper)
wake_up_process(io->sleeper);
@@ -159,6 +165,9 @@
unsigned context_u;
void *context_ptr;
+
+ void *vma_invalidate_address;
+ unsigned long vma_invalidate_size;
};
/*
@@ -377,6 +386,9 @@
io->sleeper = current;
io->client = client;
+ io->vma_invalidate_address = dp->vma_invalidate_address;
+ io->vma_invalidate_size = dp->vma_invalidate_size;
+
dispatch_io(rw, num_regions, where, dp, io, 1);
while (1) {
@@ -415,13 +427,21 @@
io->callback = fn;
io->context = context;
+ io->vma_invalidate_address = dp->vma_invalidate_address;
+ io->vma_invalidate_size = dp->vma_invalidate_size;
+
dispatch_io(rw, num_regions, where, dp, io, 0);
return 0;
}
-static int dp_init(struct dm_io_request *io_req, struct dpages *dp)
+static int dp_init(struct dm_io_request *io_req, struct dpages *dp,
+ unsigned long size)
{
/* Set up dpages based on memory type */
+
+ dp->vma_invalidate_address = NULL;
+ dp->vma_invalidate_size = 0;
+
switch (io_req->mem.type) {
case DM_IO_PAGE_LIST:
list_dp_init(dp, io_req->mem.ptr.pl, io_req->mem.offset);
@@ -432,6 +452,11 @@
break;
case DM_IO_VMA:
+ flush_kernel_vmap_range(io_req->mem.ptr.vma, size);
+ if ((io_req->bi_rw & RW_MASK) == READ) {
+ dp->vma_invalidate_address = io_req->mem.ptr.vma;
+ dp->vma_invalidate_size = size;
+ }
vm_dp_init(dp, io_req->mem.ptr.vma);
break;
@@ -460,7 +485,7 @@
int r;
struct dpages dp;
- r = dp_init(io_req, &dp);
+ r = dp_init(io_req, &dp, (unsigned long)where->count << SECTOR_SHIFT);
if (r)
return r;
diff --git a/drivers/md/dm-ioctl.c b/drivers/md/dm-ioctl.c
index 4cacdad..2e9a3ca 100644
--- a/drivers/md/dm-ioctl.c
+++ b/drivers/md/dm-ioctl.c
@@ -128,6 +128,24 @@
return NULL;
}
+static struct hash_cell *__get_dev_cell(uint64_t dev)
+{
+ struct mapped_device *md;
+ struct hash_cell *hc;
+
+ md = dm_get_md(huge_decode_dev(dev));
+ if (!md)
+ return NULL;
+
+ hc = dm_get_mdptr(md);
+ if (!hc) {
+ dm_put(md);
+ return NULL;
+ }
+
+ return hc;
+}
+
/*-----------------------------------------------------------------
* Inserting, removing and renaming a device.
*---------------------------------------------------------------*/
@@ -718,25 +736,45 @@
*/
static struct hash_cell *__find_device_hash_cell(struct dm_ioctl *param)
{
- struct mapped_device *md;
- void *mdptr = NULL;
+ struct hash_cell *hc = NULL;
- if (*param->uuid)
- return __get_uuid_cell(param->uuid);
+ if (*param->uuid) {
+ if (*param->name || param->dev)
+ return NULL;
- if (*param->name)
- return __get_name_cell(param->name);
+ hc = __get_uuid_cell(param->uuid);
+ if (!hc)
+ return NULL;
+ } else if (*param->name) {
+ if (param->dev)
+ return NULL;
- md = dm_get_md(huge_decode_dev(param->dev));
- if (!md)
- goto out;
+ hc = __get_name_cell(param->name);
+ if (!hc)
+ return NULL;
+ } else if (param->dev) {
+ hc = __get_dev_cell(param->dev);
+ if (!hc)
+ return NULL;
+ } else
+ return NULL;
- mdptr = dm_get_mdptr(md);
- if (!mdptr)
- dm_put(md);
+ /*
+ * Sneakily write in both the name and the uuid
+ * while we have the cell.
+ */
+ strlcpy(param->name, hc->name, sizeof(param->name));
+ if (hc->uuid)
+ strlcpy(param->uuid, hc->uuid, sizeof(param->uuid));
+ else
+ param->uuid[0] = '\0';
-out:
- return mdptr;
+ if (hc->new_map)
+ param->flags |= DM_INACTIVE_PRESENT_FLAG;
+ else
+ param->flags &= ~DM_INACTIVE_PRESENT_FLAG;
+
+ return hc;
}
static struct mapped_device *find_device(struct dm_ioctl *param)
@@ -746,24 +784,8 @@
down_read(&_hash_lock);
hc = __find_device_hash_cell(param);
- if (hc) {
+ if (hc)
md = hc->md;
-
- /*
- * Sneakily write in both the name and the uuid
- * while we have the cell.
- */
- strlcpy(param->name, hc->name, sizeof(param->name));
- if (hc->uuid)
- strlcpy(param->uuid, hc->uuid, sizeof(param->uuid));
- else
- param->uuid[0] = '\0';
-
- if (hc->new_map)
- param->flags |= DM_INACTIVE_PRESENT_FLAG;
- else
- param->flags &= ~DM_INACTIVE_PRESENT_FLAG;
- }
up_read(&_hash_lock);
return md;
@@ -1402,6 +1424,11 @@
goto out;
}
+ if (!argc) {
+ DMWARN("Empty message received.");
+ goto out;
+ }
+
table = dm_get_live_table(md);
if (!table)
goto out_argv;
diff --git a/drivers/md/dm-kcopyd.c b/drivers/md/dm-kcopyd.c
index 320401d..f821470 100644
--- a/drivers/md/dm-kcopyd.c
+++ b/drivers/md/dm-kcopyd.c
@@ -224,8 +224,6 @@
unsigned int num_dests;
struct dm_io_region dests[DM_KCOPYD_MAX_REGIONS];
- sector_t offset;
- unsigned int nr_pages;
struct page_list *pages;
/*
@@ -380,7 +378,7 @@
.bi_rw = job->rw,
.mem.type = DM_IO_PAGE_LIST,
.mem.ptr.pl = job->pages,
- .mem.offset = job->offset,
+ .mem.offset = 0,
.notify.fn = complete_io,
.notify.context = job,
.client = job->kc->io_client,
@@ -397,10 +395,9 @@
static int run_pages_job(struct kcopyd_job *job)
{
int r;
+ unsigned nr_pages = dm_div_up(job->dests[0].count, PAGE_SIZE >> 9);
- job->nr_pages = dm_div_up(job->dests[0].count + job->offset,
- PAGE_SIZE >> 9);
- r = kcopyd_get_pages(job->kc, job->nr_pages, &job->pages);
+ r = kcopyd_get_pages(job->kc, nr_pages, &job->pages);
if (!r) {
/* this job is ready for io */
push(&job->kc->io_jobs, job);
@@ -602,8 +599,6 @@
job->num_dests = num_dests;
memcpy(&job->dests, dests, sizeof(*dests) * num_dests);
- job->offset = 0;
- job->nr_pages = 0;
job->pages = NULL;
job->fn = fn;
@@ -622,6 +617,37 @@
}
EXPORT_SYMBOL(dm_kcopyd_copy);
+void *dm_kcopyd_prepare_callback(struct dm_kcopyd_client *kc,
+ dm_kcopyd_notify_fn fn, void *context)
+{
+ struct kcopyd_job *job;
+
+ job = mempool_alloc(kc->job_pool, GFP_NOIO);
+
+ memset(job, 0, sizeof(struct kcopyd_job));
+ job->kc = kc;
+ job->fn = fn;
+ job->context = context;
+
+ atomic_inc(&kc->nr_jobs);
+
+ return job;
+}
+EXPORT_SYMBOL(dm_kcopyd_prepare_callback);
+
+void dm_kcopyd_do_callback(void *j, int read_err, unsigned long write_err)
+{
+ struct kcopyd_job *job = j;
+ struct dm_kcopyd_client *kc = job->kc;
+
+ job->read_err = read_err;
+ job->write_err = write_err;
+
+ push(&kc->complete_jobs, job);
+ wake(kc);
+}
+EXPORT_SYMBOL(dm_kcopyd_do_callback);
+
/*
* Cancels a kcopyd job, eg. someone might be deactivating a
* mirror.
diff --git a/drivers/md/dm-log-userspace-base.c b/drivers/md/dm-log-userspace-base.c
index aa2e0c3..1021c89 100644
--- a/drivers/md/dm-log-userspace-base.c
+++ b/drivers/md/dm-log-userspace-base.c
@@ -394,8 +394,7 @@
group[count] = fe->region;
count++;
- list_del(&fe->list);
- list_add(&fe->list, &tmp_list);
+ list_move(&fe->list, &tmp_list);
type = fe->type;
if (count >= MAX_FLUSH_GROUP_COUNT)
diff --git a/drivers/md/dm-log.c b/drivers/md/dm-log.c
index 948e3f4..3b52bb7 100644
--- a/drivers/md/dm-log.c
+++ b/drivers/md/dm-log.c
@@ -197,15 +197,21 @@
#define MIRROR_DISK_VERSION 2
#define LOG_OFFSET 2
-struct log_header {
- uint32_t magic;
+struct log_header_disk {
+ __le32 magic;
/*
* Simple, incrementing version. no backward
* compatibility.
*/
+ __le32 version;
+ __le64 nr_regions;
+} __packed;
+
+struct log_header_core {
+ uint32_t magic;
uint32_t version;
- sector_t nr_regions;
+ uint64_t nr_regions;
};
struct log_c {
@@ -239,10 +245,10 @@
int log_dev_failed;
int log_dev_flush_failed;
struct dm_dev *log_dev;
- struct log_header header;
+ struct log_header_core header;
struct dm_io_region header_location;
- struct log_header *disk_header;
+ struct log_header_disk *disk_header;
};
/*
@@ -251,34 +257,34 @@
*/
static inline int log_test_bit(uint32_t *bs, unsigned bit)
{
- return test_bit_le(bit, (unsigned long *) bs) ? 1 : 0;
+ return test_bit_le(bit, bs) ? 1 : 0;
}
static inline void log_set_bit(struct log_c *l,
uint32_t *bs, unsigned bit)
{
- __test_and_set_bit_le(bit, (unsigned long *) bs);
+ __set_bit_le(bit, bs);
l->touched_cleaned = 1;
}
static inline void log_clear_bit(struct log_c *l,
uint32_t *bs, unsigned bit)
{
- __test_and_clear_bit_le(bit, (unsigned long *) bs);
+ __clear_bit_le(bit, bs);
l->touched_dirtied = 1;
}
/*----------------------------------------------------------------
* Header IO
*--------------------------------------------------------------*/
-static void header_to_disk(struct log_header *core, struct log_header *disk)
+static void header_to_disk(struct log_header_core *core, struct log_header_disk *disk)
{
disk->magic = cpu_to_le32(core->magic);
disk->version = cpu_to_le32(core->version);
disk->nr_regions = cpu_to_le64(core->nr_regions);
}
-static void header_from_disk(struct log_header *core, struct log_header *disk)
+static void header_from_disk(struct log_header_core *core, struct log_header_disk *disk)
{
core->magic = le32_to_cpu(disk->magic);
core->version = le32_to_cpu(disk->version);
@@ -486,7 +492,7 @@
memset(lc->sync_bits, (sync == NOSYNC) ? -1 : 0, bitset_size);
lc->sync_count = (sync == NOSYNC) ? region_count : 0;
- lc->recovering_bits = vmalloc(bitset_size);
+ lc->recovering_bits = vzalloc(bitset_size);
if (!lc->recovering_bits) {
DMWARN("couldn't allocate sync bitset");
vfree(lc->sync_bits);
@@ -498,7 +504,6 @@
kfree(lc);
return -ENOMEM;
}
- memset(lc->recovering_bits, 0, bitset_size);
lc->sync_search = 0;
log->context = lc;
@@ -739,8 +744,7 @@
return 0;
do {
- *region = find_next_zero_bit_le(
- (unsigned long *) lc->sync_bits,
+ *region = find_next_zero_bit_le(lc->sync_bits,
lc->region_count,
lc->sync_search);
lc->sync_search = *region + 1;
diff --git a/drivers/md/dm-mpath.c b/drivers/md/dm-mpath.c
index c354701..5e0090e 100644
--- a/drivers/md/dm-mpath.c
+++ b/drivers/md/dm-mpath.c
@@ -22,7 +22,6 @@
#include <linux/atomic.h>
#define DM_MSG_PREFIX "multipath"
-#define MESG_STR(x) x, sizeof(x)
#define DM_PG_INIT_DELAY_MSECS 2000
#define DM_PG_INIT_DELAY_DEFAULT ((unsigned) -1)
@@ -505,80 +504,29 @@
* <#paths> <#per-path selector args>
* [<path> [<arg>]* ]+ ]+
*---------------------------------------------------------------*/
-struct param {
- unsigned min;
- unsigned max;
- char *error;
-};
-
-static int read_param(struct param *param, char *str, unsigned *v, char **error)
-{
- if (!str ||
- (sscanf(str, "%u", v) != 1) ||
- (*v < param->min) ||
- (*v > param->max)) {
- *error = param->error;
- return -EINVAL;
- }
-
- return 0;
-}
-
-struct arg_set {
- unsigned argc;
- char **argv;
-};
-
-static char *shift(struct arg_set *as)
-{
- char *r;
-
- if (as->argc) {
- as->argc--;
- r = *as->argv;
- as->argv++;
- return r;
- }
-
- return NULL;
-}
-
-static void consume(struct arg_set *as, unsigned n)
-{
- BUG_ON (as->argc < n);
- as->argc -= n;
- as->argv += n;
-}
-
-static int parse_path_selector(struct arg_set *as, struct priority_group *pg,
+static int parse_path_selector(struct dm_arg_set *as, struct priority_group *pg,
struct dm_target *ti)
{
int r;
struct path_selector_type *pst;
unsigned ps_argc;
- static struct param _params[] = {
+ static struct dm_arg _args[] = {
{0, 1024, "invalid number of path selector args"},
};
- pst = dm_get_path_selector(shift(as));
+ pst = dm_get_path_selector(dm_shift_arg(as));
if (!pst) {
ti->error = "unknown path selector type";
return -EINVAL;
}
- r = read_param(_params, shift(as), &ps_argc, &ti->error);
+ r = dm_read_arg_group(_args, as, &ps_argc, &ti->error);
if (r) {
dm_put_path_selector(pst);
return -EINVAL;
}
- if (ps_argc > as->argc) {
- dm_put_path_selector(pst);
- ti->error = "not enough arguments for path selector";
- return -EINVAL;
- }
-
r = pst->create(&pg->ps, ps_argc, as->argv);
if (r) {
dm_put_path_selector(pst);
@@ -587,12 +535,12 @@
}
pg->ps.type = pst;
- consume(as, ps_argc);
+ dm_consume_args(as, ps_argc);
return 0;
}
-static struct pgpath *parse_path(struct arg_set *as, struct path_selector *ps,
+static struct pgpath *parse_path(struct dm_arg_set *as, struct path_selector *ps,
struct dm_target *ti)
{
int r;
@@ -609,7 +557,7 @@
if (!p)
return ERR_PTR(-ENOMEM);
- r = dm_get_device(ti, shift(as), dm_table_get_mode(ti->table),
+ r = dm_get_device(ti, dm_shift_arg(as), dm_table_get_mode(ti->table),
&p->path.dev);
if (r) {
ti->error = "error getting device";
@@ -660,16 +608,16 @@
return ERR_PTR(r);
}
-static struct priority_group *parse_priority_group(struct arg_set *as,
+static struct priority_group *parse_priority_group(struct dm_arg_set *as,
struct multipath *m)
{
- static struct param _params[] = {
+ static struct dm_arg _args[] = {
{1, 1024, "invalid number of paths"},
{0, 1024, "invalid number of selector args"}
};
int r;
- unsigned i, nr_selector_args, nr_params;
+ unsigned i, nr_selector_args, nr_args;
struct priority_group *pg;
struct dm_target *ti = m->ti;
@@ -693,26 +641,26 @@
/*
* read the paths
*/
- r = read_param(_params, shift(as), &pg->nr_pgpaths, &ti->error);
+ r = dm_read_arg(_args, as, &pg->nr_pgpaths, &ti->error);
if (r)
goto bad;
- r = read_param(_params + 1, shift(as), &nr_selector_args, &ti->error);
+ r = dm_read_arg(_args + 1, as, &nr_selector_args, &ti->error);
if (r)
goto bad;
- nr_params = 1 + nr_selector_args;
+ nr_args = 1 + nr_selector_args;
for (i = 0; i < pg->nr_pgpaths; i++) {
struct pgpath *pgpath;
- struct arg_set path_args;
+ struct dm_arg_set path_args;
- if (as->argc < nr_params) {
+ if (as->argc < nr_args) {
ti->error = "not enough path parameters";
r = -EINVAL;
goto bad;
}
- path_args.argc = nr_params;
+ path_args.argc = nr_args;
path_args.argv = as->argv;
pgpath = parse_path(&path_args, &pg->ps, ti);
@@ -723,7 +671,7 @@
pgpath->pg = pg;
list_add_tail(&pgpath->list, &pg->pgpaths);
- consume(as, nr_params);
+ dm_consume_args(as, nr_args);
}
return pg;
@@ -733,28 +681,23 @@
return ERR_PTR(r);
}
-static int parse_hw_handler(struct arg_set *as, struct multipath *m)
+static int parse_hw_handler(struct dm_arg_set *as, struct multipath *m)
{
unsigned hw_argc;
int ret;
struct dm_target *ti = m->ti;
- static struct param _params[] = {
+ static struct dm_arg _args[] = {
{0, 1024, "invalid number of hardware handler args"},
};
- if (read_param(_params, shift(as), &hw_argc, &ti->error))
+ if (dm_read_arg_group(_args, as, &hw_argc, &ti->error))
return -EINVAL;
if (!hw_argc)
return 0;
- if (hw_argc > as->argc) {
- ti->error = "not enough arguments for hardware handler";
- return -EINVAL;
- }
-
- m->hw_handler_name = kstrdup(shift(as), GFP_KERNEL);
+ m->hw_handler_name = kstrdup(dm_shift_arg(as), GFP_KERNEL);
request_module("scsi_dh_%s", m->hw_handler_name);
if (scsi_dh_handler_exist(m->hw_handler_name) == 0) {
ti->error = "unknown hardware handler type";
@@ -778,7 +721,7 @@
for (i = 0, p+=j+1; i <= hw_argc - 2; i++, p+=j+1)
j = sprintf(p, "%s", as->argv[i]);
}
- consume(as, hw_argc - 1);
+ dm_consume_args(as, hw_argc - 1);
return 0;
fail:
@@ -787,20 +730,20 @@
return ret;
}
-static int parse_features(struct arg_set *as, struct multipath *m)
+static int parse_features(struct dm_arg_set *as, struct multipath *m)
{
int r;
unsigned argc;
struct dm_target *ti = m->ti;
- const char *param_name;
+ const char *arg_name;
- static struct param _params[] = {
+ static struct dm_arg _args[] = {
{0, 5, "invalid number of feature args"},
{1, 50, "pg_init_retries must be between 1 and 50"},
{0, 60000, "pg_init_delay_msecs must be between 0 and 60000"},
};
- r = read_param(_params, shift(as), &argc, &ti->error);
+ r = dm_read_arg_group(_args, as, &argc, &ti->error);
if (r)
return -EINVAL;
@@ -808,26 +751,24 @@
return 0;
do {
- param_name = shift(as);
+ arg_name = dm_shift_arg(as);
argc--;
- if (!strnicmp(param_name, MESG_STR("queue_if_no_path"))) {
+ if (!strcasecmp(arg_name, "queue_if_no_path")) {
r = queue_if_no_path(m, 1, 0);
continue;
}
- if (!strnicmp(param_name, MESG_STR("pg_init_retries")) &&
+ if (!strcasecmp(arg_name, "pg_init_retries") &&
(argc >= 1)) {
- r = read_param(_params + 1, shift(as),
- &m->pg_init_retries, &ti->error);
+ r = dm_read_arg(_args + 1, as, &m->pg_init_retries, &ti->error);
argc--;
continue;
}
- if (!strnicmp(param_name, MESG_STR("pg_init_delay_msecs")) &&
+ if (!strcasecmp(arg_name, "pg_init_delay_msecs") &&
(argc >= 1)) {
- r = read_param(_params + 2, shift(as),
- &m->pg_init_delay_msecs, &ti->error);
+ r = dm_read_arg(_args + 2, as, &m->pg_init_delay_msecs, &ti->error);
argc--;
continue;
}
@@ -842,15 +783,15 @@
static int multipath_ctr(struct dm_target *ti, unsigned int argc,
char **argv)
{
- /* target parameters */
- static struct param _params[] = {
+ /* target arguments */
+ static struct dm_arg _args[] = {
{0, 1024, "invalid number of priority groups"},
{0, 1024, "invalid initial priority group number"},
};
int r;
struct multipath *m;
- struct arg_set as;
+ struct dm_arg_set as;
unsigned pg_count = 0;
unsigned next_pg_num;
@@ -871,11 +812,11 @@
if (r)
goto bad;
- r = read_param(_params, shift(&as), &m->nr_priority_groups, &ti->error);
+ r = dm_read_arg(_args, &as, &m->nr_priority_groups, &ti->error);
if (r)
goto bad;
- r = read_param(_params + 1, shift(&as), &next_pg_num, &ti->error);
+ r = dm_read_arg(_args + 1, &as, &next_pg_num, &ti->error);
if (r)
goto bad;
@@ -1505,10 +1446,10 @@
}
if (argc == 1) {
- if (!strnicmp(argv[0], MESG_STR("queue_if_no_path"))) {
+ if (!strcasecmp(argv[0], "queue_if_no_path")) {
r = queue_if_no_path(m, 1, 0);
goto out;
- } else if (!strnicmp(argv[0], MESG_STR("fail_if_no_path"))) {
+ } else if (!strcasecmp(argv[0], "fail_if_no_path")) {
r = queue_if_no_path(m, 0, 0);
goto out;
}
@@ -1519,18 +1460,18 @@
goto out;
}
- if (!strnicmp(argv[0], MESG_STR("disable_group"))) {
+ if (!strcasecmp(argv[0], "disable_group")) {
r = bypass_pg_num(m, argv[1], 1);
goto out;
- } else if (!strnicmp(argv[0], MESG_STR("enable_group"))) {
+ } else if (!strcasecmp(argv[0], "enable_group")) {
r = bypass_pg_num(m, argv[1], 0);
goto out;
- } else if (!strnicmp(argv[0], MESG_STR("switch_group"))) {
+ } else if (!strcasecmp(argv[0], "switch_group")) {
r = switch_pg_num(m, argv[1]);
goto out;
- } else if (!strnicmp(argv[0], MESG_STR("reinstate_path")))
+ } else if (!strcasecmp(argv[0], "reinstate_path"))
action = reinstate_path;
- else if (!strnicmp(argv[0], MESG_STR("fail_path")))
+ else if (!strcasecmp(argv[0], "fail_path"))
action = fail_path;
else {
DMWARN("Unrecognised multipath message received.");
diff --git a/drivers/md/dm-raid.c b/drivers/md/dm-raid.c
index e5d8904..a002dd8 100644
--- a/drivers/md/dm-raid.c
+++ b/drivers/md/dm-raid.c
@@ -8,19 +8,19 @@
#include <linux/slab.h>
#include "md.h"
+#include "raid1.h"
#include "raid5.h"
-#include "dm.h"
#include "bitmap.h"
+#include <linux/device-mapper.h>
+
#define DM_MSG_PREFIX "raid"
/*
- * If the MD doesn't support MD_SYNC_STATE_FORCED yet, then
- * make it so the flag doesn't set anything.
+ * The following flags are used by dm-raid.c to set up the array state.
+ * They must be cleared before md_run is called.
*/
-#ifndef MD_SYNC_STATE_FORCED
-#define MD_SYNC_STATE_FORCED 0
-#endif
+#define FirstUse 10 /* rdev flag */
struct raid_dev {
/*
@@ -43,14 +43,15 @@
/*
* Flags for rs->print_flags field.
*/
-#define DMPF_DAEMON_SLEEP 0x1
-#define DMPF_MAX_WRITE_BEHIND 0x2
-#define DMPF_SYNC 0x4
-#define DMPF_NOSYNC 0x8
-#define DMPF_STRIPE_CACHE 0x10
-#define DMPF_MIN_RECOVERY_RATE 0x20
-#define DMPF_MAX_RECOVERY_RATE 0x40
-
+#define DMPF_SYNC 0x1
+#define DMPF_NOSYNC 0x2
+#define DMPF_REBUILD 0x4
+#define DMPF_DAEMON_SLEEP 0x8
+#define DMPF_MIN_RECOVERY_RATE 0x10
+#define DMPF_MAX_RECOVERY_RATE 0x20
+#define DMPF_MAX_WRITE_BEHIND 0x40
+#define DMPF_STRIPE_CACHE 0x80
+#define DMPF_REGION_SIZE 0X100
struct raid_set {
struct dm_target *ti;
@@ -72,6 +73,7 @@
const unsigned level; /* RAID level. */
const unsigned algorithm; /* RAID algorithm. */
} raid_types[] = {
+ {"raid1", "RAID1 (mirroring)", 0, 2, 1, 0 /* NONE */},
{"raid4", "RAID4 (dedicated parity disk)", 1, 2, 5, ALGORITHM_PARITY_0},
{"raid5_la", "RAID5 (left asymmetric)", 1, 2, 5, ALGORITHM_LEFT_ASYMMETRIC},
{"raid5_ra", "RAID5 (right asymmetric)", 1, 2, 5, ALGORITHM_RIGHT_ASYMMETRIC},
@@ -105,7 +107,8 @@
}
sectors_per_dev = ti->len;
- if (sector_div(sectors_per_dev, (raid_devs - raid_type->parity_devs))) {
+ if ((raid_type->level > 1) &&
+ sector_div(sectors_per_dev, (raid_devs - raid_type->parity_devs))) {
ti->error = "Target length not divisible by number of data devices";
return ERR_PTR(-EINVAL);
}
@@ -147,9 +150,16 @@
{
int i;
- for (i = 0; i < rs->md.raid_disks; i++)
+ for (i = 0; i < rs->md.raid_disks; i++) {
+ if (rs->dev[i].meta_dev)
+ dm_put_device(rs->ti, rs->dev[i].meta_dev);
+ if (rs->dev[i].rdev.sb_page)
+ put_page(rs->dev[i].rdev.sb_page);
+ rs->dev[i].rdev.sb_page = NULL;
+ rs->dev[i].rdev.sb_loaded = 0;
if (rs->dev[i].data_dev)
dm_put_device(rs->ti, rs->dev[i].data_dev);
+ }
kfree(rs);
}
@@ -159,7 +169,16 @@
* <meta_dev>: meta device name or '-' if missing
* <data_dev>: data device name or '-' if missing
*
- * This code parses those words.
+ * The following are permitted:
+ * - -
+ * - <data_dev>
+ * <meta_dev> <data_dev>
+ *
+ * The following is not allowed:
+ * <meta_dev> -
+ *
+ * This code parses those words. If there is a failure,
+ * the caller must use context_free to unwind the operations.
*/
static int dev_parms(struct raid_set *rs, char **argv)
{
@@ -182,8 +201,16 @@
rs->dev[i].rdev.mddev = &rs->md;
if (strcmp(argv[0], "-")) {
- rs->ti->error = "Metadata devices not supported";
- return -EINVAL;
+ ret = dm_get_device(rs->ti, argv[0],
+ dm_table_get_mode(rs->ti->table),
+ &rs->dev[i].meta_dev);
+ rs->ti->error = "RAID metadata device lookup failure";
+ if (ret)
+ return ret;
+
+ rs->dev[i].rdev.sb_page = alloc_page(GFP_KERNEL);
+ if (!rs->dev[i].rdev.sb_page)
+ return -ENOMEM;
}
if (!strcmp(argv[1], "-")) {
@@ -193,6 +220,10 @@
return -EINVAL;
}
+ rs->ti->error = "No data device supplied with metadata device";
+ if (rs->dev[i].meta_dev)
+ return -EINVAL;
+
continue;
}
@@ -204,6 +235,10 @@
return ret;
}
+ if (rs->dev[i].meta_dev) {
+ metadata_available = 1;
+ rs->dev[i].rdev.meta_bdev = rs->dev[i].meta_dev->bdev;
+ }
rs->dev[i].rdev.bdev = rs->dev[i].data_dev->bdev;
list_add(&rs->dev[i].rdev.same_set, &rs->md.disks);
if (!test_bit(In_sync, &rs->dev[i].rdev.flags))
@@ -235,33 +270,109 @@
}
/*
+ * validate_region_size
+ * @rs
+ * @region_size: region size in sectors. If 0, pick a size (4MiB default).
+ *
+ * Set rs->md.bitmap_info.chunksize (which really refers to 'region size').
+ * Ensure that (ti->len/region_size < 2^21) - required by MD bitmap.
+ *
+ * Returns: 0 on success, -EINVAL on failure.
+ */
+static int validate_region_size(struct raid_set *rs, unsigned long region_size)
+{
+ unsigned long min_region_size = rs->ti->len / (1 << 21);
+
+ if (!region_size) {
+ /*
+ * Choose a reasonable default. All figures in sectors.
+ */
+ if (min_region_size > (1 << 13)) {
+ DMINFO("Choosing default region size of %lu sectors",
+ region_size);
+ region_size = min_region_size;
+ } else {
+ DMINFO("Choosing default region size of 4MiB");
+ region_size = 1 << 13; /* sectors */
+ }
+ } else {
+ /*
+ * Validate user-supplied value.
+ */
+ if (region_size > rs->ti->len) {
+ rs->ti->error = "Supplied region size is too large";
+ return -EINVAL;
+ }
+
+ if (region_size < min_region_size) {
+ DMERR("Supplied region_size (%lu sectors) below minimum (%lu)",
+ region_size, min_region_size);
+ rs->ti->error = "Supplied region size is too small";
+ return -EINVAL;
+ }
+
+ if (!is_power_of_2(region_size)) {
+ rs->ti->error = "Region size is not a power of 2";
+ return -EINVAL;
+ }
+
+ if (region_size < rs->md.chunk_sectors) {
+ rs->ti->error = "Region size is smaller than the chunk size";
+ return -EINVAL;
+ }
+ }
+
+ /*
+ * Convert sectors to bytes.
+ */
+ rs->md.bitmap_info.chunksize = (region_size << 9);
+
+ return 0;
+}
+
+/*
* Possible arguments are...
- * RAID456:
* <chunk_size> [optional_args]
*
- * Optional args:
- * [[no]sync] Force or prevent recovery of the entire array
+ * Argument definitions
+ * <chunk_size> The number of sectors per disk that
+ * will form the "stripe"
+ * [[no]sync] Force or prevent recovery of the
+ * entire array
* [rebuild <idx>] Rebuild the drive indicated by the index
- * [daemon_sleep <ms>] Time between bitmap daemon work to clear bits
+ * [daemon_sleep <ms>] Time between bitmap daemon work to
+ * clear bits
* [min_recovery_rate <kB/sec/disk>] Throttle RAID initialization
* [max_recovery_rate <kB/sec/disk>] Throttle RAID initialization
+ * [write_mostly <idx>] Indicate a write mostly drive via index
* [max_write_behind <sectors>] See '-write-behind=' (man mdadm)
* [stripe_cache <sectors>] Stripe cache size for higher RAIDs
+ * [region_size <sectors>] Defines granularity of bitmap
*/
static int parse_raid_params(struct raid_set *rs, char **argv,
unsigned num_raid_params)
{
unsigned i, rebuild_cnt = 0;
- unsigned long value;
+ unsigned long value, region_size = 0;
char *key;
/*
* First, parse the in-order required arguments
+ * "chunk_size" is the only argument of this type.
*/
- if ((strict_strtoul(argv[0], 10, &value) < 0) ||
- !is_power_of_2(value) || (value < 8)) {
+ if ((strict_strtoul(argv[0], 10, &value) < 0)) {
rs->ti->error = "Bad chunk size";
return -EINVAL;
+ } else if (rs->raid_type->level == 1) {
+ if (value)
+ DMERR("Ignoring chunk size parameter for RAID 1");
+ value = 0;
+ } else if (!is_power_of_2(value)) {
+ rs->ti->error = "Chunk size must be a power of 2";
+ return -EINVAL;
+ } else if (value < 8) {
+ rs->ti->error = "Chunk size value is too small";
+ return -EINVAL;
}
rs->md.new_chunk_sectors = rs->md.chunk_sectors = value;
@@ -269,22 +380,39 @@
num_raid_params--;
/*
+ * We set each individual device as In_sync with a completed
+ * 'recovery_offset'. If there has been a device failure or
+ * replacement then one of the following cases applies:
+ *
+ * 1) User specifies 'rebuild'.
+ * - Device is reset when param is read.
+ * 2) A new device is supplied.
+ * - No matching superblock found, resets device.
+ * 3) Device failure was transient and returns on reload.
+ * - Failure noticed, resets device for bitmap replay.
+ * 4) Device hadn't completed recovery after previous failure.
+ * - Superblock is read and overrides recovery_offset.
+ *
+ * What is found in the superblocks of the devices is always
+ * authoritative, unless 'rebuild' or '[no]sync' was specified.
+ */
+ for (i = 0; i < rs->md.raid_disks; i++) {
+ set_bit(In_sync, &rs->dev[i].rdev.flags);
+ rs->dev[i].rdev.recovery_offset = MaxSector;
+ }
+
+ /*
* Second, parse the unordered optional arguments
*/
- for (i = 0; i < rs->md.raid_disks; i++)
- set_bit(In_sync, &rs->dev[i].rdev.flags);
-
for (i = 0; i < num_raid_params; i++) {
- if (!strcmp(argv[i], "nosync")) {
+ if (!strcasecmp(argv[i], "nosync")) {
rs->md.recovery_cp = MaxSector;
rs->print_flags |= DMPF_NOSYNC;
- rs->md.flags |= MD_SYNC_STATE_FORCED;
continue;
}
- if (!strcmp(argv[i], "sync")) {
+ if (!strcasecmp(argv[i], "sync")) {
rs->md.recovery_cp = 0;
rs->print_flags |= DMPF_SYNC;
- rs->md.flags |= MD_SYNC_STATE_FORCED;
continue;
}
@@ -300,9 +428,13 @@
return -EINVAL;
}
- if (!strcmp(key, "rebuild")) {
- if (++rebuild_cnt > rs->raid_type->parity_devs) {
- rs->ti->error = "Too many rebuild drives given";
+ if (!strcasecmp(key, "rebuild")) {
+ rebuild_cnt++;
+ if (((rs->raid_type->level != 1) &&
+ (rebuild_cnt > rs->raid_type->parity_devs)) ||
+ ((rs->raid_type->level == 1) &&
+ (rebuild_cnt > (rs->md.raid_disks - 1)))) {
+ rs->ti->error = "Too many rebuild devices specified for given RAID type";
return -EINVAL;
}
if (value > rs->md.raid_disks) {
@@ -311,7 +443,22 @@
}
clear_bit(In_sync, &rs->dev[value].rdev.flags);
rs->dev[value].rdev.recovery_offset = 0;
- } else if (!strcmp(key, "max_write_behind")) {
+ rs->print_flags |= DMPF_REBUILD;
+ } else if (!strcasecmp(key, "write_mostly")) {
+ if (rs->raid_type->level != 1) {
+ rs->ti->error = "write_mostly option is only valid for RAID1";
+ return -EINVAL;
+ }
+ if (value > rs->md.raid_disks) {
+ rs->ti->error = "Invalid write_mostly drive index given";
+ return -EINVAL;
+ }
+ set_bit(WriteMostly, &rs->dev[value].rdev.flags);
+ } else if (!strcasecmp(key, "max_write_behind")) {
+ if (rs->raid_type->level != 1) {
+ rs->ti->error = "max_write_behind option is only valid for RAID1";
+ return -EINVAL;
+ }
rs->print_flags |= DMPF_MAX_WRITE_BEHIND;
/*
@@ -324,14 +471,14 @@
return -EINVAL;
}
rs->md.bitmap_info.max_write_behind = value;
- } else if (!strcmp(key, "daemon_sleep")) {
+ } else if (!strcasecmp(key, "daemon_sleep")) {
rs->print_flags |= DMPF_DAEMON_SLEEP;
if (!value || (value > MAX_SCHEDULE_TIMEOUT)) {
rs->ti->error = "daemon sleep period out of range";
return -EINVAL;
}
rs->md.bitmap_info.daemon_sleep = value;
- } else if (!strcmp(key, "stripe_cache")) {
+ } else if (!strcasecmp(key, "stripe_cache")) {
rs->print_flags |= DMPF_STRIPE_CACHE;
/*
@@ -348,20 +495,23 @@
rs->ti->error = "Bad stripe_cache size";
return -EINVAL;
}
- } else if (!strcmp(key, "min_recovery_rate")) {
+ } else if (!strcasecmp(key, "min_recovery_rate")) {
rs->print_flags |= DMPF_MIN_RECOVERY_RATE;
if (value > INT_MAX) {
rs->ti->error = "min_recovery_rate out of range";
return -EINVAL;
}
rs->md.sync_speed_min = (int)value;
- } else if (!strcmp(key, "max_recovery_rate")) {
+ } else if (!strcasecmp(key, "max_recovery_rate")) {
rs->print_flags |= DMPF_MAX_RECOVERY_RATE;
if (value > INT_MAX) {
rs->ti->error = "max_recovery_rate out of range";
return -EINVAL;
}
rs->md.sync_speed_max = (int)value;
+ } else if (!strcasecmp(key, "region_size")) {
+ rs->print_flags |= DMPF_REGION_SIZE;
+ region_size = value;
} else {
DMERR("Unable to parse RAID parameter: %s", key);
rs->ti->error = "Unable to parse RAID parameters";
@@ -369,6 +519,19 @@
}
}
+ if (validate_region_size(rs, region_size))
+ return -EINVAL;
+
+ if (rs->md.chunk_sectors)
+ rs->ti->split_io = rs->md.chunk_sectors;
+ else
+ rs->ti->split_io = region_size;
+
+ if (rs->md.chunk_sectors)
+ rs->ti->split_io = rs->md.chunk_sectors;
+ else
+ rs->ti->split_io = region_size;
+
/* Assume there are no metadata devices until the drives are parsed */
rs->md.persistent = 0;
rs->md.external = 1;
@@ -387,17 +550,351 @@
{
struct raid_set *rs = container_of(cb, struct raid_set, callbacks);
+ if (rs->raid_type->level == 1)
+ return md_raid1_congested(&rs->md, bits);
+
return md_raid5_congested(&rs->md, bits);
}
/*
+ * This structure is never routinely used by userspace, unlike md superblocks.
+ * Devices with this superblock should only ever be accessed via device-mapper.
+ */
+#define DM_RAID_MAGIC 0x64526D44
+struct dm_raid_superblock {
+ __le32 magic; /* "DmRd" */
+ __le32 features; /* Used to indicate possible future changes */
+
+ __le32 num_devices; /* Number of devices in this array. (Max 64) */
+ __le32 array_position; /* The position of this drive in the array */
+
+ __le64 events; /* Incremented by md when superblock updated */
+ __le64 failed_devices; /* Bit field of devices to indicate failures */
+
+ /*
+ * This offset tracks the progress of the repair or replacement of
+ * an individual drive.
+ */
+ __le64 disk_recovery_offset;
+
+ /*
+ * This offset tracks the progress of the initial array
+ * synchronisation/parity calculation.
+ */
+ __le64 array_resync_offset;
+
+ /*
+ * RAID characteristics
+ */
+ __le32 level;
+ __le32 layout;
+ __le32 stripe_sectors;
+
+ __u8 pad[452]; /* Round struct to 512 bytes. */
+ /* Always set to 0 when writing. */
+} __packed;
+
+static int read_disk_sb(mdk_rdev_t *rdev, int size)
+{
+ BUG_ON(!rdev->sb_page);
+
+ if (rdev->sb_loaded)
+ return 0;
+
+ if (!sync_page_io(rdev, 0, size, rdev->sb_page, READ, 1)) {
+ DMERR("Failed to read device superblock");
+ return -EINVAL;
+ }
+
+ rdev->sb_loaded = 1;
+
+ return 0;
+}
+
+static void super_sync(mddev_t *mddev, mdk_rdev_t *rdev)
+{
+ mdk_rdev_t *r, *t;
+ uint64_t failed_devices;
+ struct dm_raid_superblock *sb;
+
+ sb = page_address(rdev->sb_page);
+ failed_devices = le64_to_cpu(sb->failed_devices);
+
+ rdev_for_each(r, t, mddev)
+ if ((r->raid_disk >= 0) && test_bit(Faulty, &r->flags))
+ failed_devices |= (1ULL << r->raid_disk);
+
+ memset(sb, 0, sizeof(*sb));
+
+ sb->magic = cpu_to_le32(DM_RAID_MAGIC);
+ sb->features = cpu_to_le32(0); /* No features yet */
+
+ sb->num_devices = cpu_to_le32(mddev->raid_disks);
+ sb->array_position = cpu_to_le32(rdev->raid_disk);
+
+ sb->events = cpu_to_le64(mddev->events);
+ sb->failed_devices = cpu_to_le64(failed_devices);
+
+ sb->disk_recovery_offset = cpu_to_le64(rdev->recovery_offset);
+ sb->array_resync_offset = cpu_to_le64(mddev->recovery_cp);
+
+ sb->level = cpu_to_le32(mddev->level);
+ sb->layout = cpu_to_le32(mddev->layout);
+ sb->stripe_sectors = cpu_to_le32(mddev->chunk_sectors);
+}
+
+/*
+ * super_load
+ *
+ * This function creates a superblock if one is not found on the device
+ * and will decide which superblock to use if there's a choice.
+ *
+ * Return: 1 if use rdev, 0 if use refdev, -Exxx otherwise
+ */
+static int super_load(mdk_rdev_t *rdev, mdk_rdev_t *refdev)
+{
+ int ret;
+ struct dm_raid_superblock *sb;
+ struct dm_raid_superblock *refsb;
+ uint64_t events_sb, events_refsb;
+
+ rdev->sb_start = 0;
+ rdev->sb_size = sizeof(*sb);
+
+ ret = read_disk_sb(rdev, rdev->sb_size);
+ if (ret)
+ return ret;
+
+ sb = page_address(rdev->sb_page);
+ if (sb->magic != cpu_to_le32(DM_RAID_MAGIC)) {
+ super_sync(rdev->mddev, rdev);
+
+ set_bit(FirstUse, &rdev->flags);
+
+ /* Force writing of superblocks to disk */
+ set_bit(MD_CHANGE_DEVS, &rdev->mddev->flags);
+
+ /* Any superblock is better than none, choose that if given */
+ return refdev ? 0 : 1;
+ }
+
+ if (!refdev)
+ return 1;
+
+ events_sb = le64_to_cpu(sb->events);
+
+ refsb = page_address(refdev->sb_page);
+ events_refsb = le64_to_cpu(refsb->events);
+
+ return (events_sb > events_refsb) ? 1 : 0;
+}
+
+static int super_init_validation(mddev_t *mddev, mdk_rdev_t *rdev)
+{
+ int role;
+ struct raid_set *rs = container_of(mddev, struct raid_set, md);
+ uint64_t events_sb;
+ uint64_t failed_devices;
+ struct dm_raid_superblock *sb;
+ uint32_t new_devs = 0;
+ uint32_t rebuilds = 0;
+ mdk_rdev_t *r, *t;
+ struct dm_raid_superblock *sb2;
+
+ sb = page_address(rdev->sb_page);
+ events_sb = le64_to_cpu(sb->events);
+ failed_devices = le64_to_cpu(sb->failed_devices);
+
+ /*
+ * Initialise to 1 if this is a new superblock.
+ */
+ mddev->events = events_sb ? : 1;
+
+ /*
+ * Reshaping is not currently allowed
+ */
+ if ((le32_to_cpu(sb->level) != mddev->level) ||
+ (le32_to_cpu(sb->layout) != mddev->layout) ||
+ (le32_to_cpu(sb->stripe_sectors) != mddev->chunk_sectors)) {
+ DMERR("Reshaping arrays not yet supported.");
+ return -EINVAL;
+ }
+
+ /* We can only change the number of devices in RAID1 right now */
+ if ((rs->raid_type->level != 1) &&
+ (le32_to_cpu(sb->num_devices) != mddev->raid_disks)) {
+ DMERR("Reshaping arrays not yet supported.");
+ return -EINVAL;
+ }
+
+ if (!(rs->print_flags & (DMPF_SYNC | DMPF_NOSYNC)))
+ mddev->recovery_cp = le64_to_cpu(sb->array_resync_offset);
+
+ /*
+ * During load, we set FirstUse if a new superblock was written.
+ * There are two reasons we might not have a superblock:
+ * 1) The array is brand new - in which case, all of the
+ * devices must have their In_sync bit set. Also,
+ * recovery_cp must be 0, unless forced.
+ * 2) This is a new device being added to an old array
+ * and the new device needs to be rebuilt - in which
+ * case the In_sync bit will /not/ be set and
+ * recovery_cp must be MaxSector.
+ */
+ rdev_for_each(r, t, mddev) {
+ if (!test_bit(In_sync, &r->flags)) {
+ if (!test_bit(FirstUse, &r->flags))
+ DMERR("Superblock area of "
+ "rebuild device %d should have been "
+ "cleared.", r->raid_disk);
+ set_bit(FirstUse, &r->flags);
+ rebuilds++;
+ } else if (test_bit(FirstUse, &r->flags))
+ new_devs++;
+ }
+
+ if (!rebuilds) {
+ if (new_devs == mddev->raid_disks) {
+ DMINFO("Superblocks created for new array");
+ set_bit(MD_ARRAY_FIRST_USE, &mddev->flags);
+ } else if (new_devs) {
+ DMERR("New device injected "
+ "into existing array without 'rebuild' "
+ "parameter specified");
+ return -EINVAL;
+ }
+ } else if (new_devs) {
+ DMERR("'rebuild' devices cannot be "
+ "injected into an array with other first-time devices");
+ return -EINVAL;
+ } else if (mddev->recovery_cp != MaxSector) {
+ DMERR("'rebuild' specified while array is not in-sync");
+ return -EINVAL;
+ }
+
+ /*
+ * Now we set the Faulty bit for those devices that are
+ * recorded in the superblock as failed.
+ */
+ rdev_for_each(r, t, mddev) {
+ if (!r->sb_page)
+ continue;
+ sb2 = page_address(r->sb_page);
+ sb2->failed_devices = 0;
+
+ /*
+ * Check for any device re-ordering.
+ */
+ if (!test_bit(FirstUse, &r->flags) && (r->raid_disk >= 0)) {
+ role = le32_to_cpu(sb2->array_position);
+ if (role != r->raid_disk) {
+ if (rs->raid_type->level != 1) {
+ rs->ti->error = "Cannot change device "
+ "positions in RAID array";
+ return -EINVAL;
+ }
+ DMINFO("RAID1 device #%d now at position #%d",
+ role, r->raid_disk);
+ }
+
+ /*
+ * Partial recovery is performed on
+ * returning failed devices.
+ */
+ if (failed_devices & (1 << role))
+ set_bit(Faulty, &r->flags);
+ }
+ }
+
+ return 0;
+}
+
+static int super_validate(mddev_t *mddev, mdk_rdev_t *rdev)
+{
+ struct dm_raid_superblock *sb = page_address(rdev->sb_page);
+
+ /*
+ * If mddev->events is not set, we know we have not yet initialized
+ * the array.
+ */
+ if (!mddev->events && super_init_validation(mddev, rdev))
+ return -EINVAL;
+
+ mddev->bitmap_info.offset = 4096 >> 9; /* Enable bitmap creation */
+ rdev->mddev->bitmap_info.default_offset = 4096 >> 9;
+ if (!test_bit(FirstUse, &rdev->flags)) {
+ rdev->recovery_offset = le64_to_cpu(sb->disk_recovery_offset);
+ if (rdev->recovery_offset != MaxSector)
+ clear_bit(In_sync, &rdev->flags);
+ }
+
+ /*
+ * If a device comes back, set it as not In_sync and no longer faulty.
+ */
+ if (test_bit(Faulty, &rdev->flags)) {
+ clear_bit(Faulty, &rdev->flags);
+ clear_bit(In_sync, &rdev->flags);
+ rdev->saved_raid_disk = rdev->raid_disk;
+ rdev->recovery_offset = 0;
+ }
+
+ clear_bit(FirstUse, &rdev->flags);
+
+ return 0;
+}
+
+/*
+ * Analyse superblocks and select the freshest.
+ */
+static int analyse_superblocks(struct dm_target *ti, struct raid_set *rs)
+{
+ int ret;
+ mdk_rdev_t *rdev, *freshest, *tmp;
+ mddev_t *mddev = &rs->md;
+
+ freshest = NULL;
+ rdev_for_each(rdev, tmp, mddev) {
+ if (!rdev->meta_bdev)
+ continue;
+
+ ret = super_load(rdev, freshest);
+
+ switch (ret) {
+ case 1:
+ freshest = rdev;
+ break;
+ case 0:
+ break;
+ default:
+ ti->error = "Failed to load superblock";
+ return ret;
+ }
+ }
+
+ if (!freshest)
+ return 0;
+
+ /*
+ * Validation of the freshest device provides the source of
+ * validation for the remaining devices.
+ */
+ ti->error = "Unable to assemble array: Invalid superblocks";
+ if (super_validate(mddev, freshest))
+ return -EINVAL;
+
+ rdev_for_each(rdev, tmp, mddev)
+ if ((rdev != freshest) && super_validate(mddev, rdev))
+ return -EINVAL;
+
+ return 0;
+}
+
+/*
* Construct a RAID4/5/6 mapping:
* Args:
* <raid_type> <#raid_params> <raid_params> \
* <#raid_devs> { <meta_dev1> <dev1> .. <meta_devN> <devN> }
*
- * ** metadata devices are not supported yet, use '-' instead **
- *
* <raid_params> varies by <raid_type>. See 'parse_raid_params' for
* details on possible <raid_params>.
*/
@@ -465,8 +962,12 @@
if (ret)
goto bad;
+ rs->md.sync_super = super_sync;
+ ret = analyse_superblocks(ti, rs);
+ if (ret)
+ goto bad;
+
INIT_WORK(&rs->md.event_work, do_table_event);
- ti->split_io = rs->md.chunk_sectors;
ti->private = rs;
mutex_lock(&rs->md.reconfig_mutex);
@@ -482,6 +983,7 @@
rs->callbacks.congested_fn = raid_is_congested;
dm_table_add_target_callbacks(ti->table, &rs->callbacks);
+ mddev_suspend(&rs->md);
return 0;
bad:
@@ -546,12 +1048,17 @@
break;
case STATUSTYPE_TABLE:
/* The string you would use to construct this array */
- for (i = 0; i < rs->md.raid_disks; i++)
- if (rs->dev[i].data_dev &&
+ for (i = 0; i < rs->md.raid_disks; i++) {
+ if ((rs->print_flags & DMPF_REBUILD) &&
+ rs->dev[i].data_dev &&
!test_bit(In_sync, &rs->dev[i].rdev.flags))
- raid_param_cnt++; /* for rebuilds */
+ raid_param_cnt += 2; /* for rebuilds */
+ if (rs->dev[i].data_dev &&
+ test_bit(WriteMostly, &rs->dev[i].rdev.flags))
+ raid_param_cnt += 2;
+ }
- raid_param_cnt += (hweight64(rs->print_flags) * 2);
+ raid_param_cnt += (hweight64(rs->print_flags & ~DMPF_REBUILD) * 2);
if (rs->print_flags & (DMPF_SYNC | DMPF_NOSYNC))
raid_param_cnt--;
@@ -565,7 +1072,8 @@
DMEMIT(" nosync");
for (i = 0; i < rs->md.raid_disks; i++)
- if (rs->dev[i].data_dev &&
+ if ((rs->print_flags & DMPF_REBUILD) &&
+ rs->dev[i].data_dev &&
!test_bit(In_sync, &rs->dev[i].rdev.flags))
DMEMIT(" rebuild %u", i);
@@ -579,6 +1087,11 @@
if (rs->print_flags & DMPF_MAX_RECOVERY_RATE)
DMEMIT(" max_recovery_rate %d", rs->md.sync_speed_max);
+ for (i = 0; i < rs->md.raid_disks; i++)
+ if (rs->dev[i].data_dev &&
+ test_bit(WriteMostly, &rs->dev[i].rdev.flags))
+ DMEMIT(" write_mostly %u", i);
+
if (rs->print_flags & DMPF_MAX_WRITE_BEHIND)
DMEMIT(" max_write_behind %lu",
rs->md.bitmap_info.max_write_behind);
@@ -591,9 +1104,16 @@
conf ? conf->max_nr_stripes * 2 : 0);
}
+ if (rs->print_flags & DMPF_REGION_SIZE)
+ DMEMIT(" region_size %lu",
+ rs->md.bitmap_info.chunksize >> 9);
+
DMEMIT(" %d", rs->md.raid_disks);
for (i = 0; i < rs->md.raid_disks; i++) {
- DMEMIT(" -"); /* metadata device */
+ if (rs->dev[i].meta_dev)
+ DMEMIT(" %s", rs->dev[i].meta_dev->name);
+ else
+ DMEMIT(" -");
if (rs->dev[i].data_dev)
DMEMIT(" %s", rs->dev[i].data_dev->name);
@@ -650,12 +1170,13 @@
{
struct raid_set *rs = ti->private;
+ bitmap_load(&rs->md);
mddev_resume(&rs->md);
}
static struct target_type raid_target = {
.name = "raid",
- .version = {1, 0, 0},
+ .version = {1, 1, 0},
.module = THIS_MODULE,
.ctr = raid_ctr,
.dtr = raid_dtr,
diff --git a/drivers/md/dm-snap-persistent.c b/drivers/md/dm-snap-persistent.c
index 135c2f1..d1f1d70 100644
--- a/drivers/md/dm-snap-persistent.c
+++ b/drivers/md/dm-snap-persistent.c
@@ -58,25 +58,30 @@
#define NUM_SNAPSHOT_HDR_CHUNKS 1
struct disk_header {
- uint32_t magic;
+ __le32 magic;
/*
* Is this snapshot valid. There is no way of recovering
* an invalid snapshot.
*/
- uint32_t valid;
+ __le32 valid;
/*
* Simple, incrementing version. no backward
* compatibility.
*/
- uint32_t version;
+ __le32 version;
/* In sectors */
- uint32_t chunk_size;
-};
+ __le32 chunk_size;
+} __packed;
struct disk_exception {
+ __le64 old_chunk;
+ __le64 new_chunk;
+} __packed;
+
+struct core_exception {
uint64_t old_chunk;
uint64_t new_chunk;
};
@@ -169,10 +174,9 @@
if (!ps->area)
goto err_area;
- ps->zero_area = vmalloc(len);
+ ps->zero_area = vzalloc(len);
if (!ps->zero_area)
goto err_zero_area;
- memset(ps->zero_area, 0, len);
ps->header_area = vmalloc(len);
if (!ps->header_area)
@@ -396,32 +400,32 @@
}
static void read_exception(struct pstore *ps,
- uint32_t index, struct disk_exception *result)
+ uint32_t index, struct core_exception *result)
{
- struct disk_exception *e = get_exception(ps, index);
+ struct disk_exception *de = get_exception(ps, index);
/* copy it */
- result->old_chunk = le64_to_cpu(e->old_chunk);
- result->new_chunk = le64_to_cpu(e->new_chunk);
+ result->old_chunk = le64_to_cpu(de->old_chunk);
+ result->new_chunk = le64_to_cpu(de->new_chunk);
}
static void write_exception(struct pstore *ps,
- uint32_t index, struct disk_exception *de)
+ uint32_t index, struct core_exception *e)
{
- struct disk_exception *e = get_exception(ps, index);
+ struct disk_exception *de = get_exception(ps, index);
/* copy it */
- e->old_chunk = cpu_to_le64(de->old_chunk);
- e->new_chunk = cpu_to_le64(de->new_chunk);
+ de->old_chunk = cpu_to_le64(e->old_chunk);
+ de->new_chunk = cpu_to_le64(e->new_chunk);
}
static void clear_exception(struct pstore *ps, uint32_t index)
{
- struct disk_exception *e = get_exception(ps, index);
+ struct disk_exception *de = get_exception(ps, index);
/* clear it */
- e->old_chunk = 0;
- e->new_chunk = 0;
+ de->old_chunk = 0;
+ de->new_chunk = 0;
}
/*
@@ -437,13 +441,13 @@
{
int r;
unsigned int i;
- struct disk_exception de;
+ struct core_exception e;
/* presume the area is full */
*full = 1;
for (i = 0; i < ps->exceptions_per_area; i++) {
- read_exception(ps, i, &de);
+ read_exception(ps, i, &e);
/*
* If the new_chunk is pointing at the start of
@@ -451,7 +455,7 @@
* is we know that we've hit the end of the
* exceptions. Therefore the area is not full.
*/
- if (de.new_chunk == 0LL) {
+ if (e.new_chunk == 0LL) {
ps->current_committed = i;
*full = 0;
break;
@@ -460,13 +464,13 @@
/*
* Keep track of the start of the free chunks.
*/
- if (ps->next_free <= de.new_chunk)
- ps->next_free = de.new_chunk + 1;
+ if (ps->next_free <= e.new_chunk)
+ ps->next_free = e.new_chunk + 1;
/*
* Otherwise we add the exception to the snapshot.
*/
- r = callback(callback_context, de.old_chunk, de.new_chunk);
+ r = callback(callback_context, e.old_chunk, e.new_chunk);
if (r)
return r;
}
@@ -563,7 +567,7 @@
ps->exceptions_per_area = (ps->store->chunk_size << SECTOR_SHIFT) /
sizeof(struct disk_exception);
ps->callbacks = dm_vcalloc(ps->exceptions_per_area,
- sizeof(*ps->callbacks));
+ sizeof(*ps->callbacks));
if (!ps->callbacks)
return -ENOMEM;
@@ -641,12 +645,12 @@
{
unsigned int i;
struct pstore *ps = get_info(store);
- struct disk_exception de;
+ struct core_exception ce;
struct commit_callback *cb;
- de.old_chunk = e->old_chunk;
- de.new_chunk = e->new_chunk;
- write_exception(ps, ps->current_committed++, &de);
+ ce.old_chunk = e->old_chunk;
+ ce.new_chunk = e->new_chunk;
+ write_exception(ps, ps->current_committed++, &ce);
/*
* Add the callback to the back of the array. This code
@@ -670,7 +674,7 @@
* If we completely filled the current area, then wipe the next one.
*/
if ((ps->current_committed == ps->exceptions_per_area) &&
- zero_disk_area(ps, ps->current_area + 1))
+ zero_disk_area(ps, ps->current_area + 1))
ps->valid = 0;
/*
@@ -701,7 +705,7 @@
chunk_t *last_new_chunk)
{
struct pstore *ps = get_info(store);
- struct disk_exception de;
+ struct core_exception ce;
int nr_consecutive;
int r;
@@ -722,9 +726,9 @@
ps->current_committed = ps->exceptions_per_area;
}
- read_exception(ps, ps->current_committed - 1, &de);
- *last_old_chunk = de.old_chunk;
- *last_new_chunk = de.new_chunk;
+ read_exception(ps, ps->current_committed - 1, &ce);
+ *last_old_chunk = ce.old_chunk;
+ *last_new_chunk = ce.new_chunk;
/*
* Find number of consecutive chunks within the current area,
@@ -733,9 +737,9 @@
for (nr_consecutive = 1; nr_consecutive < ps->current_committed;
nr_consecutive++) {
read_exception(ps, ps->current_committed - 1 - nr_consecutive,
- &de);
- if (de.old_chunk != *last_old_chunk - nr_consecutive ||
- de.new_chunk != *last_new_chunk - nr_consecutive)
+ &ce);
+ if (ce.old_chunk != *last_old_chunk - nr_consecutive ||
+ ce.new_chunk != *last_new_chunk - nr_consecutive)
break;
}
@@ -753,7 +757,7 @@
for (i = 0; i < nr_merged; i++)
clear_exception(ps, ps->current_committed - 1 - i);
- r = area_io(ps, WRITE);
+ r = area_io(ps, WRITE_FLUSH_FUA);
if (r < 0)
return r;
diff --git a/drivers/md/dm-snap.c b/drivers/md/dm-snap.c
index 9ecff5f..6f75887 100644
--- a/drivers/md/dm-snap.c
+++ b/drivers/md/dm-snap.c
@@ -30,16 +30,6 @@
((ti)->type->name == dm_snapshot_merge_target_name)
/*
- * The percentage increment we will wake up users at
- */
-#define WAKE_UP_PERCENT 5
-
-/*
- * kcopyd priority of snapshot operations
- */
-#define SNAPSHOT_COPY_PRIORITY 2
-
-/*
* The size of the mempool used to track chunks in use.
*/
#define MIN_IOS 256
@@ -180,6 +170,13 @@
* kcopyd.
*/
int started;
+
+ /*
+ * For writing a complete chunk, bypassing the copy.
+ */
+ struct bio *full_bio;
+ bio_end_io_t *full_bio_end_io;
+ void *full_bio_private;
};
/*
@@ -1055,8 +1052,7 @@
s = kmalloc(sizeof(*s), GFP_KERNEL);
if (!s) {
- ti->error = "Cannot allocate snapshot context private "
- "structure";
+ ti->error = "Cannot allocate private snapshot structure";
r = -ENOMEM;
goto bad;
}
@@ -1380,6 +1376,7 @@
struct dm_snapshot *s = pe->snap;
struct bio *origin_bios = NULL;
struct bio *snapshot_bios = NULL;
+ struct bio *full_bio = NULL;
int error = 0;
if (!success) {
@@ -1415,10 +1412,15 @@
*/
dm_insert_exception(&s->complete, e);
- out:
+out:
dm_remove_exception(&pe->e);
snapshot_bios = bio_list_get(&pe->snapshot_bios);
origin_bios = bio_list_get(&pe->origin_bios);
+ full_bio = pe->full_bio;
+ if (full_bio) {
+ full_bio->bi_end_io = pe->full_bio_end_io;
+ full_bio->bi_private = pe->full_bio_private;
+ }
free_pending_exception(pe);
increment_pending_exceptions_done_count();
@@ -1426,10 +1428,15 @@
up_write(&s->lock);
/* Submit any pending write bios */
- if (error)
+ if (error) {
+ if (full_bio)
+ bio_io_error(full_bio);
error_bios(snapshot_bios);
- else
+ } else {
+ if (full_bio)
+ bio_endio(full_bio, 0);
flush_bios(snapshot_bios);
+ }
retry_origin_bios(s, origin_bios);
}
@@ -1480,8 +1487,33 @@
dest.count = src.count;
/* Hand over to kcopyd */
- dm_kcopyd_copy(s->kcopyd_client,
- &src, 1, &dest, 0, copy_callback, pe);
+ dm_kcopyd_copy(s->kcopyd_client, &src, 1, &dest, 0, copy_callback, pe);
+}
+
+static void full_bio_end_io(struct bio *bio, int error)
+{
+ void *callback_data = bio->bi_private;
+
+ dm_kcopyd_do_callback(callback_data, 0, error ? 1 : 0);
+}
+
+static void start_full_bio(struct dm_snap_pending_exception *pe,
+ struct bio *bio)
+{
+ struct dm_snapshot *s = pe->snap;
+ void *callback_data;
+
+ pe->full_bio = bio;
+ pe->full_bio_end_io = bio->bi_end_io;
+ pe->full_bio_private = bio->bi_private;
+
+ callback_data = dm_kcopyd_prepare_callback(s->kcopyd_client,
+ copy_callback, pe);
+
+ bio->bi_end_io = full_bio_end_io;
+ bio->bi_private = callback_data;
+
+ generic_make_request(bio);
}
static struct dm_snap_pending_exception *
@@ -1519,6 +1551,7 @@
bio_list_init(&pe->origin_bios);
bio_list_init(&pe->snapshot_bios);
pe->started = 0;
+ pe->full_bio = NULL;
if (s->store->type->prepare_exception(s->store, &pe->e)) {
free_pending_exception(pe);
@@ -1612,10 +1645,19 @@
}
remap_exception(s, &pe->e, bio, chunk);
- bio_list_add(&pe->snapshot_bios, bio);
r = DM_MAPIO_SUBMITTED;
+ if (!pe->started &&
+ bio->bi_size == (s->store->chunk_size << SECTOR_SHIFT)) {
+ pe->started = 1;
+ up_write(&s->lock);
+ start_full_bio(pe, bio);
+ goto out;
+ }
+
+ bio_list_add(&pe->snapshot_bios, bio);
+
if (!pe->started) {
/* this is protected by snap->lock */
pe->started = 1;
@@ -1628,9 +1670,9 @@
map_context->ptr = track_chunk(s, chunk);
}
- out_unlock:
+out_unlock:
up_write(&s->lock);
- out:
+out:
return r;
}
@@ -1974,7 +2016,7 @@
pe_to_start_now = pe;
}
- next_snapshot:
+next_snapshot:
up_write(&snap->lock);
if (pe_to_start_now) {
diff --git a/drivers/md/dm-table.c b/drivers/md/dm-table.c
index bfe9c23..986b875 100644
--- a/drivers/md/dm-table.c
+++ b/drivers/md/dm-table.c
@@ -54,7 +54,6 @@
sector_t *highs;
struct dm_target *targets;
- unsigned discards_supported:1;
unsigned integrity_supported:1;
/*
@@ -154,12 +153,11 @@
return NULL;
size = nmemb * elem_size;
- addr = vmalloc(size);
- if (addr)
- memset(addr, 0, size);
+ addr = vzalloc(size);
return addr;
}
+EXPORT_SYMBOL(dm_vcalloc);
/*
* highs, and targets are managed as dynamic arrays during a
@@ -209,7 +207,6 @@
INIT_LIST_HEAD(&t->devices);
INIT_LIST_HEAD(&t->target_callbacks);
atomic_set(&t->holders, 0);
- t->discards_supported = 1;
if (!num_targets)
num_targets = KEYS_PER_NODE;
@@ -281,6 +278,7 @@
{
atomic_inc(&t->holders);
}
+EXPORT_SYMBOL(dm_table_get);
void dm_table_put(struct dm_table *t)
{
@@ -290,6 +288,7 @@
smp_mb__before_atomic_dec();
atomic_dec(&t->holders);
}
+EXPORT_SYMBOL(dm_table_put);
/*
* Checks to see if we need to extend highs or targets.
@@ -455,13 +454,14 @@
* Add a device to the list, or just increment the usage count if
* it's already present.
*/
-static int __table_get_device(struct dm_table *t, struct dm_target *ti,
- const char *path, fmode_t mode, struct dm_dev **result)
+int dm_get_device(struct dm_target *ti, const char *path, fmode_t mode,
+ struct dm_dev **result)
{
int r;
dev_t uninitialized_var(dev);
struct dm_dev_internal *dd;
unsigned int major, minor;
+ struct dm_table *t = ti->table;
BUG_ON(!t);
@@ -509,6 +509,7 @@
*result = &dd->dm_dev;
return 0;
}
+EXPORT_SYMBOL(dm_get_device);
int dm_set_device_limits(struct dm_target *ti, struct dm_dev *dev,
sector_t start, sector_t len, void *data)
@@ -539,23 +540,15 @@
* If not we'll force DM to use PAGE_SIZE or
* smaller I/O, just to be safe.
*/
-
- if (q->merge_bvec_fn && !ti->type->merge)
+ if (dm_queue_merge_is_compulsory(q) && !ti->type->merge)
blk_limits_max_hw_sectors(limits,
(unsigned int) (PAGE_SIZE >> 9));
return 0;
}
EXPORT_SYMBOL_GPL(dm_set_device_limits);
-int dm_get_device(struct dm_target *ti, const char *path, fmode_t mode,
- struct dm_dev **result)
-{
- return __table_get_device(ti->table, ti, path, mode, result);
-}
-
-
/*
- * Decrement a devices use count and remove it if necessary.
+ * Decrement a device's use count and remove it if necessary.
*/
void dm_put_device(struct dm_target *ti, struct dm_dev *d)
{
@@ -568,6 +561,7 @@
kfree(dd);
}
}
+EXPORT_SYMBOL(dm_put_device);
/*
* Checks to see if the target joins onto the end of the table.
@@ -791,8 +785,9 @@
t->highs[t->num_targets++] = tgt->begin + tgt->len - 1;
- if (!tgt->num_discard_requests)
- t->discards_supported = 0;
+ if (!tgt->num_discard_requests && tgt->discards_supported)
+ DMWARN("%s: %s: ignoring discards_supported because num_discard_requests is zero.",
+ dm_device_name(t->md), type);
return 0;
@@ -802,6 +797,63 @@
return r;
}
+/*
+ * Target argument parsing helpers.
+ */
+static int validate_next_arg(struct dm_arg *arg, struct dm_arg_set *arg_set,
+ unsigned *value, char **error, unsigned grouped)
+{
+ const char *arg_str = dm_shift_arg(arg_set);
+
+ if (!arg_str ||
+ (sscanf(arg_str, "%u", value) != 1) ||
+ (*value < arg->min) ||
+ (*value > arg->max) ||
+ (grouped && arg_set->argc < *value)) {
+ *error = arg->error;
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+int dm_read_arg(struct dm_arg *arg, struct dm_arg_set *arg_set,
+ unsigned *value, char **error)
+{
+ return validate_next_arg(arg, arg_set, value, error, 0);
+}
+EXPORT_SYMBOL(dm_read_arg);
+
+int dm_read_arg_group(struct dm_arg *arg, struct dm_arg_set *arg_set,
+ unsigned *value, char **error)
+{
+ return validate_next_arg(arg, arg_set, value, error, 1);
+}
+EXPORT_SYMBOL(dm_read_arg_group);
+
+const char *dm_shift_arg(struct dm_arg_set *as)
+{
+ char *r;
+
+ if (as->argc) {
+ as->argc--;
+ r = *as->argv;
+ as->argv++;
+ return r;
+ }
+
+ return NULL;
+}
+EXPORT_SYMBOL(dm_shift_arg);
+
+void dm_consume_args(struct dm_arg_set *as, unsigned num_args)
+{
+ BUG_ON(as->argc < num_args);
+ as->argc -= num_args;
+ as->argv += num_args;
+}
+EXPORT_SYMBOL(dm_consume_args);
+
static int dm_table_set_type(struct dm_table *t)
{
unsigned i;
@@ -1077,11 +1129,13 @@
t->event_fn(t->event_context);
mutex_unlock(&_event_lock);
}
+EXPORT_SYMBOL(dm_table_event);
sector_t dm_table_get_size(struct dm_table *t)
{
return t->num_targets ? (t->highs[t->num_targets - 1] + 1) : 0;
}
+EXPORT_SYMBOL(dm_table_get_size);
struct dm_target *dm_table_get_target(struct dm_table *t, unsigned int index)
{
@@ -1194,9 +1248,45 @@
blk_get_integrity(template_disk));
}
+static int device_flush_capable(struct dm_target *ti, struct dm_dev *dev,
+ sector_t start, sector_t len, void *data)
+{
+ unsigned flush = (*(unsigned *)data);
+ struct request_queue *q = bdev_get_queue(dev->bdev);
+
+ return q && (q->flush_flags & flush);
+}
+
+static bool dm_table_supports_flush(struct dm_table *t, unsigned flush)
+{
+ struct dm_target *ti;
+ unsigned i = 0;
+
+ /*
+ * Require at least one underlying device to support flushes.
+ * t->devices includes internal dm devices such as mirror logs
+ * so we need to use iterate_devices here, which targets
+ * supporting flushes must provide.
+ */
+ while (i < dm_table_get_num_targets(t)) {
+ ti = dm_table_get_target(t, i++);
+
+ if (!ti->num_flush_requests)
+ continue;
+
+ if (ti->type->iterate_devices &&
+ ti->type->iterate_devices(ti, device_flush_capable, &flush))
+ return 1;
+ }
+
+ return 0;
+}
+
void dm_table_set_restrictions(struct dm_table *t, struct request_queue *q,
struct queue_limits *limits)
{
+ unsigned flush = 0;
+
/*
* Copy table's limits to the DM device's request_queue
*/
@@ -1207,6 +1297,13 @@
else
queue_flag_set_unlocked(QUEUE_FLAG_DISCARD, q);
+ if (dm_table_supports_flush(t, REQ_FLUSH)) {
+ flush |= REQ_FLUSH;
+ if (dm_table_supports_flush(t, REQ_FUA))
+ flush |= REQ_FUA;
+ }
+ blk_queue_flush(q, flush);
+
dm_table_set_integrity(t);
/*
@@ -1237,6 +1334,7 @@
{
return t->mode;
}
+EXPORT_SYMBOL(dm_table_get_mode);
static void suspend_targets(struct dm_table *t, unsigned postsuspend)
{
@@ -1345,6 +1443,7 @@
{
return t->md;
}
+EXPORT_SYMBOL(dm_table_get_md);
static int device_discard_capable(struct dm_target *ti, struct dm_dev *dev,
sector_t start, sector_t len, void *data)
@@ -1359,19 +1458,19 @@
struct dm_target *ti;
unsigned i = 0;
- if (!t->discards_supported)
- return 0;
-
/*
* Unless any target used by the table set discards_supported,
* require at least one underlying device to support discards.
* t->devices includes internal dm devices such as mirror logs
* so we need to use iterate_devices here, which targets
- * supporting discard must provide.
+ * supporting discard selectively must provide.
*/
while (i < dm_table_get_num_targets(t)) {
ti = dm_table_get_target(t, i++);
+ if (!ti->num_discard_requests)
+ continue;
+
if (ti->discards_supported)
return 1;
@@ -1382,13 +1481,3 @@
return 0;
}
-
-EXPORT_SYMBOL(dm_vcalloc);
-EXPORT_SYMBOL(dm_get_device);
-EXPORT_SYMBOL(dm_put_device);
-EXPORT_SYMBOL(dm_table_event);
-EXPORT_SYMBOL(dm_table_get_size);
-EXPORT_SYMBOL(dm_table_get_mode);
-EXPORT_SYMBOL(dm_table_get_md);
-EXPORT_SYMBOL(dm_table_put);
-EXPORT_SYMBOL(dm_table_get);
diff --git a/drivers/md/dm.c b/drivers/md/dm.c
index 0cf68b4..52b39f3 100644
--- a/drivers/md/dm.c
+++ b/drivers/md/dm.c
@@ -37,6 +37,8 @@
static unsigned int major = 0;
static unsigned int _major = 0;
+static DEFINE_IDR(_minor_idr);
+
static DEFINE_SPINLOCK(_minor_lock);
/*
* For bio-based dm.
@@ -109,6 +111,7 @@
#define DMF_FREEING 3
#define DMF_DELETING 4
#define DMF_NOFLUSH_SUSPENDING 5
+#define DMF_MERGE_IS_OPTIONAL 6
/*
* Work processed by per-device workqueue.
@@ -313,6 +316,12 @@
while (i--)
_exits[i]();
+
+ /*
+ * Should be empty by this point.
+ */
+ idr_remove_all(&_minor_idr);
+ idr_destroy(&_minor_idr);
}
/*
@@ -1171,7 +1180,8 @@
/*
* Even though the device advertised discard support,
- * reconfiguration might have changed that since the
+ * that does not mean every target supports it, and
+ * reconfiguration might also have changed that since the
* check was performed.
*/
if (!ti->num_discard_requests)
@@ -1705,8 +1715,6 @@
/*-----------------------------------------------------------------
* An IDR is used to keep track of allocated minor numbers.
*---------------------------------------------------------------*/
-static DEFINE_IDR(_minor_idr);
-
static void free_minor(int minor)
{
spin_lock(&_minor_lock);
@@ -1800,7 +1808,6 @@
blk_queue_make_request(md->queue, dm_request);
blk_queue_bounce_limit(md->queue, BLK_BOUNCE_ANY);
blk_queue_merge_bvec(md->queue, dm_merge_bvec);
- blk_queue_flush(md->queue, REQ_FLUSH | REQ_FUA);
}
/*
@@ -1986,6 +1993,59 @@
}
/*
+ * Return 1 if the queue has a compulsory merge_bvec_fn function.
+ *
+ * If this function returns 0, then the device is either a non-dm
+ * device without a merge_bvec_fn, or it is a dm device that is
+ * able to split any bios it receives that are too big.
+ */
+int dm_queue_merge_is_compulsory(struct request_queue *q)
+{
+ struct mapped_device *dev_md;
+
+ if (!q->merge_bvec_fn)
+ return 0;
+
+ if (q->make_request_fn == dm_request) {
+ dev_md = q->queuedata;
+ if (test_bit(DMF_MERGE_IS_OPTIONAL, &dev_md->flags))
+ return 0;
+ }
+
+ return 1;
+}
+
+static int dm_device_merge_is_compulsory(struct dm_target *ti,
+ struct dm_dev *dev, sector_t start,
+ sector_t len, void *data)
+{
+ struct block_device *bdev = dev->bdev;
+ struct request_queue *q = bdev_get_queue(bdev);
+
+ return dm_queue_merge_is_compulsory(q);
+}
+
+/*
+ * Return 1 if it is acceptable to ignore merge_bvec_fn based
+ * on the properties of the underlying devices.
+ */
+static int dm_table_merge_is_optional(struct dm_table *table)
+{
+ unsigned i = 0;
+ struct dm_target *ti;
+
+ while (i < dm_table_get_num_targets(table)) {
+ ti = dm_table_get_target(table, i++);
+
+ if (ti->type->iterate_devices &&
+ ti->type->iterate_devices(ti, dm_device_merge_is_compulsory, NULL))
+ return 0;
+ }
+
+ return 1;
+}
+
+/*
* Returns old map, which caller must destroy.
*/
static struct dm_table *__bind(struct mapped_device *md, struct dm_table *t,
@@ -1995,6 +2055,7 @@
struct request_queue *q = md->queue;
sector_t size;
unsigned long flags;
+ int merge_is_optional;
size = dm_table_get_size(t);
@@ -2020,10 +2081,16 @@
__bind_mempools(md, t);
+ merge_is_optional = dm_table_merge_is_optional(t);
+
write_lock_irqsave(&md->map_lock, flags);
old_map = md->map;
md->map = t;
dm_table_set_restrictions(t, q, limits);
+ if (merge_is_optional)
+ set_bit(DMF_MERGE_IS_OPTIONAL, &md->flags);
+ else
+ clear_bit(DMF_MERGE_IS_OPTIONAL, &md->flags);
write_unlock_irqrestore(&md->map_lock, flags);
return old_map;
diff --git a/drivers/md/dm.h b/drivers/md/dm.h
index 1aaf167..6745dbd 100644
--- a/drivers/md/dm.h
+++ b/drivers/md/dm.h
@@ -66,6 +66,8 @@
void dm_table_free_md_mempools(struct dm_table *t);
struct dm_md_mempools *dm_table_get_md_mempools(struct dm_table *t);
+int dm_queue_merge_is_compulsory(struct request_queue *q);
+
void dm_lock_md_type(struct mapped_device *md);
void dm_unlock_md_type(struct mapped_device *md);
void dm_set_md_type(struct mapped_device *md, unsigned type);
diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig
index 37b83eb..21574bd 100644
--- a/drivers/mfd/Kconfig
+++ b/drivers/mfd/Kconfig
@@ -171,6 +171,37 @@
This driver can also be built as a module. If so, the module
will be called tps6586x.
+config MFD_TPS65910
+ bool "TPS65910 Power Management chip"
+ depends on I2C=y && GPIOLIB
+ select MFD_CORE
+ select GPIO_TPS65910
+ help
+ if you say yes here you get support for the TPS65910 series of
+ Power Management chips.
+
+config MFD_TPS65912
+ bool
+ depends on GPIOLIB
+
+config MFD_TPS65912_I2C
+ bool "TPS95612 Power Management chip with I2C"
+ select MFD_CORE
+ select MFD_TPS65912
+ depends on I2C=y && GPIOLIB
+ help
+ If you say yes here you get support for the TPS65912 series of
+ PM chips with I2C interface.
+
+config MFD_TPS65912_SPI
+ bool "TPS65912 Power Management chip with SPI"
+ select MFD_CORE
+ select MFD_TPS65912
+ depends on SPI_MASTER && GPIOLIB
+ help
+ If you say yes here you get support for the TPS65912 series of
+ PM chips with SPI interface.
+
config MENELAUS
bool "Texas Instruments TWL92330/Menelaus PM chip"
depends on I2C=y && ARCH_OMAP2
@@ -662,8 +693,9 @@
CAN and GPIO controllers.
config MFD_JZ4740_ADC
- tristate "Support for the JZ4740 SoC ADC core"
+ bool "Support for the JZ4740 SoC ADC core"
select MFD_CORE
+ select GENERIC_IRQ_CHIP
depends on MACH_JZ4740
help
Say yes here if you want support for the ADC unit in the JZ4740 SoC.
@@ -725,18 +757,19 @@
This is required to use certain other PM 8xxx features, such as GPIO
and MPP.
-config MFD_TPS65910
- bool "TPS65910 Power Management chip"
- depends on I2C=y && GPIOLIB
- select MFD_CORE
- select GPIO_TPS65910
- help
- if you say yes here you get support for the TPS65910 series of
- Power Management chips.
-
config TPS65911_COMPARATOR
tristate
+config MFD_AAT2870_CORE
+ bool "Support for the AnalogicTech AAT2870"
+ select MFD_CORE
+ depends on I2C=y && GPIOLIB
+ help
+ If you say yes here you get support for the AAT2870.
+ This driver provides common support for accessing the device,
+ additional drivers must be enabled in order to use the
+ functionality of the device.
+
endif # MFD_SUPPORT
menu "Multimedia Capabilities Port drivers"
diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile
index 22a280f..c580203 100644
--- a/drivers/mfd/Makefile
+++ b/drivers/mfd/Makefile
@@ -23,6 +23,7 @@
obj-$(CONFIG_MFD_WM8400) += wm8400-core.o
wm831x-objs := wm831x-core.o wm831x-irq.o wm831x-otp.o
+wm831x-objs += wm831x-auxadc.o
obj-$(CONFIG_MFD_WM831X) += wm831x.o
obj-$(CONFIG_MFD_WM831X_I2C) += wm831x-i2c.o
obj-$(CONFIG_MFD_WM831X_SPI) += wm831x-spi.o
@@ -35,6 +36,11 @@
obj-$(CONFIG_TPS6105X) += tps6105x.o
obj-$(CONFIG_TPS65010) += tps65010.o
obj-$(CONFIG_TPS6507X) += tps6507x.o
+obj-$(CONFIG_MFD_TPS65910) += tps65910.o tps65910-irq.o
+tps65912-objs := tps65912-core.o tps65912-irq.o
+obj-$(CONFIG_MFD_TPS65912) += tps65912.o
+obj-$(CONFIG_MFD_TPS65912_I2C) += tps65912-i2c.o
+obj-$(CONFIG_MFD_TPS65912_SPI) += tps65912-spi.o
obj-$(CONFIG_MENELAUS) += menelaus.o
obj-$(CONFIG_TWL4030_CORE) += twl-core.o twl4030-irq.o twl6030-irq.o
@@ -94,5 +100,5 @@
obj-$(CONFIG_MFD_OMAP_USB_HOST) += omap-usb-host.o
obj-$(CONFIG_MFD_PM8921_CORE) += pm8921-core.o
obj-$(CONFIG_MFD_PM8XXX_IRQ) += pm8xxx-irq.o
-obj-$(CONFIG_MFD_TPS65910) += tps65910.o tps65910-irq.o
obj-$(CONFIG_TPS65911_COMPARATOR) += tps65911-comparator.o
+obj-$(CONFIG_MFD_AAT2870_CORE) += aat2870-core.o
diff --git a/drivers/mfd/aat2870-core.c b/drivers/mfd/aat2870-core.c
new file mode 100644
index 0000000..345dc65
--- /dev/null
+++ b/drivers/mfd/aat2870-core.c
@@ -0,0 +1,535 @@
+/*
+ * linux/drivers/mfd/aat2870-core.c
+ *
+ * Copyright (c) 2011, NVIDIA Corporation.
+ * Author: Jin Park <jinyoungp@nvidia.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., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/debugfs.h>
+#include <linux/slab.h>
+#include <linux/uaccess.h>
+#include <linux/i2c.h>
+#include <linux/delay.h>
+#include <linux/gpio.h>
+#include <linux/mfd/core.h>
+#include <linux/mfd/aat2870.h>
+#include <linux/regulator/machine.h>
+
+static struct aat2870_register aat2870_regs[AAT2870_REG_NUM] = {
+ /* readable, writeable, value */
+ { 0, 1, 0x00 }, /* 0x00 AAT2870_BL_CH_EN */
+ { 0, 1, 0x16 }, /* 0x01 AAT2870_BLM */
+ { 0, 1, 0x16 }, /* 0x02 AAT2870_BLS */
+ { 0, 1, 0x56 }, /* 0x03 AAT2870_BL1 */
+ { 0, 1, 0x56 }, /* 0x04 AAT2870_BL2 */
+ { 0, 1, 0x56 }, /* 0x05 AAT2870_BL3 */
+ { 0, 1, 0x56 }, /* 0x06 AAT2870_BL4 */
+ { 0, 1, 0x56 }, /* 0x07 AAT2870_BL5 */
+ { 0, 1, 0x56 }, /* 0x08 AAT2870_BL6 */
+ { 0, 1, 0x56 }, /* 0x09 AAT2870_BL7 */
+ { 0, 1, 0x56 }, /* 0x0A AAT2870_BL8 */
+ { 0, 1, 0x00 }, /* 0x0B AAT2870_FLR */
+ { 0, 1, 0x03 }, /* 0x0C AAT2870_FM */
+ { 0, 1, 0x03 }, /* 0x0D AAT2870_FS */
+ { 0, 1, 0x10 }, /* 0x0E AAT2870_ALS_CFG0 */
+ { 0, 1, 0x06 }, /* 0x0F AAT2870_ALS_CFG1 */
+ { 0, 1, 0x00 }, /* 0x10 AAT2870_ALS_CFG2 */
+ { 1, 0, 0x00 }, /* 0x11 AAT2870_AMB */
+ { 0, 1, 0x00 }, /* 0x12 AAT2870_ALS0 */
+ { 0, 1, 0x00 }, /* 0x13 AAT2870_ALS1 */
+ { 0, 1, 0x00 }, /* 0x14 AAT2870_ALS2 */
+ { 0, 1, 0x00 }, /* 0x15 AAT2870_ALS3 */
+ { 0, 1, 0x00 }, /* 0x16 AAT2870_ALS4 */
+ { 0, 1, 0x00 }, /* 0x17 AAT2870_ALS5 */
+ { 0, 1, 0x00 }, /* 0x18 AAT2870_ALS6 */
+ { 0, 1, 0x00 }, /* 0x19 AAT2870_ALS7 */
+ { 0, 1, 0x00 }, /* 0x1A AAT2870_ALS8 */
+ { 0, 1, 0x00 }, /* 0x1B AAT2870_ALS9 */
+ { 0, 1, 0x00 }, /* 0x1C AAT2870_ALSA */
+ { 0, 1, 0x00 }, /* 0x1D AAT2870_ALSB */
+ { 0, 1, 0x00 }, /* 0x1E AAT2870_ALSC */
+ { 0, 1, 0x00 }, /* 0x1F AAT2870_ALSD */
+ { 0, 1, 0x00 }, /* 0x20 AAT2870_ALSE */
+ { 0, 1, 0x00 }, /* 0x21 AAT2870_ALSF */
+ { 0, 1, 0x00 }, /* 0x22 AAT2870_SUB_SET */
+ { 0, 1, 0x00 }, /* 0x23 AAT2870_SUB_CTRL */
+ { 0, 1, 0x00 }, /* 0x24 AAT2870_LDO_AB */
+ { 0, 1, 0x00 }, /* 0x25 AAT2870_LDO_CD */
+ { 0, 1, 0x00 }, /* 0x26 AAT2870_LDO_EN */
+};
+
+static struct mfd_cell aat2870_devs[] = {
+ {
+ .name = "aat2870-backlight",
+ .id = AAT2870_ID_BL,
+ .pdata_size = sizeof(struct aat2870_bl_platform_data),
+ },
+ {
+ .name = "aat2870-regulator",
+ .id = AAT2870_ID_LDOA,
+ .pdata_size = sizeof(struct regulator_init_data),
+ },
+ {
+ .name = "aat2870-regulator",
+ .id = AAT2870_ID_LDOB,
+ .pdata_size = sizeof(struct regulator_init_data),
+ },
+ {
+ .name = "aat2870-regulator",
+ .id = AAT2870_ID_LDOC,
+ .pdata_size = sizeof(struct regulator_init_data),
+ },
+ {
+ .name = "aat2870-regulator",
+ .id = AAT2870_ID_LDOD,
+ .pdata_size = sizeof(struct regulator_init_data),
+ },
+};
+
+static int __aat2870_read(struct aat2870_data *aat2870, u8 addr, u8 *val)
+{
+ int ret;
+
+ if (addr >= AAT2870_REG_NUM) {
+ dev_err(aat2870->dev, "Invalid address, 0x%02x\n", addr);
+ return -EINVAL;
+ }
+
+ if (!aat2870->reg_cache[addr].readable) {
+ *val = aat2870->reg_cache[addr].value;
+ goto out;
+ }
+
+ ret = i2c_master_send(aat2870->client, &addr, 1);
+ if (ret < 0)
+ return ret;
+ if (ret != 1)
+ return -EIO;
+
+ ret = i2c_master_recv(aat2870->client, val, 1);
+ if (ret < 0)
+ return ret;
+ if (ret != 1)
+ return -EIO;
+
+out:
+ dev_dbg(aat2870->dev, "read: addr=0x%02x, val=0x%02x\n", addr, *val);
+ return 0;
+}
+
+static int __aat2870_write(struct aat2870_data *aat2870, u8 addr, u8 val)
+{
+ u8 msg[2];
+ int ret;
+
+ if (addr >= AAT2870_REG_NUM) {
+ dev_err(aat2870->dev, "Invalid address, 0x%02x\n", addr);
+ return -EINVAL;
+ }
+
+ if (!aat2870->reg_cache[addr].writeable) {
+ dev_err(aat2870->dev, "Address 0x%02x is not writeable\n",
+ addr);
+ return -EINVAL;
+ }
+
+ msg[0] = addr;
+ msg[1] = val;
+ ret = i2c_master_send(aat2870->client, msg, 2);
+ if (ret < 0)
+ return ret;
+ if (ret != 2)
+ return -EIO;
+
+ aat2870->reg_cache[addr].value = val;
+
+ dev_dbg(aat2870->dev, "write: addr=0x%02x, val=0x%02x\n", addr, val);
+ return 0;
+}
+
+static int aat2870_read(struct aat2870_data *aat2870, u8 addr, u8 *val)
+{
+ int ret;
+
+ mutex_lock(&aat2870->io_lock);
+ ret = __aat2870_read(aat2870, addr, val);
+ mutex_unlock(&aat2870->io_lock);
+
+ return ret;
+}
+
+static int aat2870_write(struct aat2870_data *aat2870, u8 addr, u8 val)
+{
+ int ret;
+
+ mutex_lock(&aat2870->io_lock);
+ ret = __aat2870_write(aat2870, addr, val);
+ mutex_unlock(&aat2870->io_lock);
+
+ return ret;
+}
+
+static int aat2870_update(struct aat2870_data *aat2870, u8 addr, u8 mask,
+ u8 val)
+{
+ int change;
+ u8 old_val, new_val;
+ int ret;
+
+ mutex_lock(&aat2870->io_lock);
+
+ ret = __aat2870_read(aat2870, addr, &old_val);
+ if (ret)
+ goto out_unlock;
+
+ new_val = (old_val & ~mask) | (val & mask);
+ change = old_val != new_val;
+ if (change)
+ ret = __aat2870_write(aat2870, addr, new_val);
+
+out_unlock:
+ mutex_unlock(&aat2870->io_lock);
+
+ return ret;
+}
+
+static inline void aat2870_enable(struct aat2870_data *aat2870)
+{
+ if (aat2870->en_pin >= 0)
+ gpio_set_value(aat2870->en_pin, 1);
+
+ aat2870->is_enable = 1;
+}
+
+static inline void aat2870_disable(struct aat2870_data *aat2870)
+{
+ if (aat2870->en_pin >= 0)
+ gpio_set_value(aat2870->en_pin, 0);
+
+ aat2870->is_enable = 0;
+}
+
+#ifdef CONFIG_DEBUG_FS
+static ssize_t aat2870_dump_reg(struct aat2870_data *aat2870, char *buf)
+{
+ u8 addr, val;
+ ssize_t count = 0;
+ int ret;
+
+ count += sprintf(buf, "aat2870 registers\n");
+ for (addr = 0; addr < AAT2870_REG_NUM; addr++) {
+ count += sprintf(buf + count, "0x%02x: ", addr);
+ if (count >= PAGE_SIZE - 1)
+ break;
+
+ ret = aat2870->read(aat2870, addr, &val);
+ if (ret == 0)
+ count += snprintf(buf + count, PAGE_SIZE - count,
+ "0x%02x", val);
+ else
+ count += snprintf(buf + count, PAGE_SIZE - count,
+ "<read fail: %d>", ret);
+
+ if (count >= PAGE_SIZE - 1)
+ break;
+
+ count += snprintf(buf + count, PAGE_SIZE - count, "\n");
+ if (count >= PAGE_SIZE - 1)
+ break;
+ }
+
+ /* Truncate count; min() would cause a warning */
+ if (count >= PAGE_SIZE)
+ count = PAGE_SIZE - 1;
+
+ return count;
+}
+
+static int aat2870_reg_open_file(struct inode *inode, struct file *file)
+{
+ file->private_data = inode->i_private;
+
+ return 0;
+}
+
+static ssize_t aat2870_reg_read_file(struct file *file, char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ struct aat2870_data *aat2870 = file->private_data;
+ char *buf;
+ ssize_t ret;
+
+ buf = kmalloc(PAGE_SIZE, GFP_KERNEL);
+ if (!buf)
+ return -ENOMEM;
+
+ ret = aat2870_dump_reg(aat2870, buf);
+ if (ret >= 0)
+ ret = simple_read_from_buffer(user_buf, count, ppos, buf, ret);
+
+ kfree(buf);
+
+ return ret;
+}
+
+static ssize_t aat2870_reg_write_file(struct file *file,
+ const char __user *user_buf, size_t count,
+ loff_t *ppos)
+{
+ struct aat2870_data *aat2870 = file->private_data;
+ char buf[32];
+ int buf_size;
+ char *start = buf;
+ unsigned long addr, val;
+ int ret;
+
+ buf_size = min(count, (sizeof(buf)-1));
+ if (copy_from_user(buf, user_buf, buf_size)) {
+ dev_err(aat2870->dev, "Failed to copy from user\n");
+ return -EFAULT;
+ }
+ buf[buf_size] = 0;
+
+ while (*start == ' ')
+ start++;
+
+ addr = simple_strtoul(start, &start, 16);
+ if (addr >= AAT2870_REG_NUM) {
+ dev_err(aat2870->dev, "Invalid address, 0x%lx\n", addr);
+ return -EINVAL;
+ }
+
+ while (*start == ' ')
+ start++;
+
+ if (strict_strtoul(start, 16, &val))
+ return -EINVAL;
+
+ ret = aat2870->write(aat2870, (u8)addr, (u8)val);
+ if (ret)
+ return ret;
+
+ return buf_size;
+}
+
+static const struct file_operations aat2870_reg_fops = {
+ .open = aat2870_reg_open_file,
+ .read = aat2870_reg_read_file,
+ .write = aat2870_reg_write_file,
+};
+
+static void aat2870_init_debugfs(struct aat2870_data *aat2870)
+{
+ aat2870->dentry_root = debugfs_create_dir("aat2870", NULL);
+ if (!aat2870->dentry_root) {
+ dev_warn(aat2870->dev,
+ "Failed to create debugfs root directory\n");
+ return;
+ }
+
+ aat2870->dentry_reg = debugfs_create_file("regs", 0644,
+ aat2870->dentry_root,
+ aat2870, &aat2870_reg_fops);
+ if (!aat2870->dentry_reg)
+ dev_warn(aat2870->dev,
+ "Failed to create debugfs register file\n");
+}
+
+static void aat2870_uninit_debugfs(struct aat2870_data *aat2870)
+{
+ debugfs_remove_recursive(aat2870->dentry_root);
+}
+#else
+static inline void aat2870_init_debugfs(struct aat2870_data *aat2870)
+{
+}
+
+static inline void aat2870_uninit_debugfs(struct aat2870_data *aat2870)
+{
+}
+#endif /* CONFIG_DEBUG_FS */
+
+static int aat2870_i2c_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct aat2870_platform_data *pdata = client->dev.platform_data;
+ struct aat2870_data *aat2870;
+ int i, j;
+ int ret = 0;
+
+ aat2870 = kzalloc(sizeof(struct aat2870_data), GFP_KERNEL);
+ if (!aat2870) {
+ dev_err(&client->dev,
+ "Failed to allocate memory for aat2870\n");
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ aat2870->dev = &client->dev;
+ dev_set_drvdata(aat2870->dev, aat2870);
+
+ aat2870->client = client;
+ i2c_set_clientdata(client, aat2870);
+
+ aat2870->reg_cache = aat2870_regs;
+
+ if (pdata->en_pin < 0)
+ aat2870->en_pin = -1;
+ else
+ aat2870->en_pin = pdata->en_pin;
+
+ aat2870->init = pdata->init;
+ aat2870->uninit = pdata->uninit;
+ aat2870->read = aat2870_read;
+ aat2870->write = aat2870_write;
+ aat2870->update = aat2870_update;
+
+ mutex_init(&aat2870->io_lock);
+
+ if (aat2870->init)
+ aat2870->init(aat2870);
+
+ if (aat2870->en_pin >= 0) {
+ ret = gpio_request(aat2870->en_pin, "aat2870-en");
+ if (ret < 0) {
+ dev_err(&client->dev,
+ "Failed to request GPIO %d\n", aat2870->en_pin);
+ goto out_kfree;
+ }
+ gpio_direction_output(aat2870->en_pin, 1);
+ }
+
+ aat2870_enable(aat2870);
+
+ for (i = 0; i < pdata->num_subdevs; i++) {
+ for (j = 0; j < ARRAY_SIZE(aat2870_devs); j++) {
+ if ((pdata->subdevs[i].id == aat2870_devs[j].id) &&
+ !strcmp(pdata->subdevs[i].name,
+ aat2870_devs[j].name)) {
+ aat2870_devs[j].platform_data =
+ pdata->subdevs[i].platform_data;
+ break;
+ }
+ }
+ }
+
+ ret = mfd_add_devices(aat2870->dev, 0, aat2870_devs,
+ ARRAY_SIZE(aat2870_devs), NULL, 0);
+ if (ret != 0) {
+ dev_err(aat2870->dev, "Failed to add subdev: %d\n", ret);
+ goto out_disable;
+ }
+
+ aat2870_init_debugfs(aat2870);
+
+ return 0;
+
+out_disable:
+ aat2870_disable(aat2870);
+ if (aat2870->en_pin >= 0)
+ gpio_free(aat2870->en_pin);
+out_kfree:
+ kfree(aat2870);
+out:
+ return ret;
+}
+
+static int aat2870_i2c_remove(struct i2c_client *client)
+{
+ struct aat2870_data *aat2870 = i2c_get_clientdata(client);
+
+ aat2870_uninit_debugfs(aat2870);
+
+ mfd_remove_devices(aat2870->dev);
+ aat2870_disable(aat2870);
+ if (aat2870->en_pin >= 0)
+ gpio_free(aat2870->en_pin);
+ if (aat2870->uninit)
+ aat2870->uninit(aat2870);
+ kfree(aat2870);
+
+ return 0;
+}
+
+#ifdef CONFIG_PM
+static int aat2870_i2c_suspend(struct i2c_client *client, pm_message_t state)
+{
+ struct aat2870_data *aat2870 = i2c_get_clientdata(client);
+
+ aat2870_disable(aat2870);
+
+ return 0;
+}
+
+static int aat2870_i2c_resume(struct i2c_client *client)
+{
+ struct aat2870_data *aat2870 = i2c_get_clientdata(client);
+ struct aat2870_register *reg = NULL;
+ int i;
+
+ aat2870_enable(aat2870);
+
+ /* restore registers */
+ for (i = 0; i < AAT2870_REG_NUM; i++) {
+ reg = &aat2870->reg_cache[i];
+ if (reg->writeable)
+ aat2870->write(aat2870, i, reg->value);
+ }
+
+ return 0;
+}
+#else
+#define aat2870_i2c_suspend NULL
+#define aat2870_i2c_resume NULL
+#endif /* CONFIG_PM */
+
+static struct i2c_device_id aat2870_i2c_id_table[] = {
+ { "aat2870", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, aat2870_i2c_id_table);
+
+static struct i2c_driver aat2870_i2c_driver = {
+ .driver = {
+ .name = "aat2870",
+ .owner = THIS_MODULE,
+ },
+ .probe = aat2870_i2c_probe,
+ .remove = aat2870_i2c_remove,
+ .suspend = aat2870_i2c_suspend,
+ .resume = aat2870_i2c_resume,
+ .id_table = aat2870_i2c_id_table,
+};
+
+static int __init aat2870_init(void)
+{
+ return i2c_add_driver(&aat2870_i2c_driver);
+}
+subsys_initcall(aat2870_init);
+
+static void __exit aat2870_exit(void)
+{
+ i2c_del_driver(&aat2870_i2c_driver);
+}
+module_exit(aat2870_exit);
+
+MODULE_DESCRIPTION("Core support for the AnalogicTech AAT2870");
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Jin Park <jinyoungp@nvidia.com>");
diff --git a/drivers/mfd/ab3550-core.c b/drivers/mfd/ab3550-core.c
index 3d7dce6..56ba194 100644
--- a/drivers/mfd/ab3550-core.c
+++ b/drivers/mfd/ab3550-core.c
@@ -879,20 +879,13 @@
size_t count, loff_t *ppos)
{
struct ab3550 *ab = ((struct seq_file *)(file->private_data))->private;
- char buf[32];
- int buf_size;
unsigned long user_bank;
int err;
/* Get userspace string and assure termination */
- buf_size = min(count, (sizeof(buf) - 1));
- if (copy_from_user(buf, user_buf, buf_size))
- return -EFAULT;
- buf[buf_size] = 0;
-
- err = strict_strtoul(buf, 0, &user_bank);
+ err = kstrtoul_from_user(user_buf, count, 0, &user_bank);
if (err)
- return -EINVAL;
+ return err;
if (user_bank >= AB3550_NUM_BANKS) {
dev_err(&ab->i2c_client[0]->dev,
@@ -902,7 +895,7 @@
ab->debug_bank = user_bank;
- return buf_size;
+ return count;
}
static int ab3550_address_print(struct seq_file *s, void *p)
@@ -923,27 +916,21 @@
size_t count, loff_t *ppos)
{
struct ab3550 *ab = ((struct seq_file *)(file->private_data))->private;
- char buf[32];
- int buf_size;
unsigned long user_address;
int err;
/* Get userspace string and assure termination */
- buf_size = min(count, (sizeof(buf) - 1));
- if (copy_from_user(buf, user_buf, buf_size))
- return -EFAULT;
- buf[buf_size] = 0;
-
- err = strict_strtoul(buf, 0, &user_address);
+ err = kstrtoul_from_user(user_buf, count, 0, &user_address);
if (err)
- return -EINVAL;
+ return err;
+
if (user_address > 0xff) {
dev_err(&ab->i2c_client[0]->dev,
"debugfs error input > 0xff\n");
return -EINVAL;
}
ab->debug_address = user_address;
- return buf_size;
+ return count;
}
static int ab3550_val_print(struct seq_file *s, void *p)
@@ -971,21 +958,15 @@
size_t count, loff_t *ppos)
{
struct ab3550 *ab = ((struct seq_file *)(file->private_data))->private;
- char buf[32];
- int buf_size;
unsigned long user_val;
int err;
u8 regvalue;
/* Get userspace string and assure termination */
- buf_size = min(count, (sizeof(buf)-1));
- if (copy_from_user(buf, user_buf, buf_size))
- return -EFAULT;
- buf[buf_size] = 0;
-
- err = strict_strtoul(buf, 0, &user_val);
+ err = kstrtoul_from_user(user_buf, count, 0, &user_val);
if (err)
- return -EINVAL;
+ return err;
+
if (user_val > 0xff) {
dev_err(&ab->i2c_client[0]->dev,
"debugfs error input > 0xff\n");
@@ -1002,7 +983,7 @@
if (err)
return -EINVAL;
- return buf_size;
+ return count;
}
static const struct file_operations ab3550_bank_fops = {
diff --git a/drivers/mfd/ab8500-core.c b/drivers/mfd/ab8500-core.c
index fc0c1af..387705e 100644
--- a/drivers/mfd/ab8500-core.c
+++ b/drivers/mfd/ab8500-core.c
@@ -363,7 +363,7 @@
}
}
-static struct resource ab8500_gpio_resources[] = {
+static struct resource __devinitdata ab8500_gpio_resources[] = {
{
.name = "GPIO_INT6",
.start = AB8500_INT_GPIO6R,
@@ -372,7 +372,7 @@
}
};
-static struct resource ab8500_gpadc_resources[] = {
+static struct resource __devinitdata ab8500_gpadc_resources[] = {
{
.name = "HW_CONV_END",
.start = AB8500_INT_GP_HW_ADC_CONV_END,
@@ -387,7 +387,7 @@
},
};
-static struct resource ab8500_rtc_resources[] = {
+static struct resource __devinitdata ab8500_rtc_resources[] = {
{
.name = "60S",
.start = AB8500_INT_RTC_60S,
@@ -402,7 +402,7 @@
},
};
-static struct resource ab8500_poweronkey_db_resources[] = {
+static struct resource __devinitdata ab8500_poweronkey_db_resources[] = {
{
.name = "ONKEY_DBF",
.start = AB8500_INT_PON_KEY1DB_F,
@@ -417,20 +417,47 @@
},
};
-static struct resource ab8500_bm_resources[] = {
+static struct resource __devinitdata ab8500_av_acc_detect_resources[] = {
{
- .name = "MAIN_EXT_CH_NOT_OK",
- .start = AB8500_INT_MAIN_EXT_CH_NOT_OK,
- .end = AB8500_INT_MAIN_EXT_CH_NOT_OK,
- .flags = IORESOURCE_IRQ,
+ .name = "ACC_DETECT_1DB_F",
+ .start = AB8500_INT_ACC_DETECT_1DB_F,
+ .end = AB8500_INT_ACC_DETECT_1DB_F,
+ .flags = IORESOURCE_IRQ,
},
{
- .name = "BATT_OVV",
- .start = AB8500_INT_BATT_OVV,
- .end = AB8500_INT_BATT_OVV,
- .flags = IORESOURCE_IRQ,
+ .name = "ACC_DETECT_1DB_R",
+ .start = AB8500_INT_ACC_DETECT_1DB_R,
+ .end = AB8500_INT_ACC_DETECT_1DB_R,
+ .flags = IORESOURCE_IRQ,
},
{
+ .name = "ACC_DETECT_21DB_F",
+ .start = AB8500_INT_ACC_DETECT_21DB_F,
+ .end = AB8500_INT_ACC_DETECT_21DB_F,
+ .flags = IORESOURCE_IRQ,
+ },
+ {
+ .name = "ACC_DETECT_21DB_R",
+ .start = AB8500_INT_ACC_DETECT_21DB_R,
+ .end = AB8500_INT_ACC_DETECT_21DB_R,
+ .flags = IORESOURCE_IRQ,
+ },
+ {
+ .name = "ACC_DETECT_22DB_F",
+ .start = AB8500_INT_ACC_DETECT_22DB_F,
+ .end = AB8500_INT_ACC_DETECT_22DB_F,
+ .flags = IORESOURCE_IRQ,
+ },
+ {
+ .name = "ACC_DETECT_22DB_R",
+ .start = AB8500_INT_ACC_DETECT_22DB_R,
+ .end = AB8500_INT_ACC_DETECT_22DB_R,
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+static struct resource __devinitdata ab8500_charger_resources[] = {
+ {
.name = "MAIN_CH_UNPLUG_DET",
.start = AB8500_INT_MAIN_CH_UNPLUG_DET,
.end = AB8500_INT_MAIN_CH_UNPLUG_DET,
@@ -443,27 +470,27 @@
.flags = IORESOURCE_IRQ,
},
{
- .name = "VBUS_DET_F",
- .start = AB8500_INT_VBUS_DET_F,
- .end = AB8500_INT_VBUS_DET_F,
- .flags = IORESOURCE_IRQ,
- },
- {
.name = "VBUS_DET_R",
.start = AB8500_INT_VBUS_DET_R,
.end = AB8500_INT_VBUS_DET_R,
.flags = IORESOURCE_IRQ,
},
{
- .name = "BAT_CTRL_INDB",
- .start = AB8500_INT_BAT_CTRL_INDB,
- .end = AB8500_INT_BAT_CTRL_INDB,
+ .name = "VBUS_DET_F",
+ .start = AB8500_INT_VBUS_DET_F,
+ .end = AB8500_INT_VBUS_DET_F,
.flags = IORESOURCE_IRQ,
},
{
- .name = "CH_WD_EXP",
- .start = AB8500_INT_CH_WD_EXP,
- .end = AB8500_INT_CH_WD_EXP,
+ .name = "USB_LINK_STATUS",
+ .start = AB8500_INT_USB_LINK_STATUS,
+ .end = AB8500_INT_USB_LINK_STATUS,
+ .flags = IORESOURCE_IRQ,
+ },
+ {
+ .name = "USB_CHARGE_DET_DONE",
+ .start = AB8500_INT_USB_CHG_DET_DONE,
+ .end = AB8500_INT_USB_CHG_DET_DONE,
.flags = IORESOURCE_IRQ,
},
{
@@ -473,21 +500,60 @@
.flags = IORESOURCE_IRQ,
},
{
- .name = "NCONV_ACCU",
- .start = AB8500_INT_CCN_CONV_ACC,
- .end = AB8500_INT_CCN_CONV_ACC,
+ .name = "USB_CH_TH_PROT_R",
+ .start = AB8500_INT_USB_CH_TH_PROT_R,
+ .end = AB8500_INT_USB_CH_TH_PROT_R,
.flags = IORESOURCE_IRQ,
},
{
- .name = "LOW_BAT_F",
- .start = AB8500_INT_LOW_BAT_F,
- .end = AB8500_INT_LOW_BAT_F,
+ .name = "USB_CH_TH_PROT_F",
+ .start = AB8500_INT_USB_CH_TH_PROT_F,
+ .end = AB8500_INT_USB_CH_TH_PROT_F,
.flags = IORESOURCE_IRQ,
},
{
- .name = "LOW_BAT_R",
- .start = AB8500_INT_LOW_BAT_R,
- .end = AB8500_INT_LOW_BAT_R,
+ .name = "MAIN_EXT_CH_NOT_OK",
+ .start = AB8500_INT_MAIN_EXT_CH_NOT_OK,
+ .end = AB8500_INT_MAIN_EXT_CH_NOT_OK,
+ .flags = IORESOURCE_IRQ,
+ },
+ {
+ .name = "MAIN_CH_TH_PROT_R",
+ .start = AB8500_INT_MAIN_CH_TH_PROT_R,
+ .end = AB8500_INT_MAIN_CH_TH_PROT_R,
+ .flags = IORESOURCE_IRQ,
+ },
+ {
+ .name = "MAIN_CH_TH_PROT_F",
+ .start = AB8500_INT_MAIN_CH_TH_PROT_F,
+ .end = AB8500_INT_MAIN_CH_TH_PROT_F,
+ .flags = IORESOURCE_IRQ,
+ },
+ {
+ .name = "USB_CHARGER_NOT_OKR",
+ .start = AB8500_INT_USB_CHARGER_NOT_OK,
+ .end = AB8500_INT_USB_CHARGER_NOT_OK,
+ .flags = IORESOURCE_IRQ,
+ },
+ {
+ .name = "USB_CHARGER_NOT_OKF",
+ .start = AB8500_INT_USB_CHARGER_NOT_OKF,
+ .end = AB8500_INT_USB_CHARGER_NOT_OKF,
+ .flags = IORESOURCE_IRQ,
+ },
+ {
+ .name = "CH_WD_EXP",
+ .start = AB8500_INT_CH_WD_EXP,
+ .end = AB8500_INT_CH_WD_EXP,
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+static struct resource __devinitdata ab8500_btemp_resources[] = {
+ {
+ .name = "BAT_CTRL_INDB",
+ .start = AB8500_INT_BAT_CTRL_INDB,
+ .end = AB8500_INT_BAT_CTRL_INDB,
.flags = IORESOURCE_IRQ,
},
{
@@ -503,38 +569,55 @@
.flags = IORESOURCE_IRQ,
},
{
- .name = "USB_CHARGER_NOT_OKR",
- .start = AB8500_INT_USB_CHARGER_NOT_OK,
- .end = AB8500_INT_USB_CHARGER_NOT_OK,
+ .name = "BTEMP_LOW_MEDIUM",
+ .start = AB8500_INT_BTEMP_LOW_MEDIUM,
+ .end = AB8500_INT_BTEMP_LOW_MEDIUM,
.flags = IORESOURCE_IRQ,
},
{
- .name = "USB_CHARGE_DET_DONE",
- .start = AB8500_INT_USB_CHG_DET_DONE,
- .end = AB8500_INT_USB_CHG_DET_DONE,
- .flags = IORESOURCE_IRQ,
- },
- {
- .name = "USB_CH_TH_PROT_R",
- .start = AB8500_INT_USB_CH_TH_PROT_R,
- .end = AB8500_INT_USB_CH_TH_PROT_R,
- .flags = IORESOURCE_IRQ,
- },
- {
- .name = "MAIN_CH_TH_PROT_R",
- .start = AB8500_INT_MAIN_CH_TH_PROT_R,
- .end = AB8500_INT_MAIN_CH_TH_PROT_R,
- .flags = IORESOURCE_IRQ,
- },
- {
- .name = "USB_CHARGER_NOT_OKF",
- .start = AB8500_INT_USB_CHARGER_NOT_OKF,
- .end = AB8500_INT_USB_CHARGER_NOT_OKF,
+ .name = "BTEMP_MEDIUM_HIGH",
+ .start = AB8500_INT_BTEMP_MEDIUM_HIGH,
+ .end = AB8500_INT_BTEMP_MEDIUM_HIGH,
.flags = IORESOURCE_IRQ,
},
};
-static struct resource ab8500_debug_resources[] = {
+static struct resource __devinitdata ab8500_fg_resources[] = {
+ {
+ .name = "NCONV_ACCU",
+ .start = AB8500_INT_CCN_CONV_ACC,
+ .end = AB8500_INT_CCN_CONV_ACC,
+ .flags = IORESOURCE_IRQ,
+ },
+ {
+ .name = "BATT_OVV",
+ .start = AB8500_INT_BATT_OVV,
+ .end = AB8500_INT_BATT_OVV,
+ .flags = IORESOURCE_IRQ,
+ },
+ {
+ .name = "LOW_BAT_F",
+ .start = AB8500_INT_LOW_BAT_F,
+ .end = AB8500_INT_LOW_BAT_F,
+ .flags = IORESOURCE_IRQ,
+ },
+ {
+ .name = "LOW_BAT_R",
+ .start = AB8500_INT_LOW_BAT_R,
+ .end = AB8500_INT_LOW_BAT_R,
+ .flags = IORESOURCE_IRQ,
+ },
+ {
+ .name = "CC_INT_CALIB",
+ .start = AB8500_INT_CC_INT_CALIB,
+ .end = AB8500_INT_CC_INT_CALIB,
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+static struct resource __devinitdata ab8500_chargalg_resources[] = {};
+
+static struct resource __devinitdata ab8500_debug_resources[] = {
{
.name = "IRQ_FIRST",
.start = AB8500_INT_MAIN_EXT_CH_NOT_OK,
@@ -549,7 +632,7 @@
},
};
-static struct resource ab8500_usb_resources[] = {
+static struct resource __devinitdata ab8500_usb_resources[] = {
{
.name = "ID_WAKEUP_R",
.start = AB8500_INT_ID_WAKEUP_R,
@@ -580,9 +663,21 @@
.end = AB8500_INT_USB_LINK_STATUS,
.flags = IORESOURCE_IRQ,
},
+ {
+ .name = "USB_ADP_PROBE_PLUG",
+ .start = AB8500_INT_ADP_PROBE_PLUG,
+ .end = AB8500_INT_ADP_PROBE_PLUG,
+ .flags = IORESOURCE_IRQ,
+ },
+ {
+ .name = "USB_ADP_PROBE_UNPLUG",
+ .start = AB8500_INT_ADP_PROBE_UNPLUG,
+ .end = AB8500_INT_ADP_PROBE_UNPLUG,
+ .flags = IORESOURCE_IRQ,
+ },
};
-static struct resource ab8500_temp_resources[] = {
+static struct resource __devinitdata ab8500_temp_resources[] = {
{
.name = "AB8500_TEMP_WARM",
.start = AB8500_INT_TEMP_WARM,
@@ -591,7 +686,7 @@
},
};
-static struct mfd_cell ab8500_devs[] = {
+static struct mfd_cell __devinitdata ab8500_devs[] = {
#ifdef CONFIG_DEBUG_FS
{
.name = "ab8500-debug",
@@ -621,11 +716,33 @@
.resources = ab8500_rtc_resources,
},
{
- .name = "ab8500-bm",
- .num_resources = ARRAY_SIZE(ab8500_bm_resources),
- .resources = ab8500_bm_resources,
+ .name = "ab8500-charger",
+ .num_resources = ARRAY_SIZE(ab8500_charger_resources),
+ .resources = ab8500_charger_resources,
},
- { .name = "ab8500-codec", },
+ {
+ .name = "ab8500-btemp",
+ .num_resources = ARRAY_SIZE(ab8500_btemp_resources),
+ .resources = ab8500_btemp_resources,
+ },
+ {
+ .name = "ab8500-fg",
+ .num_resources = ARRAY_SIZE(ab8500_fg_resources),
+ .resources = ab8500_fg_resources,
+ },
+ {
+ .name = "ab8500-chargalg",
+ .num_resources = ARRAY_SIZE(ab8500_chargalg_resources),
+ .resources = ab8500_chargalg_resources,
+ },
+ {
+ .name = "ab8500-acc-det",
+ .num_resources = ARRAY_SIZE(ab8500_av_acc_detect_resources),
+ .resources = ab8500_av_acc_detect_resources,
+ },
+ {
+ .name = "ab8500-codec",
+ },
{
.name = "ab8500-usb",
.num_resources = ARRAY_SIZE(ab8500_usb_resources),
diff --git a/drivers/mfd/ab8500-debugfs.c b/drivers/mfd/ab8500-debugfs.c
index 64748e4..64bdeeb 100644
--- a/drivers/mfd/ab8500-debugfs.c
+++ b/drivers/mfd/ab8500-debugfs.c
@@ -419,20 +419,13 @@
size_t count, loff_t *ppos)
{
struct device *dev = ((struct seq_file *)(file->private_data))->private;
- char buf[32];
- int buf_size;
unsigned long user_bank;
int err;
/* Get userspace string and assure termination */
- buf_size = min(count, (sizeof(buf) - 1));
- if (copy_from_user(buf, user_buf, buf_size))
- return -EFAULT;
- buf[buf_size] = 0;
-
- err = strict_strtoul(buf, 0, &user_bank);
+ err = kstrtoul_from_user(user_buf, count, 0, &user_bank);
if (err)
- return -EINVAL;
+ return err;
if (user_bank >= AB8500_NUM_BANKS) {
dev_err(dev, "debugfs error input > number of banks\n");
@@ -441,7 +434,7 @@
debug_bank = user_bank;
- return buf_size;
+ return count;
}
static int ab8500_address_print(struct seq_file *s, void *p)
@@ -459,26 +452,20 @@
size_t count, loff_t *ppos)
{
struct device *dev = ((struct seq_file *)(file->private_data))->private;
- char buf[32];
- int buf_size;
unsigned long user_address;
int err;
/* Get userspace string and assure termination */
- buf_size = min(count, (sizeof(buf) - 1));
- if (copy_from_user(buf, user_buf, buf_size))
- return -EFAULT;
- buf[buf_size] = 0;
-
- err = strict_strtoul(buf, 0, &user_address);
+ err = kstrtoul_from_user(user_buf, count, 0, &user_address);
if (err)
- return -EINVAL;
+ return err;
+
if (user_address > 0xff) {
dev_err(dev, "debugfs error input > 0xff\n");
return -EINVAL;
}
debug_address = user_address;
- return buf_size;
+ return count;
}
static int ab8500_val_print(struct seq_file *s, void *p)
@@ -509,20 +496,14 @@
size_t count, loff_t *ppos)
{
struct device *dev = ((struct seq_file *)(file->private_data))->private;
- char buf[32];
- int buf_size;
unsigned long user_val;
int err;
/* Get userspace string and assure termination */
- buf_size = min(count, (sizeof(buf)-1));
- if (copy_from_user(buf, user_buf, buf_size))
- return -EFAULT;
- buf[buf_size] = 0;
-
- err = strict_strtoul(buf, 0, &user_val);
+ err = kstrtoul_from_user(user_buf, count, 0, &user_val);
if (err)
- return -EINVAL;
+ return err;
+
if (user_val > 0xff) {
dev_err(dev, "debugfs error input > 0xff\n");
return -EINVAL;
@@ -534,7 +515,7 @@
return -EINVAL;
}
- return buf_size;
+ return count;
}
static const struct file_operations ab8500_bank_fops = {
diff --git a/drivers/mfd/jz4740-adc.c b/drivers/mfd/jz4740-adc.c
index a0bd0cf..21131c7 100644
--- a/drivers/mfd/jz4740-adc.c
+++ b/drivers/mfd/jz4740-adc.c
@@ -56,7 +56,7 @@
void __iomem *base;
int irq;
- int irq_base;
+ struct irq_chip_generic *gc;
struct clk *clk;
atomic_t clk_ref;
@@ -64,63 +64,17 @@
spinlock_t lock;
};
-static inline void jz4740_adc_irq_set_masked(struct jz4740_adc *adc, int irq,
- bool masked)
-{
- unsigned long flags;
- uint8_t val;
-
- irq -= adc->irq_base;
-
- spin_lock_irqsave(&adc->lock, flags);
-
- val = readb(adc->base + JZ_REG_ADC_CTRL);
- if (masked)
- val |= BIT(irq);
- else
- val &= ~BIT(irq);
- writeb(val, adc->base + JZ_REG_ADC_CTRL);
-
- spin_unlock_irqrestore(&adc->lock, flags);
-}
-
-static void jz4740_adc_irq_mask(struct irq_data *data)
-{
- struct jz4740_adc *adc = irq_data_get_irq_chip_data(data);
- jz4740_adc_irq_set_masked(adc, data->irq, true);
-}
-
-static void jz4740_adc_irq_unmask(struct irq_data *data)
-{
- struct jz4740_adc *adc = irq_data_get_irq_chip_data(data);
- jz4740_adc_irq_set_masked(adc, data->irq, false);
-}
-
-static void jz4740_adc_irq_ack(struct irq_data *data)
-{
- struct jz4740_adc *adc = irq_data_get_irq_chip_data(data);
- unsigned int irq = data->irq - adc->irq_base;
- writeb(BIT(irq), adc->base + JZ_REG_ADC_STATUS);
-}
-
-static struct irq_chip jz4740_adc_irq_chip = {
- .name = "jz4740-adc",
- .irq_mask = jz4740_adc_irq_mask,
- .irq_unmask = jz4740_adc_irq_unmask,
- .irq_ack = jz4740_adc_irq_ack,
-};
-
static void jz4740_adc_irq_demux(unsigned int irq, struct irq_desc *desc)
{
- struct jz4740_adc *adc = irq_desc_get_handler_data(desc);
+ struct irq_chip_generic *gc = irq_desc_get_handler_data(desc);
uint8_t status;
unsigned int i;
- status = readb(adc->base + JZ_REG_ADC_STATUS);
+ status = readb(gc->reg_base + JZ_REG_ADC_STATUS);
for (i = 0; i < 5; ++i) {
if (status & BIT(i))
- generic_handle_irq(adc->irq_base + i);
+ generic_handle_irq(gc->irq_base + i);
}
}
@@ -249,10 +203,12 @@
static int __devinit jz4740_adc_probe(struct platform_device *pdev)
{
- int ret;
+ struct irq_chip_generic *gc;
+ struct irq_chip_type *ct;
struct jz4740_adc *adc;
struct resource *mem_base;
- int irq;
+ int ret;
+ int irq_base;
adc = kmalloc(sizeof(*adc), GFP_KERNEL);
if (!adc) {
@@ -267,9 +223,9 @@
goto err_free;
}
- adc->irq_base = platform_get_irq(pdev, 1);
- if (adc->irq_base < 0) {
- ret = adc->irq_base;
+ irq_base = platform_get_irq(pdev, 1);
+ if (irq_base < 0) {
+ ret = irq_base;
dev_err(&pdev->dev, "Failed to get irq base: %d\n", ret);
goto err_free;
}
@@ -309,20 +265,28 @@
platform_set_drvdata(pdev, adc);
- for (irq = adc->irq_base; irq < adc->irq_base + 5; ++irq) {
- irq_set_chip_data(irq, adc);
- irq_set_chip_and_handler(irq, &jz4740_adc_irq_chip,
- handle_level_irq);
- }
+ gc = irq_alloc_generic_chip("INTC", 1, irq_base, adc->base,
+ handle_level_irq);
- irq_set_handler_data(adc->irq, adc);
+ ct = gc->chip_types;
+ ct->regs.mask = JZ_REG_ADC_CTRL;
+ ct->regs.ack = JZ_REG_ADC_STATUS;
+ ct->chip.irq_mask = irq_gc_mask_set_bit;
+ ct->chip.irq_unmask = irq_gc_mask_clr_bit;
+ ct->chip.irq_ack = irq_gc_ack;
+
+ irq_setup_generic_chip(gc, IRQ_MSK(5), 0, 0, IRQ_NOPROBE | IRQ_LEVEL);
+
+ adc->gc = gc;
+
+ irq_set_handler_data(adc->irq, gc);
irq_set_chained_handler(adc->irq, jz4740_adc_irq_demux);
writeb(0x00, adc->base + JZ_REG_ADC_ENABLE);
writeb(0xff, adc->base + JZ_REG_ADC_CTRL);
ret = mfd_add_devices(&pdev->dev, 0, jz4740_adc_cells,
- ARRAY_SIZE(jz4740_adc_cells), mem_base, adc->irq_base);
+ ARRAY_SIZE(jz4740_adc_cells), mem_base, irq_base);
if (ret < 0)
goto err_clk_put;
@@ -347,6 +311,8 @@
mfd_remove_devices(&pdev->dev);
+ irq_remove_generic_chip(adc->gc, IRQ_MSK(5), IRQ_NOPROBE | IRQ_LEVEL, 0);
+ kfree(adc->gc);
irq_set_handler_data(adc->irq, NULL);
irq_set_chained_handler(adc->irq, NULL);
diff --git a/drivers/mfd/lpc_sch.c b/drivers/mfd/lpc_sch.c
index ea3f52c..ea1169b 100644
--- a/drivers/mfd/lpc_sch.c
+++ b/drivers/mfd/lpc_sch.c
@@ -37,6 +37,9 @@
#define GPIOBASE 0x44
#define GPIO_IO_SIZE 64
+#define WDTBASE 0x84
+#define WDT_IO_SIZE 64
+
static struct resource smbus_sch_resource = {
.flags = IORESOURCE_IO,
};
@@ -59,6 +62,18 @@
},
};
+static struct resource wdt_sch_resource = {
+ .flags = IORESOURCE_IO,
+};
+
+static struct mfd_cell tunnelcreek_cells[] = {
+ {
+ .name = "tunnelcreek_wdt",
+ .num_resources = 1,
+ .resources = &wdt_sch_resource,
+ },
+};
+
static struct pci_device_id lpc_sch_ids[] = {
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_SCH_LPC) },
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ITC_LPC) },
@@ -72,6 +87,7 @@
unsigned int base_addr_cfg;
unsigned short base_addr;
int i;
+ int ret;
pci_read_config_dword(dev, SMBASE, &base_addr_cfg);
if (!(base_addr_cfg & (1 << 31))) {
@@ -104,8 +120,39 @@
for (i=0; i < ARRAY_SIZE(lpc_sch_cells); i++)
lpc_sch_cells[i].id = id->device;
- return mfd_add_devices(&dev->dev, 0,
+ ret = mfd_add_devices(&dev->dev, 0,
lpc_sch_cells, ARRAY_SIZE(lpc_sch_cells), NULL, 0);
+ if (ret)
+ goto out_dev;
+
+ if (id->device == PCI_DEVICE_ID_INTEL_ITC_LPC) {
+ pci_read_config_dword(dev, WDTBASE, &base_addr_cfg);
+ if (!(base_addr_cfg & (1 << 31))) {
+ dev_err(&dev->dev, "Decode of the WDT I/O range disabled\n");
+ ret = -ENODEV;
+ goto out_dev;
+ }
+ base_addr = (unsigned short)base_addr_cfg;
+ if (base_addr == 0) {
+ dev_err(&dev->dev, "I/O space for WDT uninitialized\n");
+ ret = -ENODEV;
+ goto out_dev;
+ }
+
+ wdt_sch_resource.start = base_addr;
+ wdt_sch_resource.end = base_addr + WDT_IO_SIZE - 1;
+
+ for (i = 0; i < ARRAY_SIZE(tunnelcreek_cells); i++)
+ tunnelcreek_cells[i].id = id->device;
+
+ ret = mfd_add_devices(&dev->dev, 0, tunnelcreek_cells,
+ ARRAY_SIZE(tunnelcreek_cells), NULL, 0);
+ }
+
+ return ret;
+out_dev:
+ mfd_remove_devices(&dev->dev);
+ return ret;
}
static void __devexit lpc_sch_remove(struct pci_dev *dev)
diff --git a/drivers/mfd/max8997-irq.c b/drivers/mfd/max8997-irq.c
index 638bf7e..09274cf 100644
--- a/drivers/mfd/max8997-irq.c
+++ b/drivers/mfd/max8997-irq.c
@@ -58,8 +58,6 @@
default:
return ERR_PTR(-EINVAL);
}
-
- return ERR_PTR(-EINVAL);
}
struct max8997_irq_data {
diff --git a/drivers/mfd/omap-usb-host.c b/drivers/mfd/omap-usb-host.c
index 1717144..29601e7 100644
--- a/drivers/mfd/omap-usb-host.c
+++ b/drivers/mfd/omap-usb-host.c
@@ -998,9 +998,9 @@
if (is_omap_usbhs_rev2(omap)) {
if (is_ehci_tll_mode(pdata->port_mode[0]))
- clk_enable(omap->usbtll_p1_fck);
+ clk_disable(omap->usbtll_p1_fck);
if (is_ehci_tll_mode(pdata->port_mode[1]))
- clk_enable(omap->usbtll_p2_fck);
+ clk_disable(omap->usbtll_p2_fck);
clk_disable(omap->utmi_p2_fck);
clk_disable(omap->utmi_p1_fck);
}
diff --git a/drivers/mfd/stmpe.c b/drivers/mfd/stmpe.c
index 7ab7746..2963689c 100644
--- a/drivers/mfd/stmpe.c
+++ b/drivers/mfd/stmpe.c
@@ -228,7 +228,7 @@
EXPORT_SYMBOL_GPL(stmpe_block_write);
/**
- * stmpe_set_altfunc: set the alternate function for STMPE pins
+ * stmpe_set_altfunc()- set the alternate function for STMPE pins
* @stmpe: Device to configure
* @pins: Bitmask of pins to affect
* @block: block to enable alternate functions for
diff --git a/drivers/mfd/stmpe.h b/drivers/mfd/stmpe.h
index 0dbdc4e..e4ee3895 100644
--- a/drivers/mfd/stmpe.h
+++ b/drivers/mfd/stmpe.h
@@ -42,6 +42,7 @@
* @id_mask: bits valid in CHIPID register for comparison with id_val
* @num_gpios: number of GPIOS
* @af_bits: number of bits used to specify the alternate function
+ * @regs: variant specific registers.
* @blocks: list of blocks present on this device
* @num_blocks: number of blocks present on this device
* @num_irqs: number of internal IRQs available on this device
diff --git a/drivers/mfd/tps65910.c b/drivers/mfd/tps65910.c
index 2229e66..6f5b8cf 100644
--- a/drivers/mfd/tps65910.c
+++ b/drivers/mfd/tps65910.c
@@ -147,12 +147,11 @@
if (init_data == NULL)
return -ENOMEM;
- init_data->irq = pmic_plat_data->irq;
- init_data->irq_base = pmic_plat_data->irq;
-
tps65910 = kzalloc(sizeof(struct tps65910), GFP_KERNEL);
- if (tps65910 == NULL)
+ if (tps65910 == NULL) {
+ kfree(init_data);
return -ENOMEM;
+ }
i2c_set_clientdata(i2c, tps65910);
tps65910->dev = &i2c->dev;
@@ -168,17 +167,22 @@
if (ret < 0)
goto err;
+ init_data->irq = pmic_plat_data->irq;
+ init_data->irq_base = pmic_plat_data->irq;
+
tps65910_gpio_init(tps65910, pmic_plat_data->gpio_base);
ret = tps65910_irq_init(tps65910, init_data->irq, init_data);
if (ret < 0)
goto err;
+ kfree(init_data);
return ret;
err:
mfd_remove_devices(tps65910->dev);
kfree(tps65910);
+ kfree(init_data);
return ret;
}
@@ -187,6 +191,7 @@
struct tps65910 *tps65910 = i2c_get_clientdata(i2c);
mfd_remove_devices(tps65910->dev);
+ tps65910_irq_exit(tps65910);
kfree(tps65910);
return 0;
diff --git a/drivers/mfd/tps65911-comparator.c b/drivers/mfd/tps65911-comparator.c
index 283ac67..e7ff783 100644
--- a/drivers/mfd/tps65911-comparator.c
+++ b/drivers/mfd/tps65911-comparator.c
@@ -157,6 +157,8 @@
struct tps65910 *tps65910;
tps65910 = dev_get_drvdata(pdev->dev.parent);
+ device_remove_file(&pdev->dev, &dev_attr_comp2_threshold);
+ device_remove_file(&pdev->dev, &dev_attr_comp1_threshold);
return 0;
}
diff --git a/drivers/mfd/tps65912-core.c b/drivers/mfd/tps65912-core.c
new file mode 100644
index 0000000..955bc00
--- /dev/null
+++ b/drivers/mfd/tps65912-core.c
@@ -0,0 +1,177 @@
+/*
+ * tps65912-core.c -- TI TPS65912x
+ *
+ * Copyright 2011 Texas Instruments Inc.
+ *
+ * Author: Margarita Olaya Cabrera <magi@slimlogic.co.uk>
+ *
+ * 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 driver is based on wm8350 implementation.
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/gpio.h>
+#include <linux/mfd/core.h>
+#include <linux/mfd/tps65912.h>
+
+static struct mfd_cell tps65912s[] = {
+ {
+ .name = "tps65912-pmic",
+ },
+};
+
+int tps65912_set_bits(struct tps65912 *tps65912, u8 reg, u8 mask)
+{
+ u8 data;
+ int err;
+
+ mutex_lock(&tps65912->io_mutex);
+
+ err = tps65912->read(tps65912, reg, 1, &data);
+ if (err) {
+ dev_err(tps65912->dev, "Read from reg 0x%x failed\n", reg);
+ goto out;
+ }
+
+ data |= mask;
+ err = tps65912->write(tps65912, reg, 1, &data);
+ if (err)
+ dev_err(tps65912->dev, "Write to reg 0x%x failed\n", reg);
+
+out:
+ mutex_unlock(&tps65912->io_mutex);
+ return err;
+}
+EXPORT_SYMBOL_GPL(tps65912_set_bits);
+
+int tps65912_clear_bits(struct tps65912 *tps65912, u8 reg, u8 mask)
+{
+ u8 data;
+ int err;
+
+ mutex_lock(&tps65912->io_mutex);
+ err = tps65912->read(tps65912, reg, 1, &data);
+ if (err) {
+ dev_err(tps65912->dev, "Read from reg 0x%x failed\n", reg);
+ goto out;
+ }
+
+ data &= ~mask;
+ err = tps65912->write(tps65912, reg, 1, &data);
+ if (err)
+ dev_err(tps65912->dev, "Write to reg 0x%x failed\n", reg);
+
+out:
+ mutex_unlock(&tps65912->io_mutex);
+ return err;
+}
+EXPORT_SYMBOL_GPL(tps65912_clear_bits);
+
+static inline int tps65912_read(struct tps65912 *tps65912, u8 reg)
+{
+ u8 val;
+ int err;
+
+ err = tps65912->read(tps65912, reg, 1, &val);
+ if (err < 0)
+ return err;
+
+ return val;
+}
+
+static inline int tps65912_write(struct tps65912 *tps65912, u8 reg, u8 val)
+{
+ return tps65912->write(tps65912, reg, 1, &val);
+}
+
+int tps65912_reg_read(struct tps65912 *tps65912, u8 reg)
+{
+ int data;
+
+ mutex_lock(&tps65912->io_mutex);
+
+ data = tps65912_read(tps65912, reg);
+ if (data < 0)
+ dev_err(tps65912->dev, "Read from reg 0x%x failed\n", reg);
+
+ mutex_unlock(&tps65912->io_mutex);
+ return data;
+}
+EXPORT_SYMBOL_GPL(tps65912_reg_read);
+
+int tps65912_reg_write(struct tps65912 *tps65912, u8 reg, u8 val)
+{
+ int err;
+
+ mutex_lock(&tps65912->io_mutex);
+
+ err = tps65912_write(tps65912, reg, val);
+ if (err < 0)
+ dev_err(tps65912->dev, "Write for reg 0x%x failed\n", reg);
+
+ mutex_unlock(&tps65912->io_mutex);
+ return err;
+}
+EXPORT_SYMBOL_GPL(tps65912_reg_write);
+
+int tps65912_device_init(struct tps65912 *tps65912)
+{
+ struct tps65912_board *pmic_plat_data = tps65912->dev->platform_data;
+ struct tps65912_platform_data *init_data;
+ int ret, dcdc_avs, value;
+
+ init_data = kzalloc(sizeof(struct tps65912_platform_data), GFP_KERNEL);
+ if (init_data == NULL)
+ return -ENOMEM;
+
+ init_data->irq = pmic_plat_data->irq;
+ init_data->irq_base = pmic_plat_data->irq;
+
+ mutex_init(&tps65912->io_mutex);
+ dev_set_drvdata(tps65912->dev, tps65912);
+
+ dcdc_avs = (pmic_plat_data->is_dcdc1_avs << 0 |
+ pmic_plat_data->is_dcdc2_avs << 1 |
+ pmic_plat_data->is_dcdc3_avs << 2 |
+ pmic_plat_data->is_dcdc4_avs << 3);
+ if (dcdc_avs) {
+ tps65912->read(tps65912, TPS65912_I2C_SPI_CFG, 1, &value);
+ dcdc_avs |= value;
+ tps65912->write(tps65912, TPS65912_I2C_SPI_CFG, 1, &dcdc_avs);
+ }
+
+ ret = mfd_add_devices(tps65912->dev, -1,
+ tps65912s, ARRAY_SIZE(tps65912s),
+ NULL, 0);
+ if (ret < 0)
+ goto err;
+
+ ret = tps65912_irq_init(tps65912, init_data->irq, init_data);
+ if (ret < 0)
+ goto err;
+
+ return ret;
+
+err:
+ kfree(init_data);
+ mfd_remove_devices(tps65912->dev);
+ kfree(tps65912);
+ return ret;
+}
+
+void tps65912_device_exit(struct tps65912 *tps65912)
+{
+ mfd_remove_devices(tps65912->dev);
+ kfree(tps65912);
+}
+
+MODULE_AUTHOR("Margarita Olaya <magi@slimlogic.co.uk>");
+MODULE_DESCRIPTION("TPS65912x chip family multi-function driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/mfd/tps65912-i2c.c b/drivers/mfd/tps65912-i2c.c
new file mode 100644
index 0000000..c041f2c
--- /dev/null
+++ b/drivers/mfd/tps65912-i2c.c
@@ -0,0 +1,139 @@
+/*
+ * tps65912-i2c.c -- I2C access for TI TPS65912x PMIC
+ *
+ * Copyright 2011 Texas Instruments Inc.
+ *
+ * Author: Margarita Olaya Cabrera <magi@slimlogic.co.uk>
+ *
+ * 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 driver is based on wm8350 implementation.
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/gpio.h>
+#include <linux/i2c.h>
+#include <linux/mfd/core.h>
+#include <linux/mfd/tps65912.h>
+
+static int tps65912_i2c_read(struct tps65912 *tps65912, u8 reg,
+ int bytes, void *dest)
+{
+ struct i2c_client *i2c = tps65912->control_data;
+ struct i2c_msg xfer[2];
+ int ret;
+
+ /* Write register */
+ xfer[0].addr = i2c->addr;
+ xfer[0].flags = 0;
+ xfer[0].len = 1;
+ xfer[0].buf = ®
+
+ /* Read data */
+ xfer[1].addr = i2c->addr;
+ xfer[1].flags = I2C_M_RD;
+ xfer[1].len = bytes;
+ xfer[1].buf = dest;
+
+ ret = i2c_transfer(i2c->adapter, xfer, 2);
+ if (ret == 2)
+ ret = 0;
+ else if (ret >= 0)
+ ret = -EIO;
+ return ret;
+}
+
+static int tps65912_i2c_write(struct tps65912 *tps65912, u8 reg,
+ int bytes, void *src)
+{
+ struct i2c_client *i2c = tps65912->control_data;
+ /* we add 1 byte for device register */
+ u8 msg[TPS6591X_MAX_REGISTER + 1];
+ int ret;
+
+ if (bytes > TPS6591X_MAX_REGISTER)
+ return -EINVAL;
+
+ msg[0] = reg;
+ memcpy(&msg[1], src, bytes);
+
+ ret = i2c_master_send(i2c, msg, bytes + 1);
+ if (ret < 0)
+ return ret;
+ if (ret != bytes + 1)
+ return -EIO;
+
+ return 0;
+}
+
+static int tps65912_i2c_probe(struct i2c_client *i2c,
+ const struct i2c_device_id *id)
+{
+ struct tps65912 *tps65912;
+
+ tps65912 = kzalloc(sizeof(struct tps65912), GFP_KERNEL);
+ if (tps65912 == NULL)
+ return -ENOMEM;
+
+ i2c_set_clientdata(i2c, tps65912);
+ tps65912->dev = &i2c->dev;
+ tps65912->control_data = i2c;
+ tps65912->read = tps65912_i2c_read;
+ tps65912->write = tps65912_i2c_write;
+
+ return tps65912_device_init(tps65912);
+}
+
+static int tps65912_i2c_remove(struct i2c_client *i2c)
+{
+ struct tps65912 *tps65912 = i2c_get_clientdata(i2c);
+
+ tps65912_device_exit(tps65912);
+
+ return 0;
+}
+
+static const struct i2c_device_id tps65912_i2c_id[] = {
+ {"tps65912", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, tps65912_i2c_id);
+
+static struct i2c_driver tps65912_i2c_driver = {
+ .driver = {
+ .name = "tps65912",
+ .owner = THIS_MODULE,
+ },
+ .probe = tps65912_i2c_probe,
+ .remove = tps65912_i2c_remove,
+ .id_table = tps65912_i2c_id,
+};
+
+static int __init tps65912_i2c_init(void)
+{
+ int ret;
+
+ ret = i2c_add_driver(&tps65912_i2c_driver);
+ if (ret != 0)
+ pr_err("Failed to register TPS65912 I2C driver: %d\n", ret);
+
+ return ret;
+}
+/* init early so consumer devices can complete system boot */
+subsys_initcall(tps65912_i2c_init);
+
+static void __exit tps65912_i2c_exit(void)
+{
+ i2c_del_driver(&tps65912_i2c_driver);
+}
+module_exit(tps65912_i2c_exit);
+
+MODULE_AUTHOR("Margarita Olaya <magi@slimlogic.co.uk>");
+MODULE_DESCRIPTION("TPS6591x chip family multi-function driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/mfd/tps65912-irq.c b/drivers/mfd/tps65912-irq.c
new file mode 100644
index 0000000..d360a83
--- /dev/null
+++ b/drivers/mfd/tps65912-irq.c
@@ -0,0 +1,224 @@
+/*
+ * tps65912-irq.c -- TI TPS6591x
+ *
+ * Copyright 2011 Texas Instruments Inc.
+ *
+ * Author: Margarita Olaya <magi@slimlogic.co.uk>
+ *
+ * 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 driver is based on wm8350 implementation.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/bug.h>
+#include <linux/device.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/gpio.h>
+#include <linux/mfd/tps65912.h>
+
+static inline int irq_to_tps65912_irq(struct tps65912 *tps65912,
+ int irq)
+{
+ return irq - tps65912->irq_base;
+}
+
+/*
+ * This is a threaded IRQ handler so can access I2C/SPI. Since the
+ * IRQ handler explicitly clears the IRQ it handles the IRQ line
+ * will be reasserted and the physical IRQ will be handled again if
+ * another interrupt is asserted while we run - in the normal course
+ * of events this is a rare occurrence so we save I2C/SPI reads. We're
+ * also assuming that it's rare to get lots of interrupts firing
+ * simultaneously so try to minimise I/O.
+ */
+static irqreturn_t tps65912_irq(int irq, void *irq_data)
+{
+ struct tps65912 *tps65912 = irq_data;
+ u32 irq_sts;
+ u32 irq_mask;
+ u8 reg;
+ int i;
+
+
+ tps65912->read(tps65912, TPS65912_INT_STS, 1, ®);
+ irq_sts = reg;
+ tps65912->read(tps65912, TPS65912_INT_STS2, 1, ®);
+ irq_sts |= reg << 8;
+ tps65912->read(tps65912, TPS65912_INT_STS3, 1, ®);
+ irq_sts |= reg << 16;
+ tps65912->read(tps65912, TPS65912_INT_STS4, 1, ®);
+ irq_sts |= reg << 24;
+
+ tps65912->read(tps65912, TPS65912_INT_MSK, 1, ®);
+ irq_mask = reg;
+ tps65912->read(tps65912, TPS65912_INT_MSK2, 1, ®);
+ irq_mask |= reg << 8;
+ tps65912->read(tps65912, TPS65912_INT_MSK3, 1, ®);
+ irq_mask |= reg << 16;
+ tps65912->read(tps65912, TPS65912_INT_MSK4, 1, ®);
+ irq_mask |= reg << 24;
+
+ irq_sts &= ~irq_mask;
+ if (!irq_sts)
+ return IRQ_NONE;
+
+ for (i = 0; i < tps65912->irq_num; i++) {
+ if (!(irq_sts & (1 << i)))
+ continue;
+
+ handle_nested_irq(tps65912->irq_base + i);
+ }
+
+ /* Write the STS register back to clear IRQs we handled */
+ reg = irq_sts & 0xFF;
+ irq_sts >>= 8;
+ if (reg)
+ tps65912->write(tps65912, TPS65912_INT_STS, 1, ®);
+ reg = irq_sts & 0xFF;
+ irq_sts >>= 8;
+ if (reg)
+ tps65912->write(tps65912, TPS65912_INT_STS2, 1, ®);
+ reg = irq_sts & 0xFF;
+ irq_sts >>= 8;
+ if (reg)
+ tps65912->write(tps65912, TPS65912_INT_STS3, 1, ®);
+ reg = irq_sts & 0xFF;
+ if (reg)
+ tps65912->write(tps65912, TPS65912_INT_STS4, 1, ®);
+
+ return IRQ_HANDLED;
+}
+
+static void tps65912_irq_lock(struct irq_data *data)
+{
+ struct tps65912 *tps65912 = irq_data_get_irq_chip_data(data);
+
+ mutex_lock(&tps65912->irq_lock);
+}
+
+static void tps65912_irq_sync_unlock(struct irq_data *data)
+{
+ struct tps65912 *tps65912 = irq_data_get_irq_chip_data(data);
+ u32 reg_mask;
+ u8 reg;
+
+ tps65912->read(tps65912, TPS65912_INT_MSK, 1, ®);
+ reg_mask = reg;
+ tps65912->read(tps65912, TPS65912_INT_MSK2, 1, ®);
+ reg_mask |= reg << 8;
+ tps65912->read(tps65912, TPS65912_INT_MSK3, 1, ®);
+ reg_mask |= reg << 16;
+ tps65912->read(tps65912, TPS65912_INT_MSK4, 1, ®);
+ reg_mask |= reg << 24;
+
+ if (tps65912->irq_mask != reg_mask) {
+ reg = tps65912->irq_mask & 0xFF;
+ tps65912->write(tps65912, TPS65912_INT_MSK, 1, ®);
+ reg = tps65912->irq_mask >> 8 & 0xFF;
+ tps65912->write(tps65912, TPS65912_INT_MSK2, 1, ®);
+ reg = tps65912->irq_mask >> 16 & 0xFF;
+ tps65912->write(tps65912, TPS65912_INT_MSK3, 1, ®);
+ reg = tps65912->irq_mask >> 24 & 0xFF;
+ tps65912->write(tps65912, TPS65912_INT_MSK4, 1, ®);
+ }
+
+ mutex_unlock(&tps65912->irq_lock);
+}
+
+static void tps65912_irq_enable(struct irq_data *data)
+{
+ struct tps65912 *tps65912 = irq_data_get_irq_chip_data(data);
+
+ tps65912->irq_mask &= ~(1 << irq_to_tps65912_irq(tps65912, data->irq));
+}
+
+static void tps65912_irq_disable(struct irq_data *data)
+{
+ struct tps65912 *tps65912 = irq_data_get_irq_chip_data(data);
+
+ tps65912->irq_mask |= (1 << irq_to_tps65912_irq(tps65912, data->irq));
+}
+
+static struct irq_chip tps65912_irq_chip = {
+ .name = "tps65912",
+ .irq_bus_lock = tps65912_irq_lock,
+ .irq_bus_sync_unlock = tps65912_irq_sync_unlock,
+ .irq_disable = tps65912_irq_disable,
+ .irq_enable = tps65912_irq_enable,
+};
+
+int tps65912_irq_init(struct tps65912 *tps65912, int irq,
+ struct tps65912_platform_data *pdata)
+{
+ int ret, cur_irq;
+ int flags = IRQF_ONESHOT;
+ u8 reg;
+
+ if (!irq) {
+ dev_warn(tps65912->dev, "No interrupt support, no core IRQ\n");
+ return 0;
+ }
+
+ if (!pdata || !pdata->irq_base) {
+ dev_warn(tps65912->dev, "No interrupt support, no IRQ base\n");
+ return 0;
+ }
+
+ /* Clear unattended interrupts */
+ tps65912->read(tps65912, TPS65912_INT_STS, 1, ®);
+ tps65912->write(tps65912, TPS65912_INT_STS, 1, ®);
+ tps65912->read(tps65912, TPS65912_INT_STS2, 1, ®);
+ tps65912->write(tps65912, TPS65912_INT_STS2, 1, ®);
+ tps65912->read(tps65912, TPS65912_INT_STS3, 1, ®);
+ tps65912->write(tps65912, TPS65912_INT_STS3, 1, ®);
+ tps65912->read(tps65912, TPS65912_INT_STS4, 1, ®);
+ tps65912->write(tps65912, TPS65912_INT_STS4, 1, ®);
+
+ /* Mask top level interrupts */
+ tps65912->irq_mask = 0xFFFFFFFF;
+
+ mutex_init(&tps65912->irq_lock);
+ tps65912->chip_irq = irq;
+ tps65912->irq_base = pdata->irq_base;
+
+ tps65912->irq_num = TPS65912_NUM_IRQ;
+
+ /* Register with genirq */
+ for (cur_irq = tps65912->irq_base;
+ cur_irq < tps65912->irq_num + tps65912->irq_base;
+ cur_irq++) {
+ irq_set_chip_data(cur_irq, tps65912);
+ irq_set_chip_and_handler(cur_irq, &tps65912_irq_chip,
+ handle_edge_irq);
+ irq_set_nested_thread(cur_irq, 1);
+ /* ARM needs us to explicitly flag the IRQ as valid
+ * and will set them noprobe when we do so. */
+#ifdef CONFIG_ARM
+ set_irq_flags(cur_irq, IRQF_VALID);
+#else
+ irq_set_noprobe(cur_irq);
+#endif
+ }
+
+ ret = request_threaded_irq(irq, NULL, tps65912_irq, flags,
+ "tps65912", tps65912);
+
+ irq_set_irq_type(irq, IRQ_TYPE_LEVEL_LOW);
+ if (ret != 0)
+ dev_err(tps65912->dev, "Failed to request IRQ: %d\n", ret);
+
+ return ret;
+}
+
+int tps65912_irq_exit(struct tps65912 *tps65912)
+{
+ free_irq(tps65912->chip_irq, tps65912);
+ return 0;
+}
diff --git a/drivers/mfd/tps65912-spi.c b/drivers/mfd/tps65912-spi.c
new file mode 100644
index 0000000..6d71e0d
--- /dev/null
+++ b/drivers/mfd/tps65912-spi.c
@@ -0,0 +1,142 @@
+/*
+ * tps65912-spi.c -- SPI access for TI TPS65912x PMIC
+ *
+ * Copyright 2011 Texas Instruments Inc.
+ *
+ * Author: Margarita Olaya Cabrera <magi@slimlogic.co.uk>
+ *
+ * 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 driver is based on wm8350 implementation.
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/gpio.h>
+#include <linux/spi/spi.h>
+#include <linux/mfd/core.h>
+#include <linux/mfd/tps65912.h>
+
+static int tps65912_spi_write(struct tps65912 *tps65912, u8 addr,
+ int bytes, void *src)
+{
+ struct spi_device *spi = tps65912->control_data;
+ u8 *data = (u8 *) src;
+ int ret;
+ /* bit 23 is the read/write bit */
+ unsigned long spi_data = 1 << 23 | addr << 15 | *data;
+ struct spi_transfer xfer;
+ struct spi_message msg;
+ u32 tx_buf, rx_buf;
+
+ tx_buf = spi_data;
+ rx_buf = 0;
+
+ xfer.tx_buf = &tx_buf;
+ xfer.rx_buf = NULL;
+ xfer.len = sizeof(unsigned long);
+ xfer.bits_per_word = 24;
+
+ spi_message_init(&msg);
+ spi_message_add_tail(&xfer, &msg);
+
+ ret = spi_sync(spi, &msg);
+ return ret;
+}
+
+static int tps65912_spi_read(struct tps65912 *tps65912, u8 addr,
+ int bytes, void *dest)
+{
+ struct spi_device *spi = tps65912->control_data;
+ /* bit 23 is the read/write bit */
+ unsigned long spi_data = 0 << 23 | addr << 15;
+ struct spi_transfer xfer;
+ struct spi_message msg;
+ int ret;
+ u8 *data = (u8 *) dest;
+ u32 tx_buf, rx_buf;
+
+ tx_buf = spi_data;
+ rx_buf = 0;
+
+ xfer.tx_buf = &tx_buf;
+ xfer.rx_buf = &rx_buf;
+ xfer.len = sizeof(unsigned long);
+ xfer.bits_per_word = 24;
+
+ spi_message_init(&msg);
+ spi_message_add_tail(&xfer, &msg);
+
+ if (spi == NULL)
+ return 0;
+
+ ret = spi_sync(spi, &msg);
+ if (ret == 0)
+ *data = (u8) (rx_buf & 0xFF);
+ return ret;
+}
+
+static int __devinit tps65912_spi_probe(struct spi_device *spi)
+{
+ struct tps65912 *tps65912;
+
+ tps65912 = kzalloc(sizeof(struct tps65912), GFP_KERNEL);
+ if (tps65912 == NULL)
+ return -ENOMEM;
+
+ tps65912->dev = &spi->dev;
+ tps65912->control_data = spi;
+ tps65912->read = tps65912_spi_read;
+ tps65912->write = tps65912_spi_write;
+
+ spi_set_drvdata(spi, tps65912);
+
+ return tps65912_device_init(tps65912);
+}
+
+static int __devexit tps65912_spi_remove(struct spi_device *spi)
+{
+ struct tps65912 *tps65912 = spi_get_drvdata(spi);
+
+ tps65912_device_exit(tps65912);
+
+ return 0;
+}
+
+static struct spi_driver tps65912_spi_driver = {
+ .driver = {
+ .name = "tps65912",
+ .bus = &spi_bus_type,
+ .owner = THIS_MODULE,
+ },
+ .probe = tps65912_spi_probe,
+ .remove = __devexit_p(tps65912_spi_remove),
+};
+
+static int __init tps65912_spi_init(void)
+{
+ int ret;
+
+ ret = spi_register_driver(&tps65912_spi_driver);
+ if (ret != 0)
+ pr_err("Failed to register TPS65912 SPI driver: %d\n", ret);
+
+ return 0;
+}
+/* init early so consumer devices can complete system boot */
+subsys_initcall(tps65912_spi_init);
+
+static void __exit tps65912_spi_exit(void)
+{
+ spi_unregister_driver(&tps65912_spi_driver);
+}
+module_exit(tps65912_spi_exit);
+
+MODULE_AUTHOR("Margarita Olaya <magi@slimlogic.co.uk>");
+MODULE_DESCRIPTION("SPI support for TPS65912 chip family mfd");
+MODULE_LICENSE("GPL");
diff --git a/drivers/mfd/twl-core.c b/drivers/mfd/twl-core.c
index a2eddc7..01ecfee 100644
--- a/drivers/mfd/twl-core.c
+++ b/drivers/mfd/twl-core.c
@@ -1283,6 +1283,8 @@
{ "tps65950", 0 }, /* catalog version of twl5030 */
{ "tps65930", TPS_SUBSET }, /* fewer LDOs and DACs; no charger */
{ "tps65920", TPS_SUBSET }, /* fewer LDOs; no codec or charger */
+ { "tps65921", TPS_SUBSET }, /* fewer LDOs; no codec, no LED
+ and vibrator. Charger in USB module*/
{ "twl6030", TWL6030_CLASS }, /* "Phoenix power chip" */
{ "twl6025", TWL6030_CLASS | TWL6025_SUBCLASS }, /* "Phoenix lite" */
{ /* end of list */ },
diff --git a/drivers/mfd/twl4030-madc.c b/drivers/mfd/twl4030-madc.c
index 3941ddc..b5d598c 100644
--- a/drivers/mfd/twl4030-madc.c
+++ b/drivers/mfd/twl4030-madc.c
@@ -530,13 +530,13 @@
if (ret) {
dev_err(twl4030_madc->dev,
"unable to write sel register 0x%X\n", method->sel + 1);
- return ret;
+ goto out;
}
ret = twl_i2c_write_u8(TWL4030_MODULE_MADC, ch_lsb, method->sel);
if (ret) {
dev_err(twl4030_madc->dev,
"unable to write sel register 0x%X\n", method->sel + 1);
- return ret;
+ goto out;
}
/* Select averaging for all channels if do_avg is set */
if (req->do_avg) {
@@ -546,7 +546,7 @@
dev_err(twl4030_madc->dev,
"unable to write avg register 0x%X\n",
method->avg + 1);
- return ret;
+ goto out;
}
ret = twl_i2c_write_u8(TWL4030_MODULE_MADC,
ch_lsb, method->avg);
@@ -554,7 +554,7 @@
dev_err(twl4030_madc->dev,
"unable to write sel reg 0x%X\n",
method->sel + 1);
- return ret;
+ goto out;
}
}
if (req->type == TWL4030_MADC_IRQ_ONESHOT && req->func_cb != NULL) {
diff --git a/drivers/mfd/twl6030-pwm.c b/drivers/mfd/twl6030-pwm.c
index 5d25bdc..e8fee14 100644
--- a/drivers/mfd/twl6030-pwm.c
+++ b/drivers/mfd/twl6030-pwm.c
@@ -161,3 +161,5 @@
kfree(pwm);
}
EXPORT_SYMBOL(pwm_free);
+
+MODULE_LICENSE("GPL");
diff --git a/drivers/mfd/wm831x-auxadc.c b/drivers/mfd/wm831x-auxadc.c
new file mode 100644
index 0000000..8721095
--- /dev/null
+++ b/drivers/mfd/wm831x-auxadc.c
@@ -0,0 +1,299 @@
+/*
+ * wm831x-auxadc.c -- AUXADC for Wolfson WM831x PMICs
+ *
+ * Copyright 2009-2011 Wolfson Microelectronics PLC.
+ *
+ * Author: Mark Brown <broonie@opensource.wolfsonmicro.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/mfd/core.h>
+#include <linux/slab.h>
+#include <linux/list.h>
+
+#include <linux/mfd/wm831x/core.h>
+#include <linux/mfd/wm831x/pdata.h>
+#include <linux/mfd/wm831x/irq.h>
+#include <linux/mfd/wm831x/auxadc.h>
+#include <linux/mfd/wm831x/otp.h>
+#include <linux/mfd/wm831x/regulator.h>
+
+struct wm831x_auxadc_req {
+ struct list_head list;
+ enum wm831x_auxadc input;
+ int val;
+ struct completion done;
+};
+
+static int wm831x_auxadc_read_irq(struct wm831x *wm831x,
+ enum wm831x_auxadc input)
+{
+ struct wm831x_auxadc_req *req;
+ int ret;
+ bool ena = false;
+
+ req = kzalloc(sizeof(*req), GFP_KERNEL);
+ if (!req)
+ return -ENOMEM;
+
+ init_completion(&req->done);
+ req->input = input;
+ req->val = -ETIMEDOUT;
+
+ mutex_lock(&wm831x->auxadc_lock);
+
+ /* Enqueue the request */
+ list_add(&req->list, &wm831x->auxadc_pending);
+
+ ena = !wm831x->auxadc_active;
+
+ if (ena) {
+ ret = wm831x_set_bits(wm831x, WM831X_AUXADC_CONTROL,
+ WM831X_AUX_ENA, WM831X_AUX_ENA);
+ if (ret != 0) {
+ dev_err(wm831x->dev, "Failed to enable AUXADC: %d\n",
+ ret);
+ goto out;
+ }
+ }
+
+ /* Enable the conversion if not already running */
+ if (!(wm831x->auxadc_active & (1 << input))) {
+ ret = wm831x_set_bits(wm831x, WM831X_AUXADC_SOURCE,
+ 1 << input, 1 << input);
+ if (ret != 0) {
+ dev_err(wm831x->dev,
+ "Failed to set AUXADC source: %d\n", ret);
+ goto out;
+ }
+
+ wm831x->auxadc_active |= 1 << input;
+ }
+
+ /* We convert at the fastest rate possible */
+ if (ena) {
+ ret = wm831x_set_bits(wm831x, WM831X_AUXADC_CONTROL,
+ WM831X_AUX_CVT_ENA |
+ WM831X_AUX_RATE_MASK,
+ WM831X_AUX_CVT_ENA |
+ WM831X_AUX_RATE_MASK);
+ if (ret != 0) {
+ dev_err(wm831x->dev, "Failed to start AUXADC: %d\n",
+ ret);
+ goto out;
+ }
+ }
+
+ mutex_unlock(&wm831x->auxadc_lock);
+
+ /* Wait for an interrupt */
+ wait_for_completion_timeout(&req->done, msecs_to_jiffies(500));
+
+ mutex_lock(&wm831x->auxadc_lock);
+
+ list_del(&req->list);
+ ret = req->val;
+
+out:
+ mutex_unlock(&wm831x->auxadc_lock);
+
+ kfree(req);
+
+ return ret;
+}
+
+static irqreturn_t wm831x_auxadc_irq(int irq, void *irq_data)
+{
+ struct wm831x *wm831x = irq_data;
+ struct wm831x_auxadc_req *req;
+ int ret, input, val;
+
+ ret = wm831x_reg_read(wm831x, WM831X_AUXADC_DATA);
+ if (ret < 0) {
+ dev_err(wm831x->dev,
+ "Failed to read AUXADC data: %d\n", ret);
+ return IRQ_NONE;
+ }
+
+ input = ((ret & WM831X_AUX_DATA_SRC_MASK)
+ >> WM831X_AUX_DATA_SRC_SHIFT) - 1;
+
+ if (input == 14)
+ input = WM831X_AUX_CAL;
+
+ val = ret & WM831X_AUX_DATA_MASK;
+
+ mutex_lock(&wm831x->auxadc_lock);
+
+ /* Disable this conversion, we're about to complete all users */
+ wm831x_set_bits(wm831x, WM831X_AUXADC_SOURCE,
+ 1 << input, 0);
+ wm831x->auxadc_active &= ~(1 << input);
+
+ /* Turn off the entire convertor if idle */
+ if (!wm831x->auxadc_active)
+ wm831x_reg_write(wm831x, WM831X_AUXADC_CONTROL, 0);
+
+ /* Wake up any threads waiting for this request */
+ list_for_each_entry(req, &wm831x->auxadc_pending, list) {
+ if (req->input == input) {
+ req->val = val;
+ complete(&req->done);
+ }
+ }
+
+ mutex_unlock(&wm831x->auxadc_lock);
+
+ return IRQ_HANDLED;
+}
+
+static int wm831x_auxadc_read_polled(struct wm831x *wm831x,
+ enum wm831x_auxadc input)
+{
+ int ret, src, timeout;
+
+ mutex_lock(&wm831x->auxadc_lock);
+
+ ret = wm831x_set_bits(wm831x, WM831X_AUXADC_CONTROL,
+ WM831X_AUX_ENA, WM831X_AUX_ENA);
+ if (ret < 0) {
+ dev_err(wm831x->dev, "Failed to enable AUXADC: %d\n", ret);
+ goto out;
+ }
+
+ /* We force a single source at present */
+ src = input;
+ ret = wm831x_reg_write(wm831x, WM831X_AUXADC_SOURCE,
+ 1 << src);
+ if (ret < 0) {
+ dev_err(wm831x->dev, "Failed to set AUXADC source: %d\n", ret);
+ goto out;
+ }
+
+ ret = wm831x_set_bits(wm831x, WM831X_AUXADC_CONTROL,
+ WM831X_AUX_CVT_ENA, WM831X_AUX_CVT_ENA);
+ if (ret < 0) {
+ dev_err(wm831x->dev, "Failed to start AUXADC: %d\n", ret);
+ goto disable;
+ }
+
+ /* If we're not using interrupts then poll the
+ * interrupt status register */
+ timeout = 5;
+ while (timeout) {
+ msleep(1);
+
+ ret = wm831x_reg_read(wm831x,
+ WM831X_INTERRUPT_STATUS_1);
+ if (ret < 0) {
+ dev_err(wm831x->dev,
+ "ISR 1 read failed: %d\n", ret);
+ goto disable;
+ }
+
+ /* Did it complete? */
+ if (ret & WM831X_AUXADC_DATA_EINT) {
+ wm831x_reg_write(wm831x,
+ WM831X_INTERRUPT_STATUS_1,
+ WM831X_AUXADC_DATA_EINT);
+ break;
+ } else {
+ dev_err(wm831x->dev,
+ "AUXADC conversion timeout\n");
+ ret = -EBUSY;
+ goto disable;
+ }
+ }
+
+ ret = wm831x_reg_read(wm831x, WM831X_AUXADC_DATA);
+ if (ret < 0) {
+ dev_err(wm831x->dev,
+ "Failed to read AUXADC data: %d\n", ret);
+ goto disable;
+ }
+
+ src = ((ret & WM831X_AUX_DATA_SRC_MASK)
+ >> WM831X_AUX_DATA_SRC_SHIFT) - 1;
+
+ if (src == 14)
+ src = WM831X_AUX_CAL;
+
+ if (src != input) {
+ dev_err(wm831x->dev, "Data from source %d not %d\n",
+ src, input);
+ ret = -EINVAL;
+ } else {
+ ret &= WM831X_AUX_DATA_MASK;
+ }
+
+disable:
+ wm831x_set_bits(wm831x, WM831X_AUXADC_CONTROL, WM831X_AUX_ENA, 0);
+out:
+ mutex_unlock(&wm831x->auxadc_lock);
+ return ret;
+}
+
+/**
+ * wm831x_auxadc_read: Read a value from the WM831x AUXADC
+ *
+ * @wm831x: Device to read from.
+ * @input: AUXADC input to read.
+ */
+int wm831x_auxadc_read(struct wm831x *wm831x, enum wm831x_auxadc input)
+{
+ return wm831x->auxadc_read(wm831x, input);
+}
+EXPORT_SYMBOL_GPL(wm831x_auxadc_read);
+
+/**
+ * wm831x_auxadc_read_uv: Read a voltage from the WM831x AUXADC
+ *
+ * @wm831x: Device to read from.
+ * @input: AUXADC input to read.
+ */
+int wm831x_auxadc_read_uv(struct wm831x *wm831x, enum wm831x_auxadc input)
+{
+ int ret;
+
+ ret = wm831x_auxadc_read(wm831x, input);
+ if (ret < 0)
+ return ret;
+
+ ret *= 1465;
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(wm831x_auxadc_read_uv);
+
+void wm831x_auxadc_init(struct wm831x *wm831x)
+{
+ int ret;
+
+ mutex_init(&wm831x->auxadc_lock);
+ INIT_LIST_HEAD(&wm831x->auxadc_pending);
+
+ if (wm831x->irq && wm831x->irq_base) {
+ wm831x->auxadc_read = wm831x_auxadc_read_irq;
+
+ ret = request_threaded_irq(wm831x->irq_base +
+ WM831X_IRQ_AUXADC_DATA,
+ NULL, wm831x_auxadc_irq, 0,
+ "auxadc", wm831x);
+ if (ret < 0) {
+ dev_err(wm831x->dev, "AUXADC IRQ request failed: %d\n",
+ ret);
+ wm831x->auxadc_read = NULL;
+ }
+ }
+
+ if (!wm831x->auxadc_read)
+ wm831x->auxadc_read = wm831x_auxadc_read_polled;
+}
diff --git a/drivers/mfd/wm831x-core.c b/drivers/mfd/wm831x-core.c
index 265f75f..282e76a 100644
--- a/drivers/mfd/wm831x-core.c
+++ b/drivers/mfd/wm831x-core.c
@@ -295,7 +295,7 @@
goto out;
r &= ~mask;
- r |= val;
+ r |= val & mask;
ret = wm831x_write(wm831x, reg, 2, &r);
@@ -306,146 +306,6 @@
}
EXPORT_SYMBOL_GPL(wm831x_set_bits);
-/**
- * wm831x_auxadc_read: Read a value from the WM831x AUXADC
- *
- * @wm831x: Device to read from.
- * @input: AUXADC input to read.
- */
-int wm831x_auxadc_read(struct wm831x *wm831x, enum wm831x_auxadc input)
-{
- int ret, src, irq_masked, timeout;
-
- /* Are we using the interrupt? */
- irq_masked = wm831x_reg_read(wm831x, WM831X_INTERRUPT_STATUS_1_MASK);
- irq_masked &= WM831X_AUXADC_DATA_EINT;
-
- mutex_lock(&wm831x->auxadc_lock);
-
- ret = wm831x_set_bits(wm831x, WM831X_AUXADC_CONTROL,
- WM831X_AUX_ENA, WM831X_AUX_ENA);
- if (ret < 0) {
- dev_err(wm831x->dev, "Failed to enable AUXADC: %d\n", ret);
- goto out;
- }
-
- /* We force a single source at present */
- src = input;
- ret = wm831x_reg_write(wm831x, WM831X_AUXADC_SOURCE,
- 1 << src);
- if (ret < 0) {
- dev_err(wm831x->dev, "Failed to set AUXADC source: %d\n", ret);
- goto out;
- }
-
- /* Clear any notification from a very late arriving interrupt */
- try_wait_for_completion(&wm831x->auxadc_done);
-
- ret = wm831x_set_bits(wm831x, WM831X_AUXADC_CONTROL,
- WM831X_AUX_CVT_ENA, WM831X_AUX_CVT_ENA);
- if (ret < 0) {
- dev_err(wm831x->dev, "Failed to start AUXADC: %d\n", ret);
- goto disable;
- }
-
- if (irq_masked) {
- /* If we're not using interrupts then poll the
- * interrupt status register */
- timeout = 5;
- while (timeout) {
- msleep(1);
-
- ret = wm831x_reg_read(wm831x,
- WM831X_INTERRUPT_STATUS_1);
- if (ret < 0) {
- dev_err(wm831x->dev,
- "ISR 1 read failed: %d\n", ret);
- goto disable;
- }
-
- /* Did it complete? */
- if (ret & WM831X_AUXADC_DATA_EINT) {
- wm831x_reg_write(wm831x,
- WM831X_INTERRUPT_STATUS_1,
- WM831X_AUXADC_DATA_EINT);
- break;
- } else {
- dev_err(wm831x->dev,
- "AUXADC conversion timeout\n");
- ret = -EBUSY;
- goto disable;
- }
- }
- } else {
- /* If we are using interrupts then wait for the
- * interrupt to complete. Use an extremely long
- * timeout to handle situations with heavy load where
- * the notification of the interrupt may be delayed by
- * threaded IRQ handling. */
- if (!wait_for_completion_timeout(&wm831x->auxadc_done,
- msecs_to_jiffies(500))) {
- dev_err(wm831x->dev, "Timed out waiting for AUXADC\n");
- ret = -EBUSY;
- goto disable;
- }
- }
-
- ret = wm831x_reg_read(wm831x, WM831X_AUXADC_DATA);
- if (ret < 0) {
- dev_err(wm831x->dev, "Failed to read AUXADC data: %d\n", ret);
- } else {
- src = ((ret & WM831X_AUX_DATA_SRC_MASK)
- >> WM831X_AUX_DATA_SRC_SHIFT) - 1;
-
- if (src == 14)
- src = WM831X_AUX_CAL;
-
- if (src != input) {
- dev_err(wm831x->dev, "Data from source %d not %d\n",
- src, input);
- ret = -EINVAL;
- } else {
- ret &= WM831X_AUX_DATA_MASK;
- }
- }
-
-disable:
- wm831x_set_bits(wm831x, WM831X_AUXADC_CONTROL, WM831X_AUX_ENA, 0);
-out:
- mutex_unlock(&wm831x->auxadc_lock);
- return ret;
-}
-EXPORT_SYMBOL_GPL(wm831x_auxadc_read);
-
-static irqreturn_t wm831x_auxadc_irq(int irq, void *irq_data)
-{
- struct wm831x *wm831x = irq_data;
-
- complete(&wm831x->auxadc_done);
-
- return IRQ_HANDLED;
-}
-
-/**
- * wm831x_auxadc_read_uv: Read a voltage from the WM831x AUXADC
- *
- * @wm831x: Device to read from.
- * @input: AUXADC input to read.
- */
-int wm831x_auxadc_read_uv(struct wm831x *wm831x, enum wm831x_auxadc input)
-{
- int ret;
-
- ret = wm831x_auxadc_read(wm831x, input);
- if (ret < 0)
- return ret;
-
- ret *= 1465;
-
- return ret;
-}
-EXPORT_SYMBOL_GPL(wm831x_auxadc_read_uv);
-
static struct resource wm831x_dcdc1_resources[] = {
{
.start = WM831X_DC1_CONTROL_1,
@@ -872,6 +732,9 @@
.resources = wm831x_dcdc4_resources,
},
{
+ .name = "wm831x-clk",
+ },
+ {
.name = "wm831x-epe",
.id = 1,
},
@@ -976,11 +839,6 @@
.resources = wm831x_power_resources,
},
{
- .name = "wm831x-rtc",
- .num_resources = ARRAY_SIZE(wm831x_rtc_resources),
- .resources = wm831x_rtc_resources,
- },
- {
.name = "wm831x-status",
.id = 1,
.num_resources = ARRAY_SIZE(wm831x_status1_resources),
@@ -1028,6 +886,9 @@
.resources = wm831x_dcdc4_resources,
},
{
+ .name = "wm831x-clk",
+ },
+ {
.name = "wm831x-epe",
.id = 1,
},
@@ -1108,11 +969,6 @@
.resources = wm831x_power_resources,
},
{
- .name = "wm831x-rtc",
- .num_resources = ARRAY_SIZE(wm831x_rtc_resources),
- .resources = wm831x_rtc_resources,
- },
- {
.name = "wm831x-status",
.id = 1,
.num_resources = ARRAY_SIZE(wm831x_status1_resources),
@@ -1125,11 +981,6 @@
.resources = wm831x_status2_resources,
},
{
- .name = "wm831x-touch",
- .num_resources = ARRAY_SIZE(wm831x_touch_resources),
- .resources = wm831x_touch_resources,
- },
- {
.name = "wm831x-watchdog",
.num_resources = ARRAY_SIZE(wm831x_wdt_resources),
.resources = wm831x_wdt_resources,
@@ -1165,6 +1016,9 @@
.resources = wm831x_dcdc4_resources,
},
{
+ .name = "wm831x-clk",
+ },
+ {
.name = "wm831x-epe",
.id = 1,
},
@@ -1269,11 +1123,6 @@
.resources = wm831x_power_resources,
},
{
- .name = "wm831x-rtc",
- .num_resources = ARRAY_SIZE(wm831x_rtc_resources),
- .resources = wm831x_rtc_resources,
- },
- {
.name = "wm831x-status",
.id = 1,
.num_resources = ARRAY_SIZE(wm831x_status1_resources),
@@ -1286,11 +1135,6 @@
.resources = wm831x_status2_resources,
},
{
- .name = "wm831x-touch",
- .num_resources = ARRAY_SIZE(wm831x_touch_resources),
- .resources = wm831x_touch_resources,
- },
- {
.name = "wm831x-watchdog",
.num_resources = ARRAY_SIZE(wm831x_wdt_resources),
.resources = wm831x_wdt_resources,
@@ -1326,6 +1170,9 @@
.resources = wm8320_dcdc4_buck_resources,
},
{
+ .name = "wm831x-clk",
+ },
+ {
.name = "wm831x-gpio",
.num_resources = ARRAY_SIZE(wm831x_gpio_resources),
.resources = wm831x_gpio_resources,
@@ -1405,11 +1252,6 @@
.resources = wm831x_on_resources,
},
{
- .name = "wm831x-rtc",
- .num_resources = ARRAY_SIZE(wm831x_rtc_resources),
- .resources = wm831x_rtc_resources,
- },
- {
.name = "wm831x-status",
.id = 1,
.num_resources = ARRAY_SIZE(wm831x_status1_resources),
@@ -1428,6 +1270,22 @@
},
};
+static struct mfd_cell touch_devs[] = {
+ {
+ .name = "wm831x-touch",
+ .num_resources = ARRAY_SIZE(wm831x_touch_resources),
+ .resources = wm831x_touch_resources,
+ },
+};
+
+static struct mfd_cell rtc_devs[] = {
+ {
+ .name = "wm831x-rtc",
+ .num_resources = ARRAY_SIZE(wm831x_rtc_resources),
+ .resources = wm831x_rtc_resources,
+ },
+};
+
static struct mfd_cell backlight_devs[] = {
{
.name = "wm831x-backlight",
@@ -1440,14 +1298,12 @@
int wm831x_device_init(struct wm831x *wm831x, unsigned long id, int irq)
{
struct wm831x_pdata *pdata = wm831x->dev->platform_data;
- int rev;
+ int rev, wm831x_num;
enum wm831x_parent parent;
int ret, i;
mutex_init(&wm831x->io_lock);
mutex_init(&wm831x->key_lock);
- mutex_init(&wm831x->auxadc_lock);
- init_completion(&wm831x->auxadc_done);
dev_set_drvdata(wm831x->dev, wm831x);
ret = wm831x_reg_read(wm831x, WM831X_PARENT_ID);
@@ -1592,45 +1448,51 @@
}
}
+ /* Multiply by 10 as we have many subdevices of the same type */
+ if (pdata && pdata->wm831x_num)
+ wm831x_num = pdata->wm831x_num * 10;
+ else
+ wm831x_num = -1;
+
ret = wm831x_irq_init(wm831x, irq);
if (ret != 0)
goto err;
- if (wm831x->irq_base) {
- ret = request_threaded_irq(wm831x->irq_base +
- WM831X_IRQ_AUXADC_DATA,
- NULL, wm831x_auxadc_irq, 0,
- "auxadc", wm831x);
- if (ret < 0)
- dev_err(wm831x->dev, "AUXADC IRQ request failed: %d\n",
- ret);
- }
+ wm831x_auxadc_init(wm831x);
/* The core device is up, instantiate the subdevices. */
switch (parent) {
case WM8310:
- ret = mfd_add_devices(wm831x->dev, -1,
+ ret = mfd_add_devices(wm831x->dev, wm831x_num,
wm8310_devs, ARRAY_SIZE(wm8310_devs),
NULL, wm831x->irq_base);
break;
case WM8311:
- ret = mfd_add_devices(wm831x->dev, -1,
+ ret = mfd_add_devices(wm831x->dev, wm831x_num,
wm8311_devs, ARRAY_SIZE(wm8311_devs),
NULL, wm831x->irq_base);
+ if (!pdata || !pdata->disable_touch)
+ mfd_add_devices(wm831x->dev, wm831x_num,
+ touch_devs, ARRAY_SIZE(touch_devs),
+ NULL, wm831x->irq_base);
break;
case WM8312:
- ret = mfd_add_devices(wm831x->dev, -1,
+ ret = mfd_add_devices(wm831x->dev, wm831x_num,
wm8312_devs, ARRAY_SIZE(wm8312_devs),
NULL, wm831x->irq_base);
+ if (!pdata || !pdata->disable_touch)
+ mfd_add_devices(wm831x->dev, wm831x_num,
+ touch_devs, ARRAY_SIZE(touch_devs),
+ NULL, wm831x->irq_base);
break;
case WM8320:
case WM8321:
case WM8325:
case WM8326:
- ret = mfd_add_devices(wm831x->dev, -1,
+ ret = mfd_add_devices(wm831x->dev, wm831x_num,
wm8320_devs, ARRAY_SIZE(wm8320_devs),
NULL, wm831x->irq_base);
break;
@@ -1645,9 +1507,30 @@
goto err_irq;
}
+ /* The RTC can only be used if the 32.768kHz crystal is
+ * enabled; this can't be controlled by software at runtime.
+ */
+ ret = wm831x_reg_read(wm831x, WM831X_CLOCK_CONTROL_2);
+ if (ret < 0) {
+ dev_err(wm831x->dev, "Failed to read clock status: %d\n", ret);
+ goto err_irq;
+ }
+
+ if (ret & WM831X_XTAL_ENA) {
+ ret = mfd_add_devices(wm831x->dev, wm831x_num,
+ rtc_devs, ARRAY_SIZE(rtc_devs),
+ NULL, wm831x->irq_base);
+ if (ret != 0) {
+ dev_err(wm831x->dev, "Failed to add RTC: %d\n", ret);
+ goto err_irq;
+ }
+ } else {
+ dev_info(wm831x->dev, "32.768kHz clock disabled, no RTC\n");
+ }
+
if (pdata && pdata->backlight) {
/* Treat errors as non-critical */
- ret = mfd_add_devices(wm831x->dev, -1, backlight_devs,
+ ret = mfd_add_devices(wm831x->dev, wm831x_num, backlight_devs,
ARRAY_SIZE(backlight_devs), NULL,
wm831x->irq_base);
if (ret < 0)
diff --git a/drivers/mfd/wm831x-irq.c b/drivers/mfd/wm831x-irq.c
index 42b928e..ada1835 100644
--- a/drivers/mfd/wm831x-irq.c
+++ b/drivers/mfd/wm831x-irq.c
@@ -348,6 +348,15 @@
struct wm831x *wm831x = irq_data_get_irq_chip_data(data);
int i;
+ for (i = 0; i < ARRAY_SIZE(wm831x->gpio_update); i++) {
+ if (wm831x->gpio_update[i]) {
+ wm831x_set_bits(wm831x, WM831X_GPIO1_CONTROL + i,
+ WM831X_GPN_INT_MODE | WM831X_GPN_POL,
+ wm831x->gpio_update[i]);
+ wm831x->gpio_update[i] = 0;
+ }
+ }
+
for (i = 0; i < ARRAY_SIZE(wm831x->irq_masks_cur); i++) {
/* If there's been a change in the mask write it back
* to the hardware. */
@@ -387,7 +396,7 @@
static int wm831x_irq_set_type(struct irq_data *data, unsigned int type)
{
struct wm831x *wm831x = irq_data_get_irq_chip_data(data);
- int val, irq;
+ int irq;
irq = data->irq - wm831x->irq_base;
@@ -399,22 +408,30 @@
return -EINVAL;
}
+ /* Rebase the IRQ into the GPIO range so we've got a sensible array
+ * index.
+ */
+ irq -= WM831X_IRQ_GPIO_1;
+
+ /* We set the high bit to flag that we need an update; don't
+ * do the update here as we can be called with the bus lock
+ * held.
+ */
switch (type) {
case IRQ_TYPE_EDGE_BOTH:
- val = WM831X_GPN_INT_MODE;
+ wm831x->gpio_update[irq] = 0x10000 | WM831X_GPN_INT_MODE;
break;
case IRQ_TYPE_EDGE_RISING:
- val = WM831X_GPN_POL;
+ wm831x->gpio_update[irq] = 0x10000 | WM831X_GPN_POL;
break;
case IRQ_TYPE_EDGE_FALLING:
- val = 0;
+ wm831x->gpio_update[irq] = 0x10000;
break;
default:
return -EINVAL;
}
- return wm831x_set_bits(wm831x, WM831X_GPIO1_CONTROL + irq,
- WM831X_GPN_INT_MODE | WM831X_GPN_POL, val);
+ return 0;
}
static struct irq_chip wm831x_irq_chip = {
@@ -432,7 +449,7 @@
{
struct wm831x *wm831x = data;
unsigned int i;
- int primary;
+ int primary, status_addr;
int status_regs[WM831X_NUM_IRQ_REGS] = { 0 };
int read[WM831X_NUM_IRQ_REGS] = { 0 };
int *status;
@@ -467,8 +484,9 @@
/* Hopefully there should only be one register to read
* each time otherwise we ought to do a block read. */
if (!read[offset]) {
- *status = wm831x_reg_read(wm831x,
- irq_data_to_status_reg(&wm831x_irqs[i]));
+ status_addr = irq_data_to_status_reg(&wm831x_irqs[i]);
+
+ *status = wm831x_reg_read(wm831x, status_addr);
if (*status < 0) {
dev_err(wm831x->dev,
"Failed to read IRQ status: %d\n",
@@ -477,26 +495,21 @@
}
read[offset] = 1;
+
+ /* Ignore any bits that we don't think are masked */
+ *status &= ~wm831x->irq_masks_cur[offset];
+
+ /* Acknowledge now so we don't miss
+ * notifications while we handle.
+ */
+ wm831x_reg_write(wm831x, status_addr, *status);
}
- /* Report it if it isn't masked, or forget the status. */
- if ((*status & ~wm831x->irq_masks_cur[offset])
- & wm831x_irqs[i].mask)
+ if (*status & wm831x_irqs[i].mask)
handle_nested_irq(wm831x->irq_base + i);
- else
- *status &= ~wm831x_irqs[i].mask;
}
out:
- /* Touchscreen interrupts are handled specially in the driver */
- status_regs[0] &= ~(WM831X_TCHDATA_EINT | WM831X_TCHPD_EINT);
-
- for (i = 0; i < ARRAY_SIZE(status_regs); i++) {
- if (status_regs[i])
- wm831x_reg_write(wm831x, WM831X_INTERRUPT_STATUS_1 + i,
- status_regs[i]);
- }
-
return IRQ_HANDLED;
}
@@ -515,13 +528,22 @@
0xffff);
}
- if (!pdata || !pdata->irq_base) {
- dev_err(wm831x->dev,
- "No interrupt base specified, no interrupts\n");
+ /* Try to dynamically allocate IRQs if no base is specified */
+ if (!pdata || !pdata->irq_base)
+ wm831x->irq_base = -1;
+ else
+ wm831x->irq_base = pdata->irq_base;
+
+ wm831x->irq_base = irq_alloc_descs(wm831x->irq_base, 0,
+ WM831X_NUM_IRQS, 0);
+ if (wm831x->irq_base < 0) {
+ dev_warn(wm831x->dev, "Failed to allocate IRQs: %d\n",
+ wm831x->irq_base);
+ wm831x->irq_base = 0;
return 0;
}
- if (pdata->irq_cmos)
+ if (pdata && pdata->irq_cmos)
i = 0;
else
i = WM831X_IRQ_OD;
@@ -541,7 +563,6 @@
}
wm831x->irq = irq;
- wm831x->irq_base = pdata->irq_base;
/* Register them with genirq */
for (cur_irq = wm831x->irq_base;
diff --git a/drivers/mfd/wm8350-irq.c b/drivers/mfd/wm8350-irq.c
index ed4b22a..8a1fafd 100644
--- a/drivers/mfd/wm8350-irq.c
+++ b/drivers/mfd/wm8350-irq.c
@@ -473,17 +473,13 @@
{
int ret, cur_irq, i;
int flags = IRQF_ONESHOT;
+ int irq_base = -1;
if (!irq) {
dev_warn(wm8350->dev, "No interrupt support, no core IRQ\n");
return 0;
}
- if (!pdata || !pdata->irq_base) {
- dev_warn(wm8350->dev, "No interrupt support, no IRQ base\n");
- return 0;
- }
-
/* Mask top level interrupts */
wm8350_reg_write(wm8350, WM8350_SYSTEM_INTERRUPTS_MASK, 0xFFFF);
@@ -502,7 +498,17 @@
wm8350->chip_irq = irq;
wm8350->irq_base = pdata->irq_base;
- if (pdata->irq_high) {
+ if (pdata && pdata->irq_base > 0)
+ irq_base = pdata->irq_base;
+
+ wm8350->irq_base = irq_alloc_descs(irq_base, 0, ARRAY_SIZE(wm8350_irqs), 0);
+ if (wm8350->irq_base < 0) {
+ dev_warn(wm8350->dev, "Allocating irqs failed with %d\n",
+ wm8350->irq_base);
+ return 0;
+ }
+
+ if (pdata && pdata->irq_high) {
flags |= IRQF_TRIGGER_HIGH;
wm8350_set_bits(wm8350, WM8350_SYSTEM_CONTROL_1,
diff --git a/drivers/mfd/wm8994-core.c b/drivers/mfd/wm8994-core.c
index e198d40..96479c9 100644
--- a/drivers/mfd/wm8994-core.c
+++ b/drivers/mfd/wm8994-core.c
@@ -316,7 +316,7 @@
static int wm8994_resume(struct device *dev)
{
struct wm8994 *wm8994 = dev_get_drvdata(dev);
- int ret;
+ int ret, i;
/* We may have lied to the PM core about suspending */
if (!wm8994->suspended)
@@ -329,10 +329,16 @@
return ret;
}
- ret = wm8994_write(wm8994, WM8994_INTERRUPT_STATUS_1_MASK,
- WM8994_NUM_IRQ_REGS * 2, &wm8994->irq_masks_cur);
- if (ret < 0)
- dev_err(dev, "Failed to restore interrupt masks: %d\n", ret);
+ /* Write register at a time as we use the cache on the CPU so store
+ * it in native endian.
+ */
+ for (i = 0; i < ARRAY_SIZE(wm8994->irq_masks_cur); i++) {
+ ret = wm8994_reg_write(wm8994, WM8994_INTERRUPT_STATUS_1_MASK
+ + i, wm8994->irq_masks_cur[i]);
+ if (ret < 0)
+ dev_err(dev, "Failed to restore interrupt masks: %d\n",
+ ret);
+ }
ret = wm8994_write(wm8994, WM8994_LDO_1, WM8994_NUM_LDO_REGS * 2,
&wm8994->ldo_regs);
@@ -403,7 +409,7 @@
break;
default:
BUG();
- return -EINVAL;
+ goto err;
}
wm8994->supplies = kzalloc(sizeof(struct regulator_bulk_data) *
@@ -425,7 +431,7 @@
break;
default:
BUG();
- return -EINVAL;
+ goto err;
}
ret = regulator_bulk_get(wm8994->dev, wm8994->num_supplies,
@@ -476,13 +482,18 @@
goto err_enable;
}
- switch (ret) {
- case 0:
- case 1:
- if (wm8994->type == WM8994)
+ switch (wm8994->type) {
+ case WM8994:
+ switch (ret) {
+ case 0:
+ case 1:
dev_warn(wm8994->dev,
"revision %c not fully supported\n",
'A' + ret);
+ break;
+ default:
+ break;
+ }
break;
default:
break;
diff --git a/drivers/mfd/wm8994-irq.c b/drivers/mfd/wm8994-irq.c
index 71c6e8f..d682f7b 100644
--- a/drivers/mfd/wm8994-irq.c
+++ b/drivers/mfd/wm8994-irq.c
@@ -231,12 +231,6 @@
status[i] &= ~wm8994->irq_masks_cur[i];
}
- /* Report */
- for (i = 0; i < ARRAY_SIZE(wm8994_irqs); i++) {
- if (status[wm8994_irqs[i].reg - 1] & wm8994_irqs[i].mask)
- handle_nested_irq(wm8994->irq_base + i);
- }
-
/* Ack any unmasked IRQs */
for (i = 0; i < ARRAY_SIZE(status); i++) {
if (status[i])
@@ -244,6 +238,12 @@
status[i]);
}
+ /* Report */
+ for (i = 0; i < ARRAY_SIZE(wm8994_irqs); i++) {
+ if (status[wm8994_irqs[i].reg - 1] & wm8994_irqs[i].mask)
+ handle_nested_irq(wm8994->irq_base + i);
+ }
+
return IRQ_HANDLED;
}
diff --git a/drivers/misc/cb710/core.c b/drivers/misc/cb710/core.c
index efec413..68cd05b 100644
--- a/drivers/misc/cb710/core.c
+++ b/drivers/misc/cb710/core.c
@@ -33,7 +33,7 @@
static int __devinit cb710_pci_configure(struct pci_dev *pdev)
{
unsigned int devfn = PCI_DEVFN(PCI_SLOT(pdev->devfn), 0);
- struct pci_dev *pdev0 = pci_get_slot(pdev->bus, devfn);
+ struct pci_dev *pdev0;
u32 val;
cb710_pci_update_config_reg(pdev, 0x48,
@@ -43,6 +43,7 @@
if (val & 0x80000000)
return 0;
+ pdev0 = pci_get_slot(pdev->bus, devfn);
if (!pdev0)
return -ENODEV;
diff --git a/drivers/mmc/card/mmc_test.c b/drivers/mmc/card/mmc_test.c
index 006a5e9..2bf229a 100644
--- a/drivers/mmc/card/mmc_test.c
+++ b/drivers/mmc/card/mmc_test.c
@@ -224,7 +224,7 @@
static int mmc_test_busy(struct mmc_command *cmd)
{
return !(cmd->resp[0] & R1_READY_FOR_DATA) ||
- (R1_CURRENT_STATE(cmd->resp[0]) == 7);
+ (R1_CURRENT_STATE(cmd->resp[0]) == R1_STATE_PRG);
}
/*
@@ -2900,7 +2900,7 @@
.release = single_release,
};
-static void mmc_test_free_file_test(struct mmc_card *card)
+static void mmc_test_free_dbgfs_file(struct mmc_card *card)
{
struct mmc_test_dbgfs_file *df, *dfs;
@@ -2917,34 +2917,21 @@
mutex_unlock(&mmc_test_lock);
}
-static int mmc_test_register_file_test(struct mmc_card *card)
+static int __mmc_test_register_dbgfs_file(struct mmc_card *card,
+ const char *name, mode_t mode, const struct file_operations *fops)
{
struct dentry *file = NULL;
struct mmc_test_dbgfs_file *df;
- int ret = 0;
-
- mutex_lock(&mmc_test_lock);
if (card->debugfs_root)
- file = debugfs_create_file("test", S_IWUSR | S_IRUGO,
- card->debugfs_root, card, &mmc_test_fops_test);
+ file = debugfs_create_file(name, mode, card->debugfs_root,
+ card, fops);
if (IS_ERR_OR_NULL(file)) {
dev_err(&card->dev,
- "Can't create test. Perhaps debugfs is disabled.\n");
- ret = -ENODEV;
- goto err;
- }
-
- if (card->debugfs_root)
- file = debugfs_create_file("testlist", S_IRUGO,
- card->debugfs_root, card, &mmc_test_fops_testlist);
-
- if (IS_ERR_OR_NULL(file)) {
- dev_err(&card->dev,
- "Can't create testlist. Perhaps debugfs is disabled.\n");
- ret = -ENODEV;
- goto err;
+ "Can't create %s. Perhaps debugfs is disabled.\n",
+ name);
+ return -ENODEV;
}
df = kmalloc(sizeof(struct mmc_test_dbgfs_file), GFP_KERNEL);
@@ -2952,14 +2939,31 @@
debugfs_remove(file);
dev_err(&card->dev,
"Can't allocate memory for internal usage.\n");
- ret = -ENOMEM;
- goto err;
+ return -ENOMEM;
}
df->card = card;
df->file = file;
list_add(&df->link, &mmc_test_file_test);
+ return 0;
+}
+
+static int mmc_test_register_dbgfs_file(struct mmc_card *card)
+{
+ int ret;
+
+ mutex_lock(&mmc_test_lock);
+
+ ret = __mmc_test_register_dbgfs_file(card, "test", S_IWUSR | S_IRUGO,
+ &mmc_test_fops_test);
+ if (ret)
+ goto err;
+
+ ret = __mmc_test_register_dbgfs_file(card, "testlist", S_IRUGO,
+ &mmc_test_fops_testlist);
+ if (ret)
+ goto err;
err:
mutex_unlock(&mmc_test_lock);
@@ -2974,7 +2978,7 @@
if (!mmc_card_mmc(card) && !mmc_card_sd(card))
return -ENODEV;
- ret = mmc_test_register_file_test(card);
+ ret = mmc_test_register_dbgfs_file(card);
if (ret)
return ret;
@@ -2986,7 +2990,7 @@
static void mmc_test_remove(struct mmc_card *card)
{
mmc_test_free_result(card);
- mmc_test_free_file_test(card);
+ mmc_test_free_dbgfs_file(card);
}
static struct mmc_driver mmc_driver = {
@@ -3006,7 +3010,7 @@
{
/* Clear stalled data if card is still plugged */
mmc_test_free_result(NULL);
- mmc_test_free_file_test(NULL);
+ mmc_test_free_dbgfs_file(NULL);
mmc_unregister_driver(&mmc_driver);
}
diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c
index 89bdeae..91a0a74 100644
--- a/drivers/mmc/core/core.c
+++ b/drivers/mmc/core/core.c
@@ -1502,7 +1502,7 @@
goto out;
}
} while (!(cmd.resp[0] & R1_READY_FOR_DATA) ||
- R1_CURRENT_STATE(cmd.resp[0]) == 7);
+ R1_CURRENT_STATE(cmd.resp[0]) == R1_STATE_PRG);
out:
return err;
}
diff --git a/drivers/mmc/core/mmc.c b/drivers/mmc/core/mmc.c
index aa7d1d7..5700b1c 100644
--- a/drivers/mmc/core/mmc.c
+++ b/drivers/mmc/core/mmc.c
@@ -259,7 +259,7 @@
}
card->ext_csd.rev = ext_csd[EXT_CSD_REV];
- if (card->ext_csd.rev > 5) {
+ if (card->ext_csd.rev > 6) {
printk(KERN_ERR "%s: unrecognised EXT_CSD revision %d\n",
mmc_hostname(card->host), card->ext_csd.rev);
err = -EINVAL;
diff --git a/drivers/mmc/core/mmc_ops.c b/drivers/mmc/core/mmc_ops.c
index 845ce7c..770c3d0 100644
--- a/drivers/mmc/core/mmc_ops.c
+++ b/drivers/mmc/core/mmc_ops.c
@@ -407,7 +407,7 @@
break;
if (mmc_host_is_spi(card->host))
break;
- } while (R1_CURRENT_STATE(status) == 7);
+ } while (R1_CURRENT_STATE(status) == R1_STATE_PRG);
if (mmc_host_is_spi(card->host)) {
if (status & R1_SPI_ILLEGAL_COMMAND)
diff --git a/drivers/mmc/host/dw_mmc.c b/drivers/mmc/host/dw_mmc.c
index 77f0b6b..ff0f714 100644
--- a/drivers/mmc/host/dw_mmc.c
+++ b/drivers/mmc/host/dw_mmc.c
@@ -62,7 +62,7 @@
u32 des1; /* Buffer sizes */
#define IDMAC_SET_BUFFER1_SIZE(d, s) \
- ((d)->des1 = ((d)->des1 & 0x03ffc000) | ((s) & 0x3fff))
+ ((d)->des1 = ((d)->des1 & 0x03ffe000) | ((s) & 0x1fff))
u32 des2; /* buffer 1 physical address */
@@ -699,7 +699,7 @@
}
/* DDR mode set */
- if (ios->ddr) {
+ if (ios->timing == MMC_TIMING_UHS_DDR50) {
regs = mci_readl(slot->host, UHS_REG);
regs |= (0x1 << slot->id) << 16;
mci_writel(slot->host, UHS_REG, regs);
@@ -1646,7 +1646,7 @@
mmc->caps |= MMC_CAP_4_BIT_DATA;
if (host->pdata->quirks & DW_MCI_QUIRK_HIGHSPEED)
- mmc->caps |= MMC_CAP_SD_HIGHSPEED;
+ mmc->caps |= MMC_CAP_SD_HIGHSPEED | MMC_CAP_MMC_HIGHSPEED;
#ifdef CONFIG_MMC_DW_IDMAC
mmc->max_segs = host->ring_size;
diff --git a/drivers/mmc/host/sdhci-esdhc-imx.c b/drivers/mmc/host/sdhci-esdhc-imx.c
index 9ebfb4b..0e9780f 100644
--- a/drivers/mmc/host/sdhci-esdhc-imx.c
+++ b/drivers/mmc/host/sdhci-esdhc-imx.c
@@ -27,6 +27,7 @@
#include "sdhci-pltfm.h"
#include "sdhci-esdhc.h"
+#define SDHCI_CTRL_D3CD 0x08
/* VENDOR SPEC register */
#define SDHCI_VENDOR_SPEC 0xC0
#define SDHCI_VENDOR_SPEC_SDIO_QUIRK 0x00000002
@@ -141,13 +142,32 @@
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
struct pltfm_imx_data *imx_data = pltfm_host->priv;
struct esdhc_platform_data *boarddata = &imx_data->boarddata;
+ u32 data;
- if (unlikely((reg == SDHCI_INT_ENABLE || reg == SDHCI_SIGNAL_ENABLE)
- && (boarddata->cd_type == ESDHC_CD_GPIO)))
- /*
- * these interrupts won't work with a custom card_detect gpio
- */
- val &= ~(SDHCI_INT_CARD_REMOVE | SDHCI_INT_CARD_INSERT);
+ if (unlikely(reg == SDHCI_INT_ENABLE || reg == SDHCI_SIGNAL_ENABLE)) {
+ if (boarddata->cd_type == ESDHC_CD_GPIO)
+ /*
+ * These interrupts won't work with a custom
+ * card_detect gpio (only applied to mx25/35)
+ */
+ val &= ~(SDHCI_INT_CARD_REMOVE | SDHCI_INT_CARD_INSERT);
+
+ if (val & SDHCI_INT_CARD_INT) {
+ /*
+ * Clear and then set D3CD bit to avoid missing the
+ * card interrupt. This is a eSDHC controller problem
+ * so we need to apply the following workaround: clear
+ * and set D3CD bit will make eSDHC re-sample the card
+ * interrupt. In case a card interrupt was lost,
+ * re-sample it by the following steps.
+ */
+ data = readl(host->ioaddr + SDHCI_HOST_CONTROL);
+ data &= ~SDHCI_CTRL_D3CD;
+ writel(data, host->ioaddr + SDHCI_HOST_CONTROL);
+ data |= SDHCI_CTRL_D3CD;
+ writel(data, host->ioaddr + SDHCI_HOST_CONTROL);
+ }
+ }
if (unlikely((imx_data->flags & ESDHC_FLAG_MULTIBLK_NO_INT)
&& (reg == SDHCI_INT_STATUS)
@@ -217,8 +237,10 @@
*/
return;
case SDHCI_HOST_CONTROL:
- /* FSL messed up here, so we can just keep those two */
- new_val = val & (SDHCI_CTRL_LED | SDHCI_CTRL_4BITBUS);
+ /* FSL messed up here, so we can just keep those three */
+ new_val = val & (SDHCI_CTRL_LED | \
+ SDHCI_CTRL_4BITBUS | \
+ SDHCI_CTRL_D3CD);
/* ensure the endianess */
new_val |= ESDHC_HOST_CONTROL_LE;
/* DMA mode bits are shifted */
diff --git a/drivers/mmc/host/sdhci-pxav3.c b/drivers/mmc/host/sdhci-pxav3.c
index 4198dbb..fc7e4a5 100644
--- a/drivers/mmc/host/sdhci-pxav3.c
+++ b/drivers/mmc/host/sdhci-pxav3.c
@@ -195,7 +195,8 @@
clk_enable(clk);
host->quirks = SDHCI_QUIRK_BROKEN_TIMEOUT_VAL
- | SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC;
+ | SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC
+ | SDHCI_QUIRK_32BIT_ADMA_SIZE;
/* enable 1/8V DDR capable */
host->mmc->caps |= MMC_CAP_1_8V_DDR;
diff --git a/drivers/mmc/host/sdhci-s3c.c b/drivers/mmc/host/sdhci-s3c.c
index 460ffaf..2bd7bf4 100644
--- a/drivers/mmc/host/sdhci-s3c.c
+++ b/drivers/mmc/host/sdhci-s3c.c
@@ -19,6 +19,7 @@
#include <linux/clk.h>
#include <linux/io.h>
#include <linux/gpio.h>
+#include <linux/module.h>
#include <linux/mmc/host.h>
@@ -502,6 +503,9 @@
/* This host supports the Auto CMD12 */
host->quirks |= SDHCI_QUIRK_MULTIBLOCK_READ_ACMD12;
+ /* Samsung SoCs need BROKEN_ADMA_ZEROLEN_DESC */
+ host->quirks |= SDHCI_QUIRK_BROKEN_ADMA_ZEROLEN_DESC;
+
if (pdata->cd_type == S3C_SDHCI_CD_NONE ||
pdata->cd_type == S3C_SDHCI_CD_PERMANENT)
host->quirks |= SDHCI_QUIRK_BROKEN_CARD_DETECTION;
diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c
index c31a334..0e02cc1 100644
--- a/drivers/mmc/host/sdhci.c
+++ b/drivers/mmc/host/sdhci.c
@@ -628,12 +628,11 @@
/* timeout in us */
if (!data)
target_timeout = cmd->cmd_timeout_ms * 1000;
- else
- target_timeout = data->timeout_ns / 1000 +
- data->timeout_clks / host->clock;
-
- if (host->quirks & SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK)
- host->timeout_clk = host->clock / 1000;
+ else {
+ target_timeout = data->timeout_ns / 1000;
+ if (host->clock)
+ target_timeout += data->timeout_clks / host->clock;
+ }
/*
* Figure out needed cycles.
@@ -645,7 +644,6 @@
* =>
* (1) / (2) > 2^6
*/
- BUG_ON(!host->timeout_clk);
count = 0;
current_timeout = (1 << 13) * 1000 / host->timeout_clk;
while (current_timeout < target_timeout) {
@@ -1867,9 +1865,6 @@
del_timer(&host->timer);
- if (host->version >= SDHCI_SPEC_300)
- del_timer(&host->tuning_timer);
-
mrq = host->mrq;
/*
@@ -2461,22 +2456,6 @@
host->max_clk = host->ops->get_max_clock(host);
}
- host->timeout_clk =
- (caps[0] & SDHCI_TIMEOUT_CLK_MASK) >> SDHCI_TIMEOUT_CLK_SHIFT;
- if (host->timeout_clk == 0) {
- if (host->ops->get_timeout_clock) {
- host->timeout_clk = host->ops->get_timeout_clock(host);
- } else if (!(host->quirks &
- SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK)) {
- printk(KERN_ERR
- "%s: Hardware doesn't specify timeout clock "
- "frequency.\n", mmc_hostname(mmc));
- return -ENODEV;
- }
- }
- if (caps[0] & SDHCI_TIMEOUT_CLK_UNIT)
- host->timeout_clk *= 1000;
-
/*
* In case of Host Controller v3.00, find out whether clock
* multiplier is supported.
@@ -2509,10 +2488,26 @@
} else
mmc->f_min = host->max_clk / SDHCI_MAX_DIV_SPEC_200;
+ host->timeout_clk =
+ (caps[0] & SDHCI_TIMEOUT_CLK_MASK) >> SDHCI_TIMEOUT_CLK_SHIFT;
+ if (host->timeout_clk == 0) {
+ if (host->ops->get_timeout_clock) {
+ host->timeout_clk = host->ops->get_timeout_clock(host);
+ } else if (!(host->quirks &
+ SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK)) {
+ printk(KERN_ERR
+ "%s: Hardware doesn't specify timeout clock "
+ "frequency.\n", mmc_hostname(mmc));
+ return -ENODEV;
+ }
+ }
+ if (caps[0] & SDHCI_TIMEOUT_CLK_UNIT)
+ host->timeout_clk *= 1000;
+
if (host->quirks & SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK)
- mmc->max_discard_to = (1 << 27) / (mmc->f_max / 1000);
- else
- mmc->max_discard_to = (1 << 27) / host->timeout_clk;
+ host->timeout_clk = mmc->f_max / 1000;
+
+ mmc->max_discard_to = (1 << 27) / host->timeout_clk;
mmc->caps |= MMC_CAP_SDIO_IRQ | MMC_CAP_ERASE | MMC_CAP_CMD23;
diff --git a/drivers/mmc/host/tmio_mmc.c b/drivers/mmc/host/tmio_mmc.c
index 8d185de..44a9668 100644
--- a/drivers/mmc/host/tmio_mmc.c
+++ b/drivers/mmc/host/tmio_mmc.c
@@ -27,7 +27,6 @@
static int tmio_mmc_suspend(struct platform_device *dev, pm_message_t state)
{
const struct mfd_cell *cell = mfd_get_cell(dev);
- struct mmc_host *mmc = platform_get_drvdata(dev);
int ret;
ret = tmio_mmc_host_suspend(&dev->dev);
@@ -42,7 +41,6 @@
static int tmio_mmc_resume(struct platform_device *dev)
{
const struct mfd_cell *cell = mfd_get_cell(dev);
- struct mmc_host *mmc = platform_get_drvdata(dev);
int ret = 0;
/* Tell the MFD core we are ready to be enabled */
diff --git a/drivers/net/bnx2x/bnx2x_cmn.c b/drivers/net/bnx2x/bnx2x_cmn.c
index 5b0dba6..37e5790 100644
--- a/drivers/net/bnx2x/bnx2x_cmn.c
+++ b/drivers/net/bnx2x/bnx2x_cmn.c
@@ -63,8 +63,9 @@
fp->disable_tpa = ((bp->flags & TPA_ENABLE_FLAG) == 0);
#ifdef BCM_CNIC
- /* We don't want TPA on FCoE, FWD and OOO L2 rings */
- bnx2x_fcoe(bp, disable_tpa) = 1;
+ /* We don't want TPA on an FCoE L2 ring */
+ if (IS_FCOE_FP(fp))
+ fp->disable_tpa = 1;
#endif
}
@@ -1404,10 +1405,9 @@
u16 bnx2x_select_queue(struct net_device *dev, struct sk_buff *skb)
{
struct bnx2x *bp = netdev_priv(dev);
+
#ifdef BCM_CNIC
- if (NO_FCOE(bp))
- return skb_tx_hash(dev, skb);
- else {
+ if (!NO_FCOE(bp)) {
struct ethhdr *hdr = (struct ethhdr *)skb->data;
u16 ether_type = ntohs(hdr->h_proto);
@@ -1424,8 +1424,7 @@
return bnx2x_fcoe_tx(bp, txq_index);
}
#endif
- /* Select a none-FCoE queue: if FCoE is enabled, exclude FCoE L2 ring
- */
+ /* select a non-FCoE queue */
return __skb_tx_hash(dev, skb, BNX2X_NUM_ETH_QUEUES(bp));
}
@@ -1448,6 +1447,28 @@
bp->num_queues += NON_ETH_CONTEXT_USE;
}
+/**
+ * bnx2x_set_real_num_queues - configure netdev->real_num_[tx,rx]_queues
+ *
+ * @bp: Driver handle
+ *
+ * We currently support for at most 16 Tx queues for each CoS thus we will
+ * allocate a multiple of 16 for ETH L2 rings according to the value of the
+ * bp->max_cos.
+ *
+ * If there is an FCoE L2 queue the appropriate Tx queue will have the next
+ * index after all ETH L2 indices.
+ *
+ * If the actual number of Tx queues (for each CoS) is less than 16 then there
+ * will be the holes at the end of each group of 16 ETh L2 indices (0..15,
+ * 16..31,...) with indicies that are not coupled with any real Tx queue.
+ *
+ * The proper configuration of skb->queue_mapping is handled by
+ * bnx2x_select_queue() and __skb_tx_hash().
+ *
+ * bnx2x_setup_tc() takes care of the proper TC mappings so that __skb_tx_hash()
+ * will return a proper Tx index if TC is enabled (netdev->num_tc > 0).
+ */
static inline int bnx2x_set_real_num_queues(struct bnx2x *bp)
{
int rc, tx, rx;
@@ -1989,14 +2010,20 @@
return -EINVAL;
}
+ /*
+ * It's important to set the bp->state to the value different from
+ * BNX2X_STATE_OPEN and only then stop the Tx. Otherwise bnx2x_tx_int()
+ * may restart the Tx from the NAPI context (see bnx2x_tx_int()).
+ */
+ bp->state = BNX2X_STATE_CLOSING_WAIT4_HALT;
+ smp_mb();
+
/* Stop Tx */
bnx2x_tx_disable(bp);
#ifdef BCM_CNIC
bnx2x_cnic_notify(bp, CNIC_CTL_STOP_CMD);
#endif
- bp->state = BNX2X_STATE_CLOSING_WAIT4_HALT;
- smp_mb();
bp->rx_mode = BNX2X_RX_MODE_NONE;
diff --git a/drivers/net/bnx2x/bnx2x_dcb.c b/drivers/net/bnx2x/bnx2x_dcb.c
index a4ea35f..a1e004a 100644
--- a/drivers/net/bnx2x/bnx2x_dcb.c
+++ b/drivers/net/bnx2x/bnx2x_dcb.c
@@ -920,7 +920,7 @@
void bnx2x_dcbx_set_state(struct bnx2x *bp, bool dcb_on, u32 dcbx_enabled)
{
- if (!CHIP_IS_E1x(bp)) {
+ if (!CHIP_IS_E1x(bp) && !CHIP_IS_E3(bp)) {
bp->dcb_state = dcb_on;
bp->dcbx_enabled = dcbx_enabled;
} else {
diff --git a/drivers/net/bnx2x/bnx2x_hsi.h b/drivers/net/bnx2x/bnx2x_hsi.h
index 06727f3..dc24de4 100644
--- a/drivers/net/bnx2x/bnx2x_hsi.h
+++ b/drivers/net/bnx2x/bnx2x_hsi.h
@@ -1204,6 +1204,8 @@
#define LINK_STATUS_PFC_ENABLED 0x20000000
+ #define LINK_STATUS_PHYSICAL_LINK_FLAG 0x40000000
+
u32 port_stx;
u32 stat_nig_timer;
diff --git a/drivers/net/bnx2x/bnx2x_link.c b/drivers/net/bnx2x/bnx2x_link.c
index bcd8f00..d45b155 100644
--- a/drivers/net/bnx2x/bnx2x_link.c
+++ b/drivers/net/bnx2x/bnx2x_link.c
@@ -1546,6 +1546,12 @@
vars->line_speed);
break;
}
+ if (!(vars->flow_ctrl & BNX2X_FLOW_CTRL_TX))
+ val |= UMAC_COMMAND_CONFIG_REG_IGNORE_TX_PAUSE;
+
+ if (!(vars->flow_ctrl & BNX2X_FLOW_CTRL_RX))
+ val |= UMAC_COMMAND_CONFIG_REG_PAUSE_IGNORE;
+
REG_WR(bp, umac_base + UMAC_REG_COMMAND_CONFIG, val);
udelay(50);
@@ -1661,10 +1667,20 @@
{
u8 port = params->port;
struct bnx2x *bp = params->bp;
- u32 xmac_base = (port) ? GRCBASE_XMAC1 : GRCBASE_XMAC0;
+ u32 pfc_ctrl, xmac_base = (port) ? GRCBASE_XMAC1 : GRCBASE_XMAC0;
if (REG_RD(bp, MISC_REG_RESET_REG_2) &
MISC_REGISTERS_RESET_REG_2_XMAC) {
+ /*
+ * Send an indication to change the state in the NIG back to XON
+ * Clearing this bit enables the next set of this bit to get
+ * rising edge
+ */
+ pfc_ctrl = REG_RD(bp, xmac_base + XMAC_REG_PFC_CTRL_HI);
+ REG_WR(bp, xmac_base + XMAC_REG_PFC_CTRL_HI,
+ (pfc_ctrl & ~(1<<1)));
+ REG_WR(bp, xmac_base + XMAC_REG_PFC_CTRL_HI,
+ (pfc_ctrl | (1<<1)));
DP(NETIF_MSG_LINK, "Disable XMAC on port %x\n", port);
REG_WR(bp, xmac_base + XMAC_REG_CTRL, 0);
usleep_range(1000, 1000);
@@ -1729,6 +1745,10 @@
DP(NETIF_MSG_LINK, "enabling EMAC\n");
+ /* Disable BMAC */
+ REG_WR(bp, GRCBASE_MISC + MISC_REGISTERS_RESET_REG_2_CLEAR,
+ (MISC_REGISTERS_RESET_REG_2_RST_BMAC0 << port));
+
/* enable emac and not bmac */
REG_WR(bp, NIG_REG_EGRESS_EMAC0_PORT + port*4, 1);
@@ -2583,12 +2603,6 @@
REG_WR_DMAE(bp, bmac_addr + BIGMAC_REGISTER_RX_LLFC_MSG_FLDS,
wb_data, 2);
- if (vars->phy_flags & PHY_TX_ERROR_CHECK_FLAG) {
- REG_RD_DMAE(bp, bmac_addr + BIGMAC_REGISTER_RX_LSS_STATUS,
- wb_data, 2);
- if (wb_data[0] > 0)
- return -ESRCH;
- }
return 0;
}
@@ -2654,16 +2668,6 @@
udelay(30);
bnx2x_update_pfc_bmac2(params, vars, is_lb);
- if (vars->phy_flags & PHY_TX_ERROR_CHECK_FLAG) {
- REG_RD_DMAE(bp, bmac_addr + BIGMAC2_REGISTER_RX_LSS_STAT,
- wb_data, 2);
- if (wb_data[0] > 0) {
- DP(NETIF_MSG_LINK, "Got bad LSS status 0x%x\n",
- wb_data[0]);
- return -ESRCH;
- }
- }
-
return 0;
}
@@ -2949,7 +2953,9 @@
u32 val;
u16 i;
int rc = 0;
-
+ if (phy->flags & FLAGS_MDC_MDIO_WA_B0)
+ bnx2x_bits_en(bp, phy->mdio_ctrl + EMAC_REG_EMAC_MDIO_STATUS,
+ EMAC_MDIO_STATUS_10MB);
/* address */
val = ((phy->addr << 21) | (devad << 16) | reg |
EMAC_MDIO_COMM_COMMAND_ADDRESS |
@@ -3003,6 +3009,9 @@
}
}
+ if (phy->flags & FLAGS_MDC_MDIO_WA_B0)
+ bnx2x_bits_dis(bp, phy->mdio_ctrl + EMAC_REG_EMAC_MDIO_STATUS,
+ EMAC_MDIO_STATUS_10MB);
return rc;
}
@@ -3012,6 +3021,9 @@
u32 tmp;
u8 i;
int rc = 0;
+ if (phy->flags & FLAGS_MDC_MDIO_WA_B0)
+ bnx2x_bits_en(bp, phy->mdio_ctrl + EMAC_REG_EMAC_MDIO_STATUS,
+ EMAC_MDIO_STATUS_10MB);
/* address */
@@ -3065,7 +3077,9 @@
bnx2x_cl45_read(bp, phy, devad, 0xf, &temp_val);
}
}
-
+ if (phy->flags & FLAGS_MDC_MDIO_WA_B0)
+ bnx2x_bits_dis(bp, phy->mdio_ctrl + EMAC_REG_EMAC_MDIO_STATUS,
+ EMAC_MDIO_STATUS_10MB);
return rc;
}
@@ -4353,6 +4367,9 @@
vars->link_up = (vars->link_status & LINK_STATUS_LINK_UP);
vars->phy_flags = PHY_XGXS_FLAG;
+ if (vars->link_status & LINK_STATUS_PHYSICAL_LINK_FLAG)
+ vars->phy_flags |= PHY_PHYSICAL_LINK_FLAG;
+
if (vars->link_up) {
DP(NETIF_MSG_LINK, "phy link up\n");
@@ -4444,6 +4461,8 @@
/* indicate no mac active */
vars->mac_type = MAC_TYPE_NONE;
+ if (vars->link_status & LINK_STATUS_PHYSICAL_LINK_FLAG)
+ vars->phy_flags |= PHY_HALF_OPEN_CONN_FLAG;
}
/* Sync media type */
@@ -5903,20 +5922,30 @@
tmp = EMAC_RD(bp, EMAC_REG_EMAC_LED);
EMAC_WR(bp, EMAC_REG_EMAC_LED,
(tmp | EMAC_LED_OVERRIDE));
- return rc;
+ /*
+ * return here without enabling traffic
+ * LED blink andsetting rate in ON mode.
+ * In oper mode, enabling LED blink
+ * and setting rate is needed.
+ */
+ if (mode == LED_MODE_ON)
+ return rc;
}
- } else if (SINGLE_MEDIA_DIRECT(params) &&
- (CHIP_IS_E1x(bp) ||
- CHIP_IS_E2(bp))) {
+ } else if (SINGLE_MEDIA_DIRECT(params)) {
/*
* This is a work-around for HW issue found when link
* is up in CL73
*/
- REG_WR(bp, NIG_REG_LED_MODE_P0 + port*4, 0);
REG_WR(bp, NIG_REG_LED_10G_P0 + port*4, 1);
- } else {
+ if (CHIP_IS_E1x(bp) ||
+ CHIP_IS_E2(bp) ||
+ (mode == LED_MODE_ON))
+ REG_WR(bp, NIG_REG_LED_MODE_P0 + port*4, 0);
+ else
+ REG_WR(bp, NIG_REG_LED_MODE_P0 + port*4,
+ hw_led_mode);
+ } else
REG_WR(bp, NIG_REG_LED_MODE_P0 + port*4, hw_led_mode);
- }
REG_WR(bp, NIG_REG_LED_CONTROL_OVERRIDE_TRAFFIC_P0 + port*4, 0);
/* Set blinking rate to ~15.9Hz */
@@ -6160,6 +6189,7 @@
/* update shared memory */
vars->link_status &= ~(LINK_STATUS_SPEED_AND_DUPLEX_MASK |
LINK_STATUS_LINK_UP |
+ LINK_STATUS_PHYSICAL_LINK_FLAG |
LINK_STATUS_AUTO_NEGOTIATE_COMPLETE |
LINK_STATUS_RX_FLOW_CONTROL_FLAG_MASK |
LINK_STATUS_TX_FLOW_CONTROL_FLAG_MASK |
@@ -6197,7 +6227,8 @@
u8 port = params->port;
int rc = 0;
- vars->link_status |= LINK_STATUS_LINK_UP;
+ vars->link_status |= (LINK_STATUS_LINK_UP |
+ LINK_STATUS_PHYSICAL_LINK_FLAG);
vars->phy_flags |= PHY_PHYSICAL_LINK_FLAG;
if (vars->flow_ctrl & BNX2X_FLOW_CTRL_TX)
@@ -7998,6 +8029,9 @@
bnx2x_cl45_read(bp, phy, MDIO_WC_DEVAD,
MDIO_WC_REG_UC_INFO_B1_FIRMWARE_MODE, &val);
+ /* Restart microcode to re-read the new mode */
+ bnx2x_warpcore_reset_lane(bp, phy, 1);
+ bnx2x_warpcore_reset_lane(bp, phy, 0);
}
@@ -8116,7 +8150,6 @@
offsetof(struct shmem_region, dev_info.
port_feature_config[params->port].
config));
-
bnx2x_set_gpio_int(bp, gpio_num,
MISC_REGISTERS_GPIO_INT_OUTPUT_SET,
gpio_port);
@@ -8125,8 +8158,9 @@
* Disable transmit for this module
*/
phy->media_type = ETH_PHY_NOT_PRESENT;
- if ((val & PORT_FEAT_CFG_OPT_MDL_ENFRCMNT_MASK) ==
- PORT_FEAT_CFG_OPT_MDL_ENFRCMNT_DISABLE_TX_LASER)
+ if (((val & PORT_FEAT_CFG_OPT_MDL_ENFRCMNT_MASK) ==
+ PORT_FEAT_CFG_OPT_MDL_ENFRCMNT_DISABLE_TX_LASER) ||
+ CHIP_IS_E3(bp))
bnx2x_sfp_set_transmitter(params, phy, 0);
}
}
@@ -8228,9 +8262,6 @@
u16 cnt, val, tmp1;
struct bnx2x *bp = params->bp;
- /* SPF+ PHY: Set flag to check for Tx error */
- vars->phy_flags = PHY_TX_ERROR_CHECK_FLAG;
-
bnx2x_set_gpio(bp, MISC_REGISTERS_GPIO_2,
MISC_REGISTERS_GPIO_OUTPUT_HIGH, params->port);
/* HW reset */
@@ -8414,9 +8445,6 @@
struct bnx2x *bp = params->bp;
DP(NETIF_MSG_LINK, "Initializing BCM8726\n");
- /* SPF+ PHY: Set flag to check for Tx error */
- vars->phy_flags = PHY_TX_ERROR_CHECK_FLAG;
-
bnx2x_cl45_write(bp, phy, MDIO_PMA_DEVAD, MDIO_PMA_REG_CTRL, 1<<15);
bnx2x_wait_reset_complete(bp, phy, params);
@@ -8585,9 +8613,6 @@
struct bnx2x *bp = params->bp;
/* Enable PMD link, MOD_ABS_FLT, and 1G link alarm */
- /* SPF+ PHY: Set flag to check for Tx error */
- vars->phy_flags = PHY_TX_ERROR_CHECK_FLAG;
-
bnx2x_wait_reset_complete(bp, phy, params);
rx_alarm_ctrl_val = (1<<2) | (1<<5) ;
/* Should be 0x6 to enable XS on Tx side. */
@@ -9243,7 +9268,13 @@
if (phy->req_duplex == DUPLEX_FULL)
autoneg_val |= (1<<8);
- bnx2x_cl45_write(bp, phy,
+ /*
+ * Always write this if this is not 84833.
+ * For 84833, write it only when it's a forced speed.
+ */
+ if ((phy->type != PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM84833) ||
+ ((autoneg_val & (1<<12)) == 0))
+ bnx2x_cl45_write(bp, phy,
MDIO_AN_DEVAD,
MDIO_AN_REG_8481_LEGACY_MII_CTRL, autoneg_val);
@@ -9257,13 +9288,12 @@
bnx2x_cl45_write(bp, phy,
MDIO_AN_DEVAD, MDIO_AN_REG_CTRL,
0x3200);
- } else if (phy->req_line_speed != SPEED_10 &&
- phy->req_line_speed != SPEED_100) {
+ } else
bnx2x_cl45_write(bp, phy,
MDIO_AN_DEVAD,
MDIO_AN_REG_8481_10GBASE_T_AN_CTRL,
1);
- }
+
/* Save spirom version */
bnx2x_save_848xx_spirom_version(phy, params);
@@ -9756,11 +9786,9 @@
bnx2x_cl45_read(bp, phy,
MDIO_CTL_DEVAD,
0x400f, &val16);
- /* Put to low power mode on newer FW */
- if ((val16 & 0x303f) > 0x1009)
- bnx2x_cl45_write(bp, phy,
- MDIO_PMA_DEVAD,
- MDIO_PMA_REG_CTRL, 0x800);
+ bnx2x_cl45_write(bp, phy,
+ MDIO_PMA_DEVAD,
+ MDIO_PMA_REG_CTRL, 0x800);
}
}
@@ -10191,8 +10219,15 @@
u32 cfg_pin;
u8 port;
- /* This works with E3 only, no need to check the chip
- before determining the port. */
+ /*
+ * In case of no EPIO routed to reset the GPHY, put it
+ * in low power mode.
+ */
+ bnx2x_cl22_write(bp, phy, MDIO_PMA_REG_CTRL, 0x800);
+ /*
+ * This works with E3 only, no need to check the chip
+ * before determining the port.
+ */
port = params->port;
cfg_pin = (REG_RD(bp, params->shmem_base +
offsetof(struct shmem_region,
@@ -10603,7 +10638,8 @@
.type = PORT_HW_CFG_XGXS_EXT_PHY_TYPE_DIRECT,
.addr = 0xff,
.def_md_devad = 0,
- .flags = FLAGS_HW_LOCK_REQUIRED,
+ .flags = (FLAGS_HW_LOCK_REQUIRED |
+ FLAGS_TX_ERROR_CHECK),
.rx_preemphasis = {0xffff, 0xffff, 0xffff, 0xffff},
.tx_preemphasis = {0xffff, 0xffff, 0xffff, 0xffff},
.mdio_ctrl = 0,
@@ -10729,7 +10765,8 @@
.type = PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8706,
.addr = 0xff,
.def_md_devad = 0,
- .flags = FLAGS_INIT_XGXS_FIRST,
+ .flags = (FLAGS_INIT_XGXS_FIRST |
+ FLAGS_TX_ERROR_CHECK),
.rx_preemphasis = {0xffff, 0xffff, 0xffff, 0xffff},
.tx_preemphasis = {0xffff, 0xffff, 0xffff, 0xffff},
.mdio_ctrl = 0,
@@ -10760,7 +10797,8 @@
.addr = 0xff,
.def_md_devad = 0,
.flags = (FLAGS_HW_LOCK_REQUIRED |
- FLAGS_INIT_XGXS_FIRST),
+ FLAGS_INIT_XGXS_FIRST |
+ FLAGS_TX_ERROR_CHECK),
.rx_preemphasis = {0xffff, 0xffff, 0xffff, 0xffff},
.tx_preemphasis = {0xffff, 0xffff, 0xffff, 0xffff},
.mdio_ctrl = 0,
@@ -10791,7 +10829,8 @@
.type = PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8727,
.addr = 0xff,
.def_md_devad = 0,
- .flags = FLAGS_FAN_FAILURE_DET_REQ,
+ .flags = (FLAGS_FAN_FAILURE_DET_REQ |
+ FLAGS_TX_ERROR_CHECK),
.rx_preemphasis = {0xffff, 0xffff, 0xffff, 0xffff},
.tx_preemphasis = {0xffff, 0xffff, 0xffff, 0xffff},
.mdio_ctrl = 0,
@@ -11112,6 +11151,8 @@
*/
if (CHIP_REV(bp) == CHIP_REV_Ax)
phy->flags |= FLAGS_MDC_MDIO_WA;
+ else
+ phy->flags |= FLAGS_MDC_MDIO_WA_B0;
} else {
switch (switch_cfg) {
case SWITCH_CFG_1G:
@@ -11500,13 +11541,12 @@
* Set WC to loopback mode since link is required to provide clock
* to the XMAC in 20G mode
*/
- if (vars->line_speed == SPEED_20000) {
- bnx2x_set_aer_mmd(params, ¶ms->phy[0]);
- bnx2x_warpcore_reset_lane(bp, ¶ms->phy[0], 0);
- params->phy[INT_PHY].config_loopback(
+ bnx2x_set_aer_mmd(params, ¶ms->phy[0]);
+ bnx2x_warpcore_reset_lane(bp, ¶ms->phy[0], 0);
+ params->phy[INT_PHY].config_loopback(
¶ms->phy[INT_PHY],
params);
- }
+
bnx2x_xmac_enable(params, vars, 1);
REG_WR(bp, NIG_REG_EGRESS_DRAIN0_MODE + params->port*4, 0);
}
@@ -11684,12 +11724,16 @@
bnx2x_set_led(params, vars, LED_MODE_OFF, 0);
if (reset_ext_phy) {
+ bnx2x_set_mdio_clk(bp, params->chip_id, port);
for (phy_index = EXT_PHY1; phy_index < params->num_phys;
phy_index++) {
- if (params->phy[phy_index].link_reset)
+ if (params->phy[phy_index].link_reset) {
+ bnx2x_set_aer_mmd(params,
+ ¶ms->phy[phy_index]);
params->phy[phy_index].link_reset(
¶ms->phy[phy_index],
params);
+ }
if (params->phy[phy_index].flags &
FLAGS_REARM_LATCH_SIGNAL)
clear_latch_ind = 1;
@@ -12178,10 +12222,6 @@
u8 led_mode;
u32 half_open_conn = (vars->phy_flags & PHY_HALF_OPEN_CONN_FLAG) > 0;
- /*DP(NETIF_MSG_LINK, "CHECK LINK: %x half_open:%x-> lss:%x\n",
- vars->link_up,
- half_open_conn, lss_status);*/
-
if ((lss_status ^ half_open_conn) == 0)
return;
@@ -12194,6 +12234,7 @@
* b. Update link_vars->link_up
*/
if (lss_status) {
+ DP(NETIF_MSG_LINK, "Remote Fault detected !!!\n");
vars->link_status &= ~LINK_STATUS_LINK_UP;
vars->link_up = 0;
vars->phy_flags |= PHY_HALF_OPEN_CONN_FLAG;
@@ -12203,6 +12244,7 @@
*/
led_mode = LED_MODE_OFF;
} else {
+ DP(NETIF_MSG_LINK, "Remote Fault cleared\n");
vars->link_status |= LINK_STATUS_LINK_UP;
vars->link_up = 1;
vars->phy_flags &= ~PHY_HALF_OPEN_CONN_FLAG;
@@ -12219,6 +12261,15 @@
bnx2x_notify_link_changed(bp);
}
+/******************************************************************************
+* Description:
+* This function checks for half opened connection change indication.
+* When such change occurs, it calls the bnx2x_analyze_link_error
+* to check if Remote Fault is set or cleared. Reception of remote fault
+* status message in the MAC indicates that the peer's MAC has detected
+* a fault, for example, due to break in the TX side of fiber.
+*
+******************************************************************************/
static void bnx2x_check_half_open_conn(struct link_params *params,
struct link_vars *vars)
{
@@ -12229,9 +12280,28 @@
if ((vars->phy_flags & PHY_PHYSICAL_LINK_FLAG) == 0)
return;
- if (!CHIP_IS_E3(bp) &&
+ if (CHIP_IS_E3(bp) &&
(REG_RD(bp, MISC_REG_RESET_REG_2) &
- (MISC_REGISTERS_RESET_REG_2_RST_BMAC0 << params->port))) {
+ (MISC_REGISTERS_RESET_REG_2_XMAC))) {
+ /* Check E3 XMAC */
+ /*
+ * Note that link speed cannot be queried here, since it may be
+ * zero while link is down. In case UMAC is active, LSS will
+ * simply not be set
+ */
+ mac_base = (params->port) ? GRCBASE_XMAC1 : GRCBASE_XMAC0;
+
+ /* Clear stick bits (Requires rising edge) */
+ REG_WR(bp, mac_base + XMAC_REG_CLEAR_RX_LSS_STATUS, 0);
+ REG_WR(bp, mac_base + XMAC_REG_CLEAR_RX_LSS_STATUS,
+ XMAC_CLEAR_RX_LSS_STATUS_REG_CLEAR_LOCAL_FAULT_STATUS |
+ XMAC_CLEAR_RX_LSS_STATUS_REG_CLEAR_REMOTE_FAULT_STATUS);
+ if (REG_RD(bp, mac_base + XMAC_REG_RX_LSS_STATUS))
+ lss_status = 1;
+
+ bnx2x_analyze_link_error(params, vars, lss_status);
+ } else if (REG_RD(bp, MISC_REG_RESET_REG_2) &
+ (MISC_REGISTERS_RESET_REG_2_RST_BMAC0 << params->port)) {
/* Check E1X / E2 BMAC */
u32 lss_status_reg;
u32 wb_data[2];
@@ -12253,14 +12323,20 @@
void bnx2x_period_func(struct link_params *params, struct link_vars *vars)
{
struct bnx2x *bp = params->bp;
+ u16 phy_idx;
if (!params) {
- DP(NETIF_MSG_LINK, "Ininitliazed params !\n");
+ DP(NETIF_MSG_LINK, "Uninitialized params !\n");
return;
}
- /* DP(NETIF_MSG_LINK, "Periodic called vars->phy_flags 0x%x speed 0x%x
- RESET_REG_2 0x%x\n", vars->phy_flags, vars->line_speed,
- REG_RD(bp, MISC_REG_RESET_REG_2)); */
- bnx2x_check_half_open_conn(params, vars);
+
+ for (phy_idx = INT_PHY; phy_idx < MAX_PHYS; phy_idx++) {
+ if (params->phy[phy_idx].flags & FLAGS_TX_ERROR_CHECK) {
+ bnx2x_set_aer_mmd(params, ¶ms->phy[phy_idx]);
+ bnx2x_check_half_open_conn(params, vars);
+ break;
+ }
+ }
+
if (CHIP_IS_E3(bp))
bnx2x_check_over_curr(params, vars);
}
diff --git a/drivers/net/bnx2x/bnx2x_link.h b/drivers/net/bnx2x/bnx2x_link.h
index 6a7708d..c12db6d 100644
--- a/drivers/net/bnx2x/bnx2x_link.h
+++ b/drivers/net/bnx2x/bnx2x_link.h
@@ -145,6 +145,8 @@
#define FLAGS_SFP_NOT_APPROVED (1<<7)
#define FLAGS_MDC_MDIO_WA (1<<8)
#define FLAGS_DUMMY_READ (1<<9)
+#define FLAGS_MDC_MDIO_WA_B0 (1<<10)
+#define FLAGS_TX_ERROR_CHECK (1<<12)
/* preemphasis values for the rx side */
u16 rx_preemphasis[4];
@@ -276,7 +278,6 @@
#define PHY_PHYSICAL_LINK_FLAG (1<<2)
#define PHY_HALF_OPEN_CONN_FLAG (1<<3)
#define PHY_OVER_CURRENT_FLAG (1<<4)
-#define PHY_TX_ERROR_CHECK_FLAG (1<<5)
u8 mac_type;
#define MAC_TYPE_NONE 0
diff --git a/drivers/net/bnx2x/bnx2x_main.c b/drivers/net/bnx2x/bnx2x_main.c
index 1507091..f74582a 100644
--- a/drivers/net/bnx2x/bnx2x_main.c
+++ b/drivers/net/bnx2x/bnx2x_main.c
@@ -5798,6 +5798,12 @@
DP(BNX2X_MSG_MCP, "starting common init func %d\n", BP_ABS_FUNC(bp));
+ /*
+ * take the UNDI lock to protect undi_unload flow from accessing
+ * registers while we're resetting the chip
+ */
+ bnx2x_acquire_hw_lock(bp, HW_LOCK_RESOURCE_UNDI);
+
bnx2x_reset_common(bp);
REG_WR(bp, GRCBASE_MISC + MISC_REGISTERS_RESET_REG_1_SET, 0xffffffff);
@@ -5808,6 +5814,8 @@
}
REG_WR(bp, GRCBASE_MISC + MISC_REGISTERS_RESET_REG_2_SET, val);
+ bnx2x_release_hw_lock(bp, HW_LOCK_RESOURCE_UNDI);
+
bnx2x_init_block(bp, BLOCK_MISC, PHASE_COMMON);
if (!CHIP_IS_E1x(bp)) {
@@ -10251,10 +10259,17 @@
/* clean indirect addresses */
pci_write_config_dword(bp->pdev, PCICFG_GRC_ADDRESS,
PCICFG_VENDOR_ID_OFFSET);
- REG_WR(bp, PXP2_REG_PGL_ADDR_88_F0 + BP_PORT(bp)*16, 0);
- REG_WR(bp, PXP2_REG_PGL_ADDR_8C_F0 + BP_PORT(bp)*16, 0);
- REG_WR(bp, PXP2_REG_PGL_ADDR_90_F0 + BP_PORT(bp)*16, 0);
- REG_WR(bp, PXP2_REG_PGL_ADDR_94_F0 + BP_PORT(bp)*16, 0);
+ /* Clean the following indirect addresses for all functions since it
+ * is not used by the driver.
+ */
+ REG_WR(bp, PXP2_REG_PGL_ADDR_88_F0, 0);
+ REG_WR(bp, PXP2_REG_PGL_ADDR_8C_F0, 0);
+ REG_WR(bp, PXP2_REG_PGL_ADDR_90_F0, 0);
+ REG_WR(bp, PXP2_REG_PGL_ADDR_94_F0, 0);
+ REG_WR(bp, PXP2_REG_PGL_ADDR_88_F1, 0);
+ REG_WR(bp, PXP2_REG_PGL_ADDR_8C_F1, 0);
+ REG_WR(bp, PXP2_REG_PGL_ADDR_90_F1, 0);
+ REG_WR(bp, PXP2_REG_PGL_ADDR_94_F1, 0);
/*
* Enable internal target-read (in case we are probed after PF FLR).
diff --git a/drivers/net/bnx2x/bnx2x_reg.h b/drivers/net/bnx2x/bnx2x_reg.h
index 02461fe..40266c1 100644
--- a/drivers/net/bnx2x/bnx2x_reg.h
+++ b/drivers/net/bnx2x/bnx2x_reg.h
@@ -3007,11 +3007,27 @@
/* [R 6] Debug only: Number of used entries in the data FIFO */
#define PXP2_REG_HST_DATA_FIFO_STATUS 0x12047c
/* [R 7] Debug only: Number of used entries in the header FIFO */
-#define PXP2_REG_HST_HEADER_FIFO_STATUS 0x120478
-#define PXP2_REG_PGL_ADDR_88_F0 0x120534
-#define PXP2_REG_PGL_ADDR_8C_F0 0x120538
-#define PXP2_REG_PGL_ADDR_90_F0 0x12053c
-#define PXP2_REG_PGL_ADDR_94_F0 0x120540
+#define PXP2_REG_HST_HEADER_FIFO_STATUS 0x120478
+#define PXP2_REG_PGL_ADDR_88_F0 0x120534
+/* [R 32] GRC address for configuration access to PCIE config address 0x88.
+ * any write to this PCIE address will cause a GRC write access to the
+ * address that's in t this register */
+#define PXP2_REG_PGL_ADDR_88_F1 0x120544
+#define PXP2_REG_PGL_ADDR_8C_F0 0x120538
+/* [R 32] GRC address for configuration access to PCIE config address 0x8c.
+ * any write to this PCIE address will cause a GRC write access to the
+ * address that's in t this register */
+#define PXP2_REG_PGL_ADDR_8C_F1 0x120548
+#define PXP2_REG_PGL_ADDR_90_F0 0x12053c
+/* [R 32] GRC address for configuration access to PCIE config address 0x90.
+ * any write to this PCIE address will cause a GRC write access to the
+ * address that's in t this register */
+#define PXP2_REG_PGL_ADDR_90_F1 0x12054c
+#define PXP2_REG_PGL_ADDR_94_F0 0x120540
+/* [R 32] GRC address for configuration access to PCIE config address 0x94.
+ * any write to this PCIE address will cause a GRC write access to the
+ * address that's in t this register */
+#define PXP2_REG_PGL_ADDR_94_F1 0x120550
#define PXP2_REG_PGL_CONTROL0 0x120490
#define PXP2_REG_PGL_CONTROL1 0x120514
#define PXP2_REG_PGL_DEBUG 0x120520
@@ -4771,9 +4787,11 @@
The fields are: [4:0] - tail pointer; 10:5] - Link List size; 15:11] -
header pointer. */
#define UCM_REG_XX_TABLE 0xe0300
+#define UMAC_COMMAND_CONFIG_REG_IGNORE_TX_PAUSE (0x1<<28)
#define UMAC_COMMAND_CONFIG_REG_LOOP_ENA (0x1<<15)
#define UMAC_COMMAND_CONFIG_REG_NO_LGTH_CHECK (0x1<<24)
#define UMAC_COMMAND_CONFIG_REG_PAD_EN (0x1<<5)
+#define UMAC_COMMAND_CONFIG_REG_PAUSE_IGNORE (0x1<<8)
#define UMAC_COMMAND_CONFIG_REG_PROMIS_EN (0x1<<4)
#define UMAC_COMMAND_CONFIG_REG_RX_ENA (0x1<<1)
#define UMAC_COMMAND_CONFIG_REG_SW_RESET (0x1<<13)
@@ -5622,8 +5640,9 @@
#define EMAC_MDIO_COMM_START_BUSY (1L<<29)
#define EMAC_MDIO_MODE_AUTO_POLL (1L<<4)
#define EMAC_MDIO_MODE_CLAUSE_45 (1L<<31)
-#define EMAC_MDIO_MODE_CLOCK_CNT (0x3fL<<16)
+#define EMAC_MDIO_MODE_CLOCK_CNT (0x3ffL<<16)
#define EMAC_MDIO_MODE_CLOCK_CNT_BITSHIFT 16
+#define EMAC_MDIO_STATUS_10MB (1L<<1)
#define EMAC_MODE_25G_MODE (1L<<5)
#define EMAC_MODE_HALF_DUPLEX (1L<<1)
#define EMAC_MODE_PORT_GMII (2L<<2)
@@ -5634,6 +5653,7 @@
#define EMAC_REG_EMAC_MAC_MATCH 0x10
#define EMAC_REG_EMAC_MDIO_COMM 0xac
#define EMAC_REG_EMAC_MDIO_MODE 0xb4
+#define EMAC_REG_EMAC_MDIO_STATUS 0xb0
#define EMAC_REG_EMAC_MODE 0x0
#define EMAC_REG_EMAC_RX_MODE 0xc8
#define EMAC_REG_EMAC_RX_MTU_SIZE 0x9c
diff --git a/drivers/net/can/slcan.c b/drivers/net/can/slcan.c
index f523f1c..4b70b7e 100644
--- a/drivers/net/can/slcan.c
+++ b/drivers/net/can/slcan.c
@@ -197,7 +197,7 @@
skb->ip_summed = CHECKSUM_UNNECESSARY;
memcpy(skb_put(skb, sizeof(struct can_frame)),
&cf, sizeof(struct can_frame));
- netif_rx(skb);
+ netif_rx_ni(skb);
sl->dev->stats.rx_packets++;
sl->dev->stats.rx_bytes += cf.can_dlc;
diff --git a/drivers/net/e1000/e1000_ethtool.c b/drivers/net/e1000/e1000_ethtool.c
index c5f0f04..5548d46 100644
--- a/drivers/net/e1000/e1000_ethtool.c
+++ b/drivers/net/e1000/e1000_ethtool.c
@@ -838,6 +838,7 @@
/* Disable all the interrupts */
ew32(IMC, 0xFFFFFFFF);
+ E1000_WRITE_FLUSH();
msleep(10);
/* Test each interrupt */
@@ -856,6 +857,7 @@
adapter->test_icr = 0;
ew32(IMC, mask);
ew32(ICS, mask);
+ E1000_WRITE_FLUSH();
msleep(10);
if (adapter->test_icr & mask) {
@@ -873,6 +875,7 @@
adapter->test_icr = 0;
ew32(IMS, mask);
ew32(ICS, mask);
+ E1000_WRITE_FLUSH();
msleep(10);
if (!(adapter->test_icr & mask)) {
@@ -890,6 +893,7 @@
adapter->test_icr = 0;
ew32(IMC, ~mask & 0x00007FFF);
ew32(ICS, ~mask & 0x00007FFF);
+ E1000_WRITE_FLUSH();
msleep(10);
if (adapter->test_icr) {
@@ -901,6 +905,7 @@
/* Disable all the interrupts */
ew32(IMC, 0xFFFFFFFF);
+ E1000_WRITE_FLUSH();
msleep(10);
/* Unhook test interrupt handler */
@@ -1394,6 +1399,7 @@
if (unlikely(++k == txdr->count)) k = 0;
}
ew32(TDT, k);
+ E1000_WRITE_FLUSH();
msleep(200);
time = jiffies; /* set the start time for the receive */
good_cnt = 0;
diff --git a/drivers/net/e1000/e1000_hw.c b/drivers/net/e1000/e1000_hw.c
index 1698622..8545c7a 100644
--- a/drivers/net/e1000/e1000_hw.c
+++ b/drivers/net/e1000/e1000_hw.c
@@ -446,6 +446,7 @@
/* Must reset the PHY before resetting the MAC */
if ((hw->mac_type == e1000_82541) || (hw->mac_type == e1000_82547)) {
ew32(CTRL, (ctrl | E1000_CTRL_PHY_RST));
+ E1000_WRITE_FLUSH();
msleep(5);
}
@@ -3752,6 +3753,7 @@
/* Clear SK and CS */
eecd &= ~(E1000_EECD_CS | E1000_EECD_SK);
ew32(EECD, eecd);
+ E1000_WRITE_FLUSH();
udelay(1);
}
@@ -3824,6 +3826,7 @@
eecd &= ~E1000_EECD_SK; /* Lower SCK */
ew32(EECD, eecd);
+ E1000_WRITE_FLUSH();
udelay(hw->eeprom.delay_usec);
} else if (hw->eeprom.type == e1000_eeprom_microwire) {
diff --git a/drivers/net/e1000e/82571.c b/drivers/net/e1000e/82571.c
index 480f259..536b3a5 100644
--- a/drivers/net/e1000e/82571.c
+++ b/drivers/net/e1000e/82571.c
@@ -2085,7 +2085,8 @@
| FLAG_HAS_AMT
| FLAG_HAS_CTRLEXT_ON_LOAD,
.flags2 = FLAG2_CHECK_PHY_HANG
- | FLAG2_DISABLE_ASPM_L0S,
+ | FLAG2_DISABLE_ASPM_L0S
+ | FLAG2_NO_DISABLE_RX,
.pba = 32,
.max_hw_frame_size = DEFAULT_JUMBO,
.get_variants = e1000_get_variants_82571,
@@ -2104,7 +2105,8 @@
| FLAG_HAS_AMT
| FLAG_HAS_JUMBO_FRAMES
| FLAG_HAS_CTRLEXT_ON_LOAD,
- .flags2 = FLAG2_DISABLE_ASPM_L0S,
+ .flags2 = FLAG2_DISABLE_ASPM_L0S
+ | FLAG2_NO_DISABLE_RX,
.pba = 32,
.max_hw_frame_size = DEFAULT_JUMBO,
.get_variants = e1000_get_variants_82571,
diff --git a/drivers/net/e1000e/e1000.h b/drivers/net/e1000e/e1000.h
index 638d175..35916f4 100644
--- a/drivers/net/e1000e/e1000.h
+++ b/drivers/net/e1000e/e1000.h
@@ -453,6 +453,7 @@
#define FLAG2_DISABLE_ASPM_L0S (1 << 7)
#define FLAG2_DISABLE_AIM (1 << 8)
#define FLAG2_CHECK_PHY_HANG (1 << 9)
+#define FLAG2_NO_DISABLE_RX (1 << 10)
#define E1000_RX_DESC_PS(R, i) \
(&(((union e1000_rx_desc_packet_split *)((R).desc))[i]))
diff --git a/drivers/net/e1000e/es2lan.c b/drivers/net/e1000e/es2lan.c
index c0ecb2d..e4f4225 100644
--- a/drivers/net/e1000e/es2lan.c
+++ b/drivers/net/e1000e/es2lan.c
@@ -1313,6 +1313,7 @@
kmrnctrlsta = ((offset << E1000_KMRNCTRLSTA_OFFSET_SHIFT) &
E1000_KMRNCTRLSTA_OFFSET) | E1000_KMRNCTRLSTA_REN;
ew32(KMRNCTRLSTA, kmrnctrlsta);
+ e1e_flush();
udelay(2);
@@ -1347,6 +1348,7 @@
kmrnctrlsta = ((offset << E1000_KMRNCTRLSTA_OFFSET_SHIFT) &
E1000_KMRNCTRLSTA_OFFSET) | data;
ew32(KMRNCTRLSTA, kmrnctrlsta);
+ e1e_flush();
udelay(2);
diff --git a/drivers/net/e1000e/ethtool.c b/drivers/net/e1000e/ethtool.c
index cb1a362..6a0526a 100644
--- a/drivers/net/e1000e/ethtool.c
+++ b/drivers/net/e1000e/ethtool.c
@@ -28,8 +28,8 @@
/* ethtool support for e1000 */
-#include <linux/interrupt.h>
#include <linux/netdevice.h>
+#include <linux/interrupt.h>
#include <linux/ethtool.h>
#include <linux/pci.h>
#include <linux/slab.h>
@@ -964,6 +964,7 @@
/* Disable all the interrupts */
ew32(IMC, 0xFFFFFFFF);
+ e1e_flush();
usleep_range(10000, 20000);
/* Test each interrupt */
@@ -996,6 +997,7 @@
adapter->test_icr = 0;
ew32(IMC, mask);
ew32(ICS, mask);
+ e1e_flush();
usleep_range(10000, 20000);
if (adapter->test_icr & mask) {
@@ -1014,6 +1016,7 @@
adapter->test_icr = 0;
ew32(IMS, mask);
ew32(ICS, mask);
+ e1e_flush();
usleep_range(10000, 20000);
if (!(adapter->test_icr & mask)) {
@@ -1032,6 +1035,7 @@
adapter->test_icr = 0;
ew32(IMC, ~mask & 0x00007FFF);
ew32(ICS, ~mask & 0x00007FFF);
+ e1e_flush();
usleep_range(10000, 20000);
if (adapter->test_icr) {
@@ -1043,6 +1047,7 @@
/* Disable all the interrupts */
ew32(IMC, 0xFFFFFFFF);
+ e1e_flush();
usleep_range(10000, 20000);
/* Unhook test interrupt handler */
@@ -1201,7 +1206,8 @@
rx_ring->next_to_clean = 0;
rctl = er32(RCTL);
- ew32(RCTL, rctl & ~E1000_RCTL_EN);
+ if (!(adapter->flags2 & FLAG2_NO_DISABLE_RX))
+ ew32(RCTL, rctl & ~E1000_RCTL_EN);
ew32(RDBAL, ((u64) rx_ring->dma & 0xFFFFFFFF));
ew32(RDBAH, ((u64) rx_ring->dma >> 32));
ew32(RDLEN, rx_ring->size);
@@ -1276,6 +1282,7 @@
E1000_CTRL_FD); /* Force Duplex to FULL */
ew32(CTRL, ctrl_reg);
+ e1e_flush();
udelay(500);
return 0;
@@ -1418,6 +1425,7 @@
*/
#define E1000_SERDES_LB_ON 0x410
ew32(SCTL, E1000_SERDES_LB_ON);
+ e1e_flush();
usleep_range(10000, 20000);
return 0;
@@ -1513,6 +1521,7 @@
hw->phy.media_type == e1000_media_type_internal_serdes) {
#define E1000_SERDES_LB_OFF 0x400
ew32(SCTL, E1000_SERDES_LB_OFF);
+ e1e_flush();
usleep_range(10000, 20000);
break;
}
@@ -1592,6 +1601,7 @@
k = 0;
}
ew32(TDT, k);
+ e1e_flush();
msleep(200);
time = jiffies; /* set the start time for the receive */
good_cnt = 0;
diff --git a/drivers/net/e1000e/ich8lan.c b/drivers/net/e1000e/ich8lan.c
index c175212..4e36978 100644
--- a/drivers/net/e1000e/ich8lan.c
+++ b/drivers/net/e1000e/ich8lan.c
@@ -283,6 +283,7 @@
ctrl |= E1000_CTRL_LANPHYPC_OVERRIDE;
ctrl &= ~E1000_CTRL_LANPHYPC_VALUE;
ew32(CTRL, ctrl);
+ e1e_flush();
udelay(10);
ctrl &= ~E1000_CTRL_LANPHYPC_OVERRIDE;
ew32(CTRL, ctrl);
@@ -1230,9 +1231,11 @@
ew32(CTRL, reg);
ew32(CTRL_EXT, ctrl_ext | E1000_CTRL_EXT_SPD_BYPS);
+ e1e_flush();
udelay(20);
ew32(CTRL, ctrl_reg);
ew32(CTRL_EXT, ctrl_ext);
+ e1e_flush();
udelay(20);
out:
@@ -2134,8 +2137,7 @@
ret_val = 0;
for (i = 0; i < words; i++) {
- if ((dev_spec->shadow_ram) &&
- (dev_spec->shadow_ram[offset+i].modified)) {
+ if (dev_spec->shadow_ram[offset+i].modified) {
data[i] = dev_spec->shadow_ram[offset+i].value;
} else {
ret_val = e1000_read_flash_word_ich8lan(hw,
@@ -3090,6 +3092,7 @@
ret_val = e1000_acquire_swflag_ich8lan(hw);
e_dbg("Issuing a global reset to ich8lan\n");
ew32(CTRL, (ctrl | E1000_CTRL_RST));
+ /* cannot issue a flush here because it hangs the hardware */
msleep(20);
if (!ret_val)
diff --git a/drivers/net/e1000e/lib.c b/drivers/net/e1000e/lib.c
index 65580b40..0893ab1 100644
--- a/drivers/net/e1000e/lib.c
+++ b/drivers/net/e1000e/lib.c
@@ -190,7 +190,8 @@
/* Check for LOM (vs. NIC) or one of two valid mezzanine cards */
if (!((nvm_data & NVM_COMPAT_LOM) ||
(hw->adapter->pdev->device == E1000_DEV_ID_82571EB_SERDES_DUAL) ||
- (hw->adapter->pdev->device == E1000_DEV_ID_82571EB_SERDES_QUAD)))
+ (hw->adapter->pdev->device == E1000_DEV_ID_82571EB_SERDES_QUAD) ||
+ (hw->adapter->pdev->device == E1000_DEV_ID_82571EB_SERDES)))
goto out;
ret_val = e1000_read_nvm(hw, NVM_ALT_MAC_ADDR_PTR, 1,
@@ -200,10 +201,10 @@
goto out;
}
- if (nvm_alt_mac_addr_offset == 0xFFFF) {
+ if ((nvm_alt_mac_addr_offset == 0xFFFF) ||
+ (nvm_alt_mac_addr_offset == 0x0000))
/* There is no Alternate MAC Address */
goto out;
- }
if (hw->bus.func == E1000_FUNC_1)
nvm_alt_mac_addr_offset += E1000_ALT_MAC_ADDRESS_OFFSET_LAN1;
@@ -1986,6 +1987,7 @@
/* Clear SK and CS */
eecd &= ~(E1000_EECD_CS | E1000_EECD_SK);
ew32(EECD, eecd);
+ e1e_flush();
udelay(1);
/*
diff --git a/drivers/net/e1000e/netdev.c b/drivers/net/e1000e/netdev.c
index 4353ad5..362f703 100644
--- a/drivers/net/e1000e/netdev.c
+++ b/drivers/net/e1000e/netdev.c
@@ -31,12 +31,12 @@
#include <linux/module.h>
#include <linux/types.h>
#include <linux/init.h>
-#include <linux/interrupt.h>
#include <linux/pci.h>
#include <linux/vmalloc.h>
#include <linux/pagemap.h>
#include <linux/delay.h>
#include <linux/netdevice.h>
+#include <linux/interrupt.h>
#include <linux/tcp.h>
#include <linux/ipv6.h>
#include <linux/slab.h>
@@ -56,7 +56,7 @@
#define DRV_EXTRAVERSION "-k"
-#define DRV_VERSION "1.3.16" DRV_EXTRAVERSION
+#define DRV_VERSION "1.4.4" DRV_EXTRAVERSION
char e1000e_driver_name[] = "e1000e";
const char e1000e_driver_version[] = DRV_VERSION;
@@ -2915,7 +2915,8 @@
/* disable receives while setting up the descriptors */
rctl = er32(RCTL);
- ew32(RCTL, rctl & ~E1000_RCTL_EN);
+ if (!(adapter->flags2 & FLAG2_NO_DISABLE_RX))
+ ew32(RCTL, rctl & ~E1000_RCTL_EN);
e1e_flush();
usleep_range(10000, 20000);
@@ -3394,7 +3395,8 @@
/* disable receives in the hardware */
rctl = er32(RCTL);
- ew32(RCTL, rctl & ~E1000_RCTL_EN);
+ if (!(adapter->flags2 & FLAG2_NO_DISABLE_RX))
+ ew32(RCTL, rctl & ~E1000_RCTL_EN);
/* flush and sleep below */
netif_stop_queue(netdev);
@@ -3403,6 +3405,7 @@
tctl = er32(TCTL);
tctl &= ~E1000_TCTL_EN;
ew32(TCTL, tctl);
+
/* flush both disables and wait for them to finish */
e1e_flush();
usleep_range(10000, 20000);
diff --git a/drivers/net/e1000e/phy.c b/drivers/net/e1000e/phy.c
index 2a6ee13..8666476 100644
--- a/drivers/net/e1000e/phy.c
+++ b/drivers/net/e1000e/phy.c
@@ -537,6 +537,7 @@
kmrnctrlsta = ((offset << E1000_KMRNCTRLSTA_OFFSET_SHIFT) &
E1000_KMRNCTRLSTA_OFFSET) | E1000_KMRNCTRLSTA_REN;
ew32(KMRNCTRLSTA, kmrnctrlsta);
+ e1e_flush();
udelay(2);
@@ -609,6 +610,7 @@
kmrnctrlsta = ((offset << E1000_KMRNCTRLSTA_OFFSET_SHIFT) &
E1000_KMRNCTRLSTA_OFFSET) | data;
ew32(KMRNCTRLSTA, kmrnctrlsta);
+ e1e_flush();
udelay(2);
diff --git a/drivers/net/gianfar_ptp.c b/drivers/net/gianfar_ptp.c
index 1c97861..f67b8ae 100644
--- a/drivers/net/gianfar_ptp.c
+++ b/drivers/net/gianfar_ptp.c
@@ -193,14 +193,9 @@
/* Caller must hold etsects->lock. */
static void set_fipers(struct etsects *etsects)
{
- u32 tmr_ctrl = gfar_read(&etsects->regs->tmr_ctrl);
-
- gfar_write(&etsects->regs->tmr_ctrl, tmr_ctrl & (~TE));
- gfar_write(&etsects->regs->tmr_prsc, etsects->tmr_prsc);
+ set_alarm(etsects);
gfar_write(&etsects->regs->tmr_fiper1, etsects->tmr_fiper1);
gfar_write(&etsects->regs->tmr_fiper2, etsects->tmr_fiper2);
- set_alarm(etsects);
- gfar_write(&etsects->regs->tmr_ctrl, tmr_ctrl|TE);
}
/*
@@ -511,7 +506,7 @@
gfar_write(&etsects->regs->tmr_fiper1, etsects->tmr_fiper1);
gfar_write(&etsects->regs->tmr_fiper2, etsects->tmr_fiper2);
set_alarm(etsects);
- gfar_write(&etsects->regs->tmr_ctrl, tmr_ctrl|FS|RTPE|TE);
+ gfar_write(&etsects->regs->tmr_ctrl, tmr_ctrl|FS|RTPE|TE|FRD);
spin_unlock_irqrestore(&etsects->lock, flags);
diff --git a/drivers/net/igb/e1000_nvm.c b/drivers/net/igb/e1000_nvm.c
index 7dcd65c..4040712 100644
--- a/drivers/net/igb/e1000_nvm.c
+++ b/drivers/net/igb/e1000_nvm.c
@@ -285,6 +285,7 @@
/* Clear SK and CS */
eecd &= ~(E1000_EECD_CS | E1000_EECD_SK);
wr32(E1000_EECD, eecd);
+ wrfl();
udelay(1);
timeout = NVM_MAX_RETRY_SPI;
diff --git a/drivers/net/igb/igb_ethtool.c b/drivers/net/igb/igb_ethtool.c
index ff244ce..414b022 100644
--- a/drivers/net/igb/igb_ethtool.c
+++ b/drivers/net/igb/igb_ethtool.c
@@ -1225,6 +1225,7 @@
/* Disable all the interrupts */
wr32(E1000_IMC, ~0);
+ wrfl();
msleep(10);
/* Define all writable bits for ICS */
@@ -1268,6 +1269,7 @@
wr32(E1000_IMC, mask);
wr32(E1000_ICS, mask);
+ wrfl();
msleep(10);
if (adapter->test_icr & mask) {
@@ -1289,6 +1291,7 @@
wr32(E1000_IMS, mask);
wr32(E1000_ICS, mask);
+ wrfl();
msleep(10);
if (!(adapter->test_icr & mask)) {
@@ -1310,6 +1313,7 @@
wr32(E1000_IMC, ~mask);
wr32(E1000_ICS, ~mask);
+ wrfl();
msleep(10);
if (adapter->test_icr & mask) {
@@ -1321,6 +1325,7 @@
/* Disable all the interrupts */
wr32(E1000_IMC, ~0);
+ wrfl();
msleep(10);
/* Unhook test interrupt handler */
diff --git a/drivers/net/igb/igb_main.c b/drivers/net/igb/igb_main.c
index dc59905..40d4c40 100644
--- a/drivers/net/igb/igb_main.c
+++ b/drivers/net/igb/igb_main.c
@@ -1052,6 +1052,7 @@
kfree(adapter->vf_data);
adapter->vf_data = NULL;
wr32(E1000_IOVCTL, E1000_IOVCTL_REUSE_VFQ);
+ wrfl();
msleep(100);
dev_info(&adapter->pdev->dev, "IOV Disabled\n");
}
@@ -2022,7 +2023,7 @@
if (hw->bus.func == 0)
hw->nvm.ops.read(hw, NVM_INIT_CONTROL3_PORT_A, 1, &eeprom_data);
- else if (hw->mac.type == e1000_82580)
+ else if (hw->mac.type >= e1000_82580)
hw->nvm.ops.read(hw, NVM_INIT_CONTROL3_PORT_A +
NVM_82580_LAN_FUNC_OFFSET(hw->bus.func), 1,
&eeprom_data);
@@ -2198,6 +2199,7 @@
kfree(adapter->vf_data);
adapter->vf_data = NULL;
wr32(E1000_IOVCTL, E1000_IOVCTL_REUSE_VFQ);
+ wrfl();
msleep(100);
dev_info(&pdev->dev, "IOV Disabled\n");
}
diff --git a/drivers/net/igbvf/netdev.c b/drivers/net/igbvf/netdev.c
index 1330c8e..40ed066 100644
--- a/drivers/net/igbvf/netdev.c
+++ b/drivers/net/igbvf/netdev.c
@@ -1226,6 +1226,7 @@
/* disable transmits */
txdctl = er32(TXDCTL(0));
ew32(TXDCTL(0), txdctl & ~E1000_TXDCTL_QUEUE_ENABLE);
+ e1e_flush();
msleep(10);
/* Setup the HW Tx Head and Tail descriptor pointers */
@@ -1306,6 +1307,7 @@
/* disable receives */
rxdctl = er32(RXDCTL(0));
ew32(RXDCTL(0), rxdctl & ~E1000_RXDCTL_QUEUE_ENABLE);
+ e1e_flush();
msleep(10);
rdlen = rx_ring->count * sizeof(union e1000_adv_rx_desc);
diff --git a/drivers/net/irda/sh_irda.c b/drivers/net/irda/sh_irda.c
index 4488bd5..8266067 100644
--- a/drivers/net/irda/sh_irda.c
+++ b/drivers/net/irda/sh_irda.c
@@ -22,6 +22,8 @@
* - DMA transfer support
* - FIFO mode support
*/
+#include <linux/io.h>
+#include <linux/interrupt.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/clk.h>
diff --git a/drivers/net/irda/sh_sir.c b/drivers/net/irda/sh_sir.c
index 52a7c86..ed7d7d6 100644
--- a/drivers/net/irda/sh_sir.c
+++ b/drivers/net/irda/sh_sir.c
@@ -12,6 +12,8 @@
* published by the Free Software Foundation.
*/
+#include <linux/io.h>
+#include <linux/interrupt.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
@@ -511,7 +513,7 @@
static int sh_sir_read_data(struct sh_sir_self *self)
{
- u16 val;
+ u16 val = 0;
int timeout = 1024;
while (timeout--) {
diff --git a/drivers/net/irda/smsc-ircc2.c b/drivers/net/irda/smsc-ircc2.c
index 954f6e93..8b1c348 100644
--- a/drivers/net/irda/smsc-ircc2.c
+++ b/drivers/net/irda/smsc-ircc2.c
@@ -2405,8 +2405,6 @@
* addresses making a subsystem device table necessary.
*/
#ifdef CONFIG_PCI
-#define PCIID_VENDOR_INTEL 0x8086
-#define PCIID_VENDOR_ALI 0x10b9
static struct smsc_ircc_subsystem_configuration subsystem_configurations[] __initdata = {
/*
* Subsystems needing entries:
@@ -2416,7 +2414,7 @@
*/
{
/* Guessed entry */
- .vendor = PCIID_VENDOR_INTEL, /* Intel 82801DBM LPC bridge */
+ .vendor = PCI_VENDOR_ID_INTEL, /* Intel 82801DBM LPC bridge */
.device = 0x24cc,
.subvendor = 0x103c,
.subdevice = 0x08bc,
@@ -2429,7 +2427,7 @@
.name = "HP nx5000 family",
},
{
- .vendor = PCIID_VENDOR_INTEL, /* Intel 82801DBM LPC bridge */
+ .vendor = PCI_VENDOR_ID_INTEL, /* Intel 82801DBM LPC bridge */
.device = 0x24cc,
.subvendor = 0x103c,
.subdevice = 0x088c,
@@ -2443,7 +2441,7 @@
.name = "HP nc8000 family",
},
{
- .vendor = PCIID_VENDOR_INTEL, /* Intel 82801DBM LPC bridge */
+ .vendor = PCI_VENDOR_ID_INTEL, /* Intel 82801DBM LPC bridge */
.device = 0x24cc,
.subvendor = 0x103c,
.subdevice = 0x0890,
@@ -2456,7 +2454,7 @@
.name = "HP nc6000 family",
},
{
- .vendor = PCIID_VENDOR_INTEL, /* Intel 82801DBM LPC bridge */
+ .vendor = PCI_VENDOR_ID_INTEL, /* Intel 82801DBM LPC bridge */
.device = 0x24cc,
.subvendor = 0x0e11,
.subdevice = 0x0860,
@@ -2471,7 +2469,7 @@
},
{
/* Intel 82801DB/DBL (ICH4/ICH4-L) LPC Interface Bridge */
- .vendor = PCIID_VENDOR_INTEL,
+ .vendor = PCI_VENDOR_ID_INTEL,
.device = 0x24c0,
.subvendor = 0x1179,
.subdevice = 0xffff, /* 0xffff is "any" */
@@ -2484,7 +2482,7 @@
.name = "Toshiba laptop with Intel 82801DB/DBL LPC bridge",
},
{
- .vendor = PCIID_VENDOR_INTEL, /* Intel 82801CAM ISA bridge */
+ .vendor = PCI_VENDOR_ID_INTEL, /* Intel 82801CAM ISA bridge */
.device = 0x248c,
.subvendor = 0x1179,
.subdevice = 0xffff, /* 0xffff is "any" */
@@ -2498,7 +2496,7 @@
},
{
/* 82801DBM (ICH4-M) LPC Interface Bridge */
- .vendor = PCIID_VENDOR_INTEL,
+ .vendor = PCI_VENDOR_ID_INTEL,
.device = 0x24cc,
.subvendor = 0x1179,
.subdevice = 0xffff, /* 0xffff is "any" */
@@ -2512,7 +2510,7 @@
},
{
/* ALi M1533/M1535 PCI to ISA Bridge [Aladdin IV/V/V+] */
- .vendor = PCIID_VENDOR_ALI,
+ .vendor = PCI_VENDOR_ID_AL,
.device = 0x1533,
.subvendor = 0x1179,
.subdevice = 0xffff, /* 0xffff is "any" */
diff --git a/drivers/net/ixgb/ixgb_ee.c b/drivers/net/ixgb/ixgb_ee.c
index c982ab9..38b362b 100644
--- a/drivers/net/ixgb/ixgb_ee.c
+++ b/drivers/net/ixgb/ixgb_ee.c
@@ -57,6 +57,7 @@
*/
*eecd_reg = *eecd_reg | IXGB_EECD_SK;
IXGB_WRITE_REG(hw, EECD, *eecd_reg);
+ IXGB_WRITE_FLUSH(hw);
udelay(50);
}
@@ -75,6 +76,7 @@
*/
*eecd_reg = *eecd_reg & ~IXGB_EECD_SK;
IXGB_WRITE_REG(hw, EECD, *eecd_reg);
+ IXGB_WRITE_FLUSH(hw);
udelay(50);
}
@@ -112,6 +114,7 @@
eecd_reg |= IXGB_EECD_DI;
IXGB_WRITE_REG(hw, EECD, eecd_reg);
+ IXGB_WRITE_FLUSH(hw);
udelay(50);
@@ -206,21 +209,25 @@
/* Deselect EEPROM */
eecd_reg &= ~(IXGB_EECD_CS | IXGB_EECD_SK);
IXGB_WRITE_REG(hw, EECD, eecd_reg);
+ IXGB_WRITE_FLUSH(hw);
udelay(50);
/* Clock high */
eecd_reg |= IXGB_EECD_SK;
IXGB_WRITE_REG(hw, EECD, eecd_reg);
+ IXGB_WRITE_FLUSH(hw);
udelay(50);
/* Select EEPROM */
eecd_reg |= IXGB_EECD_CS;
IXGB_WRITE_REG(hw, EECD, eecd_reg);
+ IXGB_WRITE_FLUSH(hw);
udelay(50);
/* Clock low */
eecd_reg &= ~IXGB_EECD_SK;
IXGB_WRITE_REG(hw, EECD, eecd_reg);
+ IXGB_WRITE_FLUSH(hw);
udelay(50);
}
@@ -239,11 +246,13 @@
/* Rising edge of clock */
eecd_reg |= IXGB_EECD_SK;
IXGB_WRITE_REG(hw, EECD, eecd_reg);
+ IXGB_WRITE_FLUSH(hw);
udelay(50);
/* Falling edge of clock */
eecd_reg &= ~IXGB_EECD_SK;
IXGB_WRITE_REG(hw, EECD, eecd_reg);
+ IXGB_WRITE_FLUSH(hw);
udelay(50);
}
diff --git a/drivers/net/ixgb/ixgb_hw.c b/drivers/net/ixgb/ixgb_hw.c
index 6cb2e42..3d61a9e 100644
--- a/drivers/net/ixgb/ixgb_hw.c
+++ b/drivers/net/ixgb/ixgb_hw.c
@@ -149,6 +149,7 @@
*/
IXGB_WRITE_REG(hw, RCTL, IXGB_READ_REG(hw, RCTL) & ~IXGB_RCTL_RXEN);
IXGB_WRITE_REG(hw, TCTL, IXGB_READ_REG(hw, TCTL) & ~IXGB_TCTL_TXEN);
+ IXGB_WRITE_FLUSH(hw);
msleep(IXGB_DELAY_BEFORE_RESET);
/* Issue a global reset to the MAC. This will reset the chip's
@@ -1220,6 +1221,7 @@
ctrl &= ~IXGB_CTRL0_SDP2;
ctrl |= IXGB_CTRL0_SDP3;
IXGB_WRITE_REG(hw, CTRL0, ctrl);
+ IXGB_WRITE_FLUSH(hw);
/* SerDes needs extra delay */
msleep(IXGB_SUN_PHY_RESET_DELAY);
diff --git a/drivers/net/ixgbe/ixgbe_82599.c b/drivers/net/ixgbe/ixgbe_82599.c
index 3b3dd4d..34f30ec 100644
--- a/drivers/net/ixgbe/ixgbe_82599.c
+++ b/drivers/net/ixgbe/ixgbe_82599.c
@@ -213,6 +213,7 @@
switch (hw->phy.type) {
case ixgbe_phy_tn:
phy->ops.check_link = &ixgbe_check_phy_link_tnx;
+ phy->ops.setup_link = &ixgbe_setup_phy_link_tnx;
phy->ops.get_firmware_version =
&ixgbe_get_phy_firmware_version_tnx;
break;
diff --git a/drivers/net/ixgbe/ixgbe_common.c b/drivers/net/ixgbe/ixgbe_common.c
index 777051f..fc1375f 100644
--- a/drivers/net/ixgbe/ixgbe_common.c
+++ b/drivers/net/ixgbe/ixgbe_common.c
@@ -2632,6 +2632,7 @@
autoc_reg |= IXGBE_AUTOC_AN_RESTART;
autoc_reg |= IXGBE_AUTOC_FLU;
IXGBE_WRITE_REG(hw, IXGBE_AUTOC, autoc_reg);
+ IXGBE_WRITE_FLUSH(hw);
usleep_range(10000, 20000);
}
diff --git a/drivers/net/ixgbe/ixgbe_ethtool.c b/drivers/net/ixgbe/ixgbe_ethtool.c
index dc64955..82d4244 100644
--- a/drivers/net/ixgbe/ixgbe_ethtool.c
+++ b/drivers/net/ixgbe/ixgbe_ethtool.c
@@ -1378,6 +1378,7 @@
/* Disable all the interrupts */
IXGBE_WRITE_REG(&adapter->hw, IXGBE_EIMC, 0xFFFFFFFF);
+ IXGBE_WRITE_FLUSH(&adapter->hw);
usleep_range(10000, 20000);
/* Test each interrupt */
@@ -1398,6 +1399,7 @@
~mask & 0x00007FFF);
IXGBE_WRITE_REG(&adapter->hw, IXGBE_EICS,
~mask & 0x00007FFF);
+ IXGBE_WRITE_FLUSH(&adapter->hw);
usleep_range(10000, 20000);
if (adapter->test_icr & mask) {
@@ -1415,6 +1417,7 @@
adapter->test_icr = 0;
IXGBE_WRITE_REG(&adapter->hw, IXGBE_EIMS, mask);
IXGBE_WRITE_REG(&adapter->hw, IXGBE_EICS, mask);
+ IXGBE_WRITE_FLUSH(&adapter->hw);
usleep_range(10000, 20000);
if (!(adapter->test_icr &mask)) {
@@ -1435,6 +1438,7 @@
~mask & 0x00007FFF);
IXGBE_WRITE_REG(&adapter->hw, IXGBE_EICS,
~mask & 0x00007FFF);
+ IXGBE_WRITE_FLUSH(&adapter->hw);
usleep_range(10000, 20000);
if (adapter->test_icr) {
@@ -1446,6 +1450,7 @@
/* Disable all the interrupts */
IXGBE_WRITE_REG(&adapter->hw, IXGBE_EIMC, 0xFFFFFFFF);
+ IXGBE_WRITE_FLUSH(&adapter->hw);
usleep_range(10000, 20000);
/* Unhook test interrupt handler */
diff --git a/drivers/net/ixgbe/ixgbe_main.c b/drivers/net/ixgbe/ixgbe_main.c
index 1be6175..e86297b 100644
--- a/drivers/net/ixgbe/ixgbe_main.c
+++ b/drivers/net/ixgbe/ixgbe_main.c
@@ -184,6 +184,7 @@
vmdctl = IXGBE_READ_REG(hw, IXGBE_VT_CTL);
vmdctl &= ~IXGBE_VT_CTL_POOL_MASK;
IXGBE_WRITE_REG(hw, IXGBE_VT_CTL, vmdctl);
+ IXGBE_WRITE_FLUSH(hw);
/* take a breather then clean up driver data */
msleep(100);
@@ -1005,7 +1006,7 @@
struct ixgbe_adapter *adapter = dev_get_drvdata(dev);
unsigned long event = *(unsigned long *)data;
- if (!(adapter->flags & IXGBE_FLAG_DCA_ENABLED))
+ if (!(adapter->flags & IXGBE_FLAG_DCA_CAPABLE))
return 0;
switch (event) {
diff --git a/drivers/net/ixgbe/ixgbe_phy.c b/drivers/net/ixgbe/ixgbe_phy.c
index 735f686..f7ca351 100644
--- a/drivers/net/ixgbe/ixgbe_phy.c
+++ b/drivers/net/ixgbe/ixgbe_phy.c
@@ -1585,6 +1585,7 @@
*i2cctl |= IXGBE_I2C_CLK_OUT;
IXGBE_WRITE_REG(hw, IXGBE_I2CCTL, *i2cctl);
+ IXGBE_WRITE_FLUSH(hw);
/* SCL rise time (1000ns) */
udelay(IXGBE_I2C_T_RISE);
@@ -1605,6 +1606,7 @@
*i2cctl &= ~IXGBE_I2C_CLK_OUT;
IXGBE_WRITE_REG(hw, IXGBE_I2CCTL, *i2cctl);
+ IXGBE_WRITE_FLUSH(hw);
/* SCL fall time (300ns) */
udelay(IXGBE_I2C_T_FALL);
@@ -1628,6 +1630,7 @@
*i2cctl &= ~IXGBE_I2C_DATA_OUT;
IXGBE_WRITE_REG(hw, IXGBE_I2CCTL, *i2cctl);
+ IXGBE_WRITE_FLUSH(hw);
/* Data rise/fall (1000ns/300ns) and set-up time (250ns) */
udelay(IXGBE_I2C_T_RISE + IXGBE_I2C_T_FALL + IXGBE_I2C_T_SU_DATA);
diff --git a/drivers/net/ixgbe/ixgbe_x540.c b/drivers/net/ixgbe/ixgbe_x540.c
index bec30ed..2696c78 100644
--- a/drivers/net/ixgbe/ixgbe_x540.c
+++ b/drivers/net/ixgbe/ixgbe_x540.c
@@ -162,6 +162,7 @@
ctrl_ext = IXGBE_READ_REG(hw, IXGBE_CTRL_EXT);
ctrl_ext |= IXGBE_CTRL_EXT_PFRSTD;
IXGBE_WRITE_REG(hw, IXGBE_CTRL_EXT, ctrl_ext);
+ IXGBE_WRITE_FLUSH(hw);
msleep(50);
diff --git a/drivers/net/macb.c b/drivers/net/macb.c
index 0fcdc25..dc4e305 100644
--- a/drivers/net/macb.c
+++ b/drivers/net/macb.c
@@ -322,6 +322,9 @@
for (i = 0; i < TX_RING_SIZE; i++)
bp->tx_ring[i].ctrl = MACB_BIT(TX_USED);
+ /* Add wrap bit */
+ bp->tx_ring[TX_RING_SIZE - 1].ctrl |= MACB_BIT(TX_WRAP);
+
/* free transmit buffer in upper layer*/
for (tail = bp->tx_tail; tail != head; tail = NEXT_TX(tail)) {
struct ring_info *rp = &bp->tx_skb[tail];
diff --git a/drivers/net/mlx4/en_port.c b/drivers/net/mlx4/en_port.c
index 5e71091..5ada5b46 100644
--- a/drivers/net/mlx4/en_port.c
+++ b/drivers/net/mlx4/en_port.c
@@ -128,7 +128,7 @@
memset(context, 0, sizeof *context);
context->base_qpn = cpu_to_be32(base_qpn);
- context->n_mac = 0x7;
+ context->n_mac = 0x2;
context->promisc = cpu_to_be32(promisc << SET_PORT_PROMISC_SHIFT |
base_qpn);
context->mcast = cpu_to_be32(m_promisc << SET_PORT_MC_PROMISC_SHIFT |
diff --git a/drivers/net/mlx4/main.c b/drivers/net/mlx4/main.c
index c94b342..f0ee35d 100644
--- a/drivers/net/mlx4/main.c
+++ b/drivers/net/mlx4/main.c
@@ -1117,6 +1117,8 @@
info->port = port;
mlx4_init_mac_table(dev, &info->mac_table);
mlx4_init_vlan_table(dev, &info->vlan_table);
+ info->base_qpn = dev->caps.reserved_qps_base[MLX4_QP_REGION_ETH_ADDR] +
+ (port - 1) * (1 << log_num_mac);
sprintf(info->dev_name, "mlx4_port%d", port);
info->port_attr.attr.name = info->dev_name;
diff --git a/drivers/net/mlx4/port.c b/drivers/net/mlx4/port.c
index 1f95afd..609e0ec 100644
--- a/drivers/net/mlx4/port.c
+++ b/drivers/net/mlx4/port.c
@@ -258,9 +258,12 @@
if (validate_index(dev, table, index))
goto out;
- table->entries[index] = 0;
- mlx4_set_port_mac_table(dev, port, table->entries);
- --table->total;
+ /* Check whether this address has reference count */
+ if (!(--table->refs[index])) {
+ table->entries[index] = 0;
+ mlx4_set_port_mac_table(dev, port, table->entries);
+ --table->total;
+ }
out:
mutex_unlock(&table->mutex);
}
diff --git a/drivers/net/niu.c b/drivers/net/niu.c
index cd6c231..ed47585 100644
--- a/drivers/net/niu.c
+++ b/drivers/net/niu.c
@@ -9201,7 +9201,7 @@
first_chan = 0;
for (i = 0; i < port; i++)
- first_chan += parent->rxchan_per_port[port];
+ first_chan += parent->rxchan_per_port[i];
num_chan = parent->rxchan_per_port[port];
for (i = first_chan; i < (first_chan + num_chan); i++) {
@@ -9217,7 +9217,7 @@
first_chan = 0;
for (i = 0; i < port; i++)
- first_chan += parent->txchan_per_port[port];
+ first_chan += parent->txchan_per_port[i];
num_chan = parent->txchan_per_port[port];
for (i = first_chan; i < (first_chan + num_chan); i++) {
err = niu_ldg_assign_ldn(np, parent,
diff --git a/drivers/net/pcnet32.c b/drivers/net/pcnet32.c
index 8b3090d..80b6f36 100644
--- a/drivers/net/pcnet32.c
+++ b/drivers/net/pcnet32.c
@@ -82,7 +82,7 @@
/*
* VLB I/O addresses
*/
-static unsigned int pcnet32_portlist[] __initdata =
+static unsigned int pcnet32_portlist[] =
{ 0x300, 0x320, 0x340, 0x360, 0 };
static int pcnet32_debug;
diff --git a/drivers/net/phy/dp83640.c b/drivers/net/phy/dp83640.c
index 2cd8dc5..cb6e0b4 100644
--- a/drivers/net/phy/dp83640.c
+++ b/drivers/net/phy/dp83640.c
@@ -34,8 +34,7 @@
#define PAGESEL 0x13
#define LAYER4 0x02
#define LAYER2 0x01
-#define MAX_RXTS 4
-#define MAX_TXTS 4
+#define MAX_RXTS 64
#define N_EXT_TS 1
#define PSF_PTPVER 2
#define PSF_EVNT 0x4000
@@ -218,7 +217,7 @@
rxts->seqid = p->seqid;
rxts->msgtype = (p->msgtype >> 12) & 0xf;
rxts->hash = p->msgtype & 0x0fff;
- rxts->tmo = jiffies + HZ;
+ rxts->tmo = jiffies + 2;
}
static u64 phy2txts(struct phy_txts *p)
diff --git a/drivers/net/r8169.c b/drivers/net/r8169.c
index 7d9c650..02339b3 100644
--- a/drivers/net/r8169.c
+++ b/drivers/net/r8169.c
@@ -239,6 +239,7 @@
{ PCI_DEVICE(PCI_VENDOR_ID_REALTEK, 0x8168), 0, 0, RTL_CFG_1 },
{ PCI_DEVICE(PCI_VENDOR_ID_REALTEK, 0x8169), 0, 0, RTL_CFG_0 },
{ PCI_DEVICE(PCI_VENDOR_ID_DLINK, 0x4300), 0, 0, RTL_CFG_0 },
+ { PCI_DEVICE(PCI_VENDOR_ID_DLINK, 0x4302), 0, 0, RTL_CFG_0 },
{ PCI_DEVICE(PCI_VENDOR_ID_AT, 0xc107), 0, 0, RTL_CFG_0 },
{ PCI_DEVICE(0x16ec, 0x0116), 0, 0, RTL_CFG_0 },
{ PCI_VENDOR_ID_LINKSYS, 0x1032,
@@ -1091,6 +1092,21 @@
rtl_eri_write(ioaddr, addr, mask, (val & ~m) | p, type);
}
+struct exgmac_reg {
+ u16 addr;
+ u16 mask;
+ u32 val;
+};
+
+static void rtl_write_exgmac_batch(void __iomem *ioaddr,
+ const struct exgmac_reg *r, int len)
+{
+ while (len-- > 0) {
+ rtl_eri_write(ioaddr, r->addr, r->mask, r->val, ERIAR_EXGMAC);
+ r++;
+ }
+}
+
static u8 rtl8168d_efuse_read(void __iomem *ioaddr, int reg_addr)
{
u8 value = 0xff;
@@ -3116,6 +3132,18 @@
RTL_W32(MAC0, low);
RTL_R32(MAC0);
+ if (tp->mac_version == RTL_GIGA_MAC_VER_34) {
+ const struct exgmac_reg e[] = {
+ { .addr = 0xe0, ERIAR_MASK_1111, .val = low },
+ { .addr = 0xe4, ERIAR_MASK_1111, .val = high },
+ { .addr = 0xf0, ERIAR_MASK_1111, .val = low << 16 },
+ { .addr = 0xf4, ERIAR_MASK_1111, .val = high << 16 |
+ low >> 16 },
+ };
+
+ rtl_write_exgmac_batch(ioaddr, e, ARRAY_SIZE(e));
+ }
+
RTL_W8(Cfg9346, Cfg9346_Lock);
spin_unlock_irq(&tp->lock);
diff --git a/drivers/net/sis190.c b/drivers/net/sis190.c
index 8ad7bfb..3c0f131 100644
--- a/drivers/net/sis190.c
+++ b/drivers/net/sis190.c
@@ -1825,6 +1825,16 @@
generic_mii_ioctl(&tp->mii_if, if_mii(ifr), cmd, NULL);
}
+static int sis190_mac_addr(struct net_device *dev, void *p)
+{
+ int rc;
+
+ rc = eth_mac_addr(dev, p);
+ if (!rc)
+ sis190_init_rxfilter(dev);
+ return rc;
+}
+
static const struct net_device_ops sis190_netdev_ops = {
.ndo_open = sis190_open,
.ndo_stop = sis190_close,
@@ -1833,7 +1843,7 @@
.ndo_tx_timeout = sis190_tx_timeout,
.ndo_set_multicast_list = sis190_set_rx_mode,
.ndo_change_mtu = eth_change_mtu,
- .ndo_set_mac_address = eth_mac_addr,
+ .ndo_set_mac_address = sis190_mac_addr,
.ndo_validate_addr = eth_validate_addr,
#ifdef CONFIG_NET_POLL_CONTROLLER
.ndo_poll_controller = sis190_netpoll,
diff --git a/drivers/net/slip.c b/drivers/net/slip.c
index f11b3f3..4c61753 100644
--- a/drivers/net/slip.c
+++ b/drivers/net/slip.c
@@ -367,7 +367,7 @@
memcpy(skb_put(skb, count), sl->rbuff, count);
skb_reset_mac_header(skb);
skb->protocol = htons(ETH_P_IP);
- netif_rx(skb);
+ netif_rx_ni(skb);
dev->stats.rx_packets++;
}
diff --git a/drivers/net/usb/cdc_ncm.c b/drivers/net/usb/cdc_ncm.c
index fd622a66..a03336e 100644
--- a/drivers/net/usb/cdc_ncm.c
+++ b/drivers/net/usb/cdc_ncm.c
@@ -53,7 +53,7 @@
#include <linux/usb/usbnet.h>
#include <linux/usb/cdc.h>
-#define DRIVER_VERSION "01-June-2011"
+#define DRIVER_VERSION "04-Aug-2011"
/* CDC NCM subclass 3.2.1 */
#define USB_CDC_NCM_NDP16_LENGTH_MIN 0x10
@@ -163,35 +163,8 @@
usb_make_path(dev->udev, info->bus_info, sizeof(info->bus_info));
}
-static int
-cdc_ncm_do_request(struct cdc_ncm_ctx *ctx, struct usb_cdc_notification *req,
- void *data, u16 flags, u16 *actlen, u16 timeout)
-{
- int err;
-
- err = usb_control_msg(ctx->udev, (req->bmRequestType & USB_DIR_IN) ?
- usb_rcvctrlpipe(ctx->udev, 0) :
- usb_sndctrlpipe(ctx->udev, 0),
- req->bNotificationType, req->bmRequestType,
- req->wValue,
- req->wIndex, data,
- req->wLength, timeout);
-
- if (err < 0) {
- if (actlen)
- *actlen = 0;
- return err;
- }
-
- if (actlen)
- *actlen = err;
-
- return 0;
-}
-
static u8 cdc_ncm_setup(struct cdc_ncm_ctx *ctx)
{
- struct usb_cdc_notification req;
u32 val;
u8 flags;
u8 iface_no;
@@ -200,14 +173,14 @@
iface_no = ctx->control->cur_altsetting->desc.bInterfaceNumber;
- req.bmRequestType = USB_TYPE_CLASS | USB_DIR_IN | USB_RECIP_INTERFACE;
- req.bNotificationType = USB_CDC_GET_NTB_PARAMETERS;
- req.wValue = 0;
- req.wIndex = cpu_to_le16(iface_no);
- req.wLength = cpu_to_le16(sizeof(ctx->ncm_parm));
-
- err = cdc_ncm_do_request(ctx, &req, &ctx->ncm_parm, 0, NULL, 1000);
- if (err) {
+ err = usb_control_msg(ctx->udev,
+ usb_rcvctrlpipe(ctx->udev, 0),
+ USB_CDC_GET_NTB_PARAMETERS,
+ USB_TYPE_CLASS | USB_DIR_IN
+ | USB_RECIP_INTERFACE,
+ 0, iface_no, &ctx->ncm_parm,
+ sizeof(ctx->ncm_parm), 10000);
+ if (err < 0) {
pr_debug("failed GET_NTB_PARAMETERS\n");
return 1;
}
@@ -253,31 +226,26 @@
/* inform device about NTB input size changes */
if (ctx->rx_max != le32_to_cpu(ctx->ncm_parm.dwNtbInMaxSize)) {
- req.bmRequestType = USB_TYPE_CLASS | USB_DIR_OUT |
- USB_RECIP_INTERFACE;
- req.bNotificationType = USB_CDC_SET_NTB_INPUT_SIZE;
- req.wValue = 0;
- req.wIndex = cpu_to_le16(iface_no);
if (flags & USB_CDC_NCM_NCAP_NTB_INPUT_SIZE) {
struct usb_cdc_ncm_ndp_input_size ndp_in_sz;
-
- req.wLength = 8;
- ndp_in_sz.dwNtbInMaxSize = cpu_to_le32(ctx->rx_max);
- ndp_in_sz.wNtbInMaxDatagrams =
- cpu_to_le16(CDC_NCM_DPT_DATAGRAMS_MAX);
- ndp_in_sz.wReserved = 0;
- err = cdc_ncm_do_request(ctx, &req, &ndp_in_sz, 0, NULL,
- 1000);
+ err = usb_control_msg(ctx->udev,
+ usb_sndctrlpipe(ctx->udev, 0),
+ USB_CDC_SET_NTB_INPUT_SIZE,
+ USB_TYPE_CLASS | USB_DIR_OUT
+ | USB_RECIP_INTERFACE,
+ 0, iface_no, &ndp_in_sz, 8, 1000);
} else {
__le32 dwNtbInMaxSize = cpu_to_le32(ctx->rx_max);
-
- req.wLength = 4;
- err = cdc_ncm_do_request(ctx, &req, &dwNtbInMaxSize, 0,
- NULL, 1000);
+ err = usb_control_msg(ctx->udev,
+ usb_sndctrlpipe(ctx->udev, 0),
+ USB_CDC_SET_NTB_INPUT_SIZE,
+ USB_TYPE_CLASS | USB_DIR_OUT
+ | USB_RECIP_INTERFACE,
+ 0, iface_no, &dwNtbInMaxSize, 4, 1000);
}
- if (err)
+ if (err < 0)
pr_debug("Setting NTB Input Size failed\n");
}
@@ -332,29 +300,24 @@
/* set CRC Mode */
if (flags & USB_CDC_NCM_NCAP_CRC_MODE) {
- req.bmRequestType = USB_TYPE_CLASS | USB_DIR_OUT |
- USB_RECIP_INTERFACE;
- req.bNotificationType = USB_CDC_SET_CRC_MODE;
- req.wValue = cpu_to_le16(USB_CDC_NCM_CRC_NOT_APPENDED);
- req.wIndex = cpu_to_le16(iface_no);
- req.wLength = 0;
-
- err = cdc_ncm_do_request(ctx, &req, NULL, 0, NULL, 1000);
- if (err)
+ err = usb_control_msg(ctx->udev, usb_sndctrlpipe(ctx->udev, 0),
+ USB_CDC_SET_CRC_MODE,
+ USB_TYPE_CLASS | USB_DIR_OUT
+ | USB_RECIP_INTERFACE,
+ USB_CDC_NCM_CRC_NOT_APPENDED,
+ iface_no, NULL, 0, 1000);
+ if (err < 0)
pr_debug("Setting CRC mode off failed\n");
}
/* set NTB format, if both formats are supported */
if (ntb_fmt_supported & USB_CDC_NCM_NTH32_SIGN) {
- req.bmRequestType = USB_TYPE_CLASS | USB_DIR_OUT |
- USB_RECIP_INTERFACE;
- req.bNotificationType = USB_CDC_SET_NTB_FORMAT;
- req.wValue = cpu_to_le16(USB_CDC_NCM_NTB16_FORMAT);
- req.wIndex = cpu_to_le16(iface_no);
- req.wLength = 0;
-
- err = cdc_ncm_do_request(ctx, &req, NULL, 0, NULL, 1000);
- if (err)
+ err = usb_control_msg(ctx->udev, usb_sndctrlpipe(ctx->udev, 0),
+ USB_CDC_SET_NTB_FORMAT, USB_TYPE_CLASS
+ | USB_DIR_OUT | USB_RECIP_INTERFACE,
+ USB_CDC_NCM_NTB16_FORMAT,
+ iface_no, NULL, 0, 1000);
+ if (err < 0)
pr_debug("Setting NTB format to 16-bit failed\n");
}
@@ -364,17 +327,13 @@
if (flags & USB_CDC_NCM_NCAP_MAX_DATAGRAM_SIZE) {
__le16 max_datagram_size;
u16 eth_max_sz = le16_to_cpu(ctx->ether_desc->wMaxSegmentSize);
-
- req.bmRequestType = USB_TYPE_CLASS | USB_DIR_IN |
- USB_RECIP_INTERFACE;
- req.bNotificationType = USB_CDC_GET_MAX_DATAGRAM_SIZE;
- req.wValue = 0;
- req.wIndex = cpu_to_le16(iface_no);
- req.wLength = cpu_to_le16(2);
-
- err = cdc_ncm_do_request(ctx, &req, &max_datagram_size, 0, NULL,
- 1000);
- if (err) {
+ err = usb_control_msg(ctx->udev, usb_rcvctrlpipe(ctx->udev, 0),
+ USB_CDC_GET_MAX_DATAGRAM_SIZE,
+ USB_TYPE_CLASS | USB_DIR_IN
+ | USB_RECIP_INTERFACE,
+ 0, iface_no, &max_datagram_size,
+ 2, 1000);
+ if (err < 0) {
pr_debug("GET_MAX_DATAGRAM_SIZE failed, use size=%u\n",
CDC_NCM_MIN_DATAGRAM_SIZE);
} else {
@@ -395,17 +354,15 @@
CDC_NCM_MIN_DATAGRAM_SIZE;
/* if value changed, update device */
- req.bmRequestType = USB_TYPE_CLASS | USB_DIR_OUT |
- USB_RECIP_INTERFACE;
- req.bNotificationType = USB_CDC_SET_MAX_DATAGRAM_SIZE;
- req.wValue = 0;
- req.wIndex = cpu_to_le16(iface_no);
- req.wLength = 2;
- max_datagram_size = cpu_to_le16(ctx->max_datagram_size);
-
- err = cdc_ncm_do_request(ctx, &req, &max_datagram_size,
- 0, NULL, 1000);
- if (err)
+ err = usb_control_msg(ctx->udev,
+ usb_sndctrlpipe(ctx->udev, 0),
+ USB_CDC_SET_MAX_DATAGRAM_SIZE,
+ USB_TYPE_CLASS | USB_DIR_OUT
+ | USB_RECIP_INTERFACE,
+ 0,
+ iface_no, &max_datagram_size,
+ 2, 1000);
+ if (err < 0)
pr_debug("SET_MAX_DATAGRAM_SIZE failed\n");
}
@@ -671,7 +628,7 @@
u32 rem;
u32 offset;
u32 last_offset;
- u16 n = 0;
+ u16 n = 0, index;
u8 ready2send = 0;
/* if there is a remaining skb, it gets priority */
@@ -859,8 +816,8 @@
cpu_to_le16(sizeof(ctx->tx_ncm.nth16));
ctx->tx_ncm.nth16.wSequence = cpu_to_le16(ctx->tx_seq);
ctx->tx_ncm.nth16.wBlockLength = cpu_to_le16(last_offset);
- ctx->tx_ncm.nth16.wNdpIndex = ALIGN(sizeof(struct usb_cdc_ncm_nth16),
- ctx->tx_ndp_modulus);
+ index = ALIGN(sizeof(struct usb_cdc_ncm_nth16), ctx->tx_ndp_modulus);
+ ctx->tx_ncm.nth16.wNdpIndex = cpu_to_le16(index);
memcpy(skb_out->data, &(ctx->tx_ncm.nth16), sizeof(ctx->tx_ncm.nth16));
ctx->tx_seq++;
@@ -873,12 +830,11 @@
ctx->tx_ncm.ndp16.wLength = cpu_to_le16(rem);
ctx->tx_ncm.ndp16.wNextNdpIndex = 0; /* reserved */
- memcpy(((u8 *)skb_out->data) + ctx->tx_ncm.nth16.wNdpIndex,
+ memcpy(((u8 *)skb_out->data) + index,
&(ctx->tx_ncm.ndp16),
sizeof(ctx->tx_ncm.ndp16));
- memcpy(((u8 *)skb_out->data) + ctx->tx_ncm.nth16.wNdpIndex +
- sizeof(ctx->tx_ncm.ndp16),
+ memcpy(((u8 *)skb_out->data) + index + sizeof(ctx->tx_ncm.ndp16),
&(ctx->tx_ncm.dpe16),
(ctx->tx_curr_frame_num + 1) *
sizeof(struct usb_cdc_ncm_dpe16));
diff --git a/drivers/net/usb/rtl8150.c b/drivers/net/usb/rtl8150.c
index 041fb7d..ef3b236 100644
--- a/drivers/net/usb/rtl8150.c
+++ b/drivers/net/usb/rtl8150.c
@@ -977,7 +977,6 @@
usb_set_intfdata(intf, NULL);
if (dev) {
set_bit(RTL8150_UNPLUG, &dev->flags);
- tasklet_disable(&dev->tl);
tasklet_kill(&dev->tl);
unregister_netdev(dev->netdev);
unlink_all_urbs(dev);
diff --git a/drivers/net/wireless/ath/ath5k/base.c b/drivers/net/wireless/ath/ath5k/base.c
index f54dff4..c3119a6 100644
--- a/drivers/net/wireless/ath/ath5k/base.c
+++ b/drivers/net/wireless/ath/ath5k/base.c
@@ -1735,6 +1735,8 @@
if (dma_mapping_error(ah->dev, bf->skbaddr)) {
ATH5K_ERR(ah, "beacon DMA mapping failed\n");
+ dev_kfree_skb_any(skb);
+ bf->skb = NULL;
return -EIO;
}
@@ -1819,8 +1821,6 @@
ath5k_txbuf_free_skb(ah, avf->bbuf);
avf->bbuf->skb = skb;
ret = ath5k_beacon_setup(ah, avf->bbuf);
- if (ret)
- avf->bbuf->skb = NULL;
out:
return ret;
}
@@ -1840,6 +1840,7 @@
struct ath5k_vif *avf;
struct ath5k_buf *bf;
struct sk_buff *skb;
+ int err;
ATH5K_DBG_UNLIMIT(ah, ATH5K_DEBUG_BEACON, "in beacon_send\n");
@@ -1888,11 +1889,6 @@
avf = (void *)vif->drv_priv;
bf = avf->bbuf;
- if (unlikely(bf->skb == NULL || ah->opmode == NL80211_IFTYPE_STATION ||
- ah->opmode == NL80211_IFTYPE_MONITOR)) {
- ATH5K_WARN(ah, "bf=%p bf_skb=%p\n", bf, bf ? bf->skb : NULL);
- return;
- }
/*
* Stop any current dma and put the new frame on the queue.
@@ -1906,8 +1902,17 @@
/* refresh the beacon for AP or MESH mode */
if (ah->opmode == NL80211_IFTYPE_AP ||
- ah->opmode == NL80211_IFTYPE_MESH_POINT)
- ath5k_beacon_update(ah->hw, vif);
+ ah->opmode == NL80211_IFTYPE_MESH_POINT) {
+ err = ath5k_beacon_update(ah->hw, vif);
+ if (err)
+ return;
+ }
+
+ if (unlikely(bf->skb == NULL || ah->opmode == NL80211_IFTYPE_STATION ||
+ ah->opmode == NL80211_IFTYPE_MONITOR)) {
+ ATH5K_WARN(ah, "bf=%p bf_skb=%p\n", bf, bf->skb);
+ return;
+ }
trace_ath5k_tx(ah, bf->skb, &ah->txqs[ah->bhalq]);
diff --git a/drivers/net/wireless/ath/ath9k/ar9002_hw.c b/drivers/net/wireless/ath/ath9k/ar9002_hw.c
index 9ff7c30..44d9d8d 100644
--- a/drivers/net/wireless/ath/ath9k/ar9002_hw.c
+++ b/drivers/net/wireless/ath/ath9k/ar9002_hw.c
@@ -309,11 +309,7 @@
u8 i;
u32 val;
- if (ah->is_pciexpress != true)
- return;
-
- /* Do not touch SerDes registers */
- if (ah->config.pcie_powersave_enable == 2)
+ if (ah->is_pciexpress != true || ah->aspm_enabled != true)
return;
/* Nothing to do on restore for 11N */
diff --git a/drivers/net/wireless/ath/ath9k/ar9003_eeprom.c b/drivers/net/wireless/ath/ath9k/ar9003_eeprom.c
index d109c25..c34bef1 100644
--- a/drivers/net/wireless/ath/ath9k/ar9003_eeprom.c
+++ b/drivers/net/wireless/ath/ath9k/ar9003_eeprom.c
@@ -307,7 +307,7 @@
{ { CTL(60, 0), CTL(60, 1), CTL(60, 0), CTL(60, 0) } },
{ { CTL(60, 1), CTL(60, 0), CTL(60, 0), CTL(60, 1) } },
- { { CTL(60, 1), CTL(60, 0), CTL(0, 0), CTL(0, 0) } },
+ { { CTL(60, 1), CTL(60, 0), CTL(60, 0), CTL(60, 0) } },
{ { CTL(60, 0), CTL(60, 1), CTL(60, 0), CTL(60, 0) } },
{ { CTL(60, 0), CTL(60, 1), CTL(60, 0), CTL(60, 0) } },
@@ -884,7 +884,7 @@
{ { CTL(60, 0), CTL(60, 1), CTL(60, 0), CTL(60, 0) } },
{ { CTL(60, 1), CTL(60, 0), CTL(60, 0), CTL(60, 1) } },
- { { CTL(60, 1), CTL(60, 0), CTL(0, 0), CTL(0, 0) } },
+ { { CTL(60, 1), CTL(60, 0), CTL(60, 0), CTL(60, 0) } },
{ { CTL(60, 0), CTL(60, 1), CTL(60, 0), CTL(60, 0) } },
{ { CTL(60, 0), CTL(60, 1), CTL(60, 0), CTL(60, 0) } },
@@ -2040,7 +2040,7 @@
{ { CTL(60, 0), CTL(60, 1), CTL(60, 0), CTL(60, 0) } },
{ { CTL(60, 1), CTL(60, 0), CTL(60, 0), CTL(60, 1) } },
- { { CTL(60, 1), CTL(60, 0), CTL(0, 0), CTL(0, 0) } },
+ { { CTL(60, 1), CTL(60, 0), CTL(60, 0), CTL(60, 0) } },
{ { CTL(60, 0), CTL(60, 1), CTL(60, 0), CTL(60, 0) } },
{ { CTL(60, 0), CTL(60, 1), CTL(60, 0), CTL(60, 0) } },
@@ -3734,7 +3734,7 @@
}
} else {
reg_pmu_set = (5 << 1) | (7 << 4) |
- (1 << 8) | (2 << 14) |
+ (2 << 8) | (2 << 14) |
(6 << 17) | (1 << 20) |
(3 << 24) | (1 << 28);
}
diff --git a/drivers/net/wireless/ath/ath9k/ar9003_hw.c b/drivers/net/wireless/ath/ath9k/ar9003_hw.c
index 8efdec2..ad2bb2b 100644
--- a/drivers/net/wireless/ath/ath9k/ar9003_hw.c
+++ b/drivers/net/wireless/ath/ath9k/ar9003_hw.c
@@ -519,11 +519,7 @@
int restore,
int power_off)
{
- if (ah->is_pciexpress != true)
- return;
-
- /* Do not touch SerDes registers */
- if (ah->config.pcie_powersave_enable == 2)
+ if (ah->is_pciexpress != true || ah->aspm_enabled != true)
return;
/* Nothing to do on restore for 11N */
diff --git a/drivers/net/wireless/ath/ath9k/ar9003_phy.h b/drivers/net/wireless/ath/ath9k/ar9003_phy.h
index 6de3f0b..5c59042 100644
--- a/drivers/net/wireless/ath/ath9k/ar9003_phy.h
+++ b/drivers/net/wireless/ath/ath9k/ar9003_phy.h
@@ -850,7 +850,7 @@
#define AR_PHY_TPC_11_B1 (AR_SM1_BASE + 0x220)
#define AR_PHY_PDADC_TAB_1 (AR_SM1_BASE + 0x240)
#define AR_PHY_TX_IQCAL_STATUS_B1 (AR_SM1_BASE + 0x48c)
-#define AR_PHY_TX_IQCAL_CORR_COEFF_B1(_i) (AR_SM_BASE + 0x450 + ((_i) << 2))
+#define AR_PHY_TX_IQCAL_CORR_COEFF_B1(_i) (AR_SM1_BASE + 0x450 + ((_i) << 2))
/*
* Channel 2 Register Map
diff --git a/drivers/net/wireless/ath/ath9k/hw.c b/drivers/net/wireless/ath/ath9k/hw.c
index 8006ce0..8dcefe7 100644
--- a/drivers/net/wireless/ath/ath9k/hw.c
+++ b/drivers/net/wireless/ath/ath9k/hw.c
@@ -318,6 +318,14 @@
REG_WRITE(ah, AR_PCIE_SERDES2, 0x00000000);
}
+static void ath9k_hw_aspm_init(struct ath_hw *ah)
+{
+ struct ath_common *common = ath9k_hw_common(ah);
+
+ if (common->bus_ops->aspm_init)
+ common->bus_ops->aspm_init(common);
+}
+
/* This should work for all families including legacy */
static bool ath9k_hw_chip_test(struct ath_hw *ah)
{
@@ -378,7 +386,6 @@
ah->config.additional_swba_backoff = 0;
ah->config.ack_6mb = 0x0;
ah->config.cwm_ignore_extcca = 0;
- ah->config.pcie_powersave_enable = 0;
ah->config.pcie_clock_req = 0;
ah->config.pcie_waen = 0;
ah->config.analog_shiftreg = 1;
@@ -598,7 +605,7 @@
if (ah->is_pciexpress)
- ath9k_hw_configpcipowersave(ah, 0, 0);
+ ath9k_hw_aspm_init(ah);
else
ath9k_hw_disablepcie(ah);
diff --git a/drivers/net/wireless/ath/ath9k/hw.h b/drivers/net/wireless/ath/ath9k/hw.h
index 6acd0f9..c798890 100644
--- a/drivers/net/wireless/ath/ath9k/hw.h
+++ b/drivers/net/wireless/ath/ath9k/hw.h
@@ -219,7 +219,6 @@
int additional_swba_backoff;
int ack_6mb;
u32 cwm_ignore_extcca;
- u8 pcie_powersave_enable;
bool pcieSerDesWrite;
u8 pcie_clock_req;
u32 pcie_waen;
@@ -673,6 +672,7 @@
bool sw_mgmt_crypto;
bool is_pciexpress;
+ bool aspm_enabled;
bool is_monitoring;
bool need_an_top2_fixup;
u16 tx_trig_level;
@@ -874,6 +874,7 @@
bool (*eeprom_read)(struct ath_common *common, u32 off, u16 *data);
void (*bt_coex_prep)(struct ath_common *common);
void (*extn_synch_en)(struct ath_common *common);
+ void (*aspm_init)(struct ath_common *common);
};
static inline struct ath_common *ath9k_hw_common(struct ath_hw *ah)
diff --git a/drivers/net/wireless/ath/ath9k/init.c b/drivers/net/wireless/ath/ath9k/init.c
index ac51071..aa0ff7e 100644
--- a/drivers/net/wireless/ath/ath9k/init.c
+++ b/drivers/net/wireless/ath/ath9k/init.c
@@ -670,8 +670,10 @@
static void ath9k_init_txpower_limits(struct ath_softc *sc)
{
struct ath_hw *ah = sc->sc_ah;
+ struct ath_common *common = ath9k_hw_common(sc->sc_ah);
struct ath9k_channel *curchan = ah->curchan;
+ ah->txchainmask = common->tx_chainmask;
if (ah->caps.hw_caps & ATH9K_HW_CAP_2GHZ)
ath9k_init_band_txpower(sc, IEEE80211_BAND_2GHZ);
if (ah->caps.hw_caps & ATH9K_HW_CAP_5GHZ)
diff --git a/drivers/net/wireless/ath/ath9k/pci.c b/drivers/net/wireless/ath/ath9k/pci.c
index 3bad0b2..be4ea13 100644
--- a/drivers/net/wireless/ath/ath9k/pci.c
+++ b/drivers/net/wireless/ath/ath9k/pci.c
@@ -16,6 +16,7 @@
#include <linux/nl80211.h>
#include <linux/pci.h>
+#include <linux/pci-aspm.h>
#include <linux/ath9k_platform.h>
#include "ath9k.h"
@@ -115,12 +116,38 @@
pci_write_config_byte(pdev, sc->sc_ah->caps.pcie_lcr_offset, lnkctl);
}
+static void ath_pci_aspm_init(struct ath_common *common)
+{
+ struct ath_softc *sc = (struct ath_softc *) common->priv;
+ struct ath_hw *ah = sc->sc_ah;
+ struct pci_dev *pdev = to_pci_dev(sc->dev);
+ struct pci_dev *parent;
+ int pos;
+ u8 aspm;
+
+ if (!pci_is_pcie(pdev))
+ return;
+
+ parent = pdev->bus->self;
+ if (WARN_ON(!parent))
+ return;
+
+ pos = pci_pcie_cap(parent);
+ pci_read_config_byte(parent, pos + PCI_EXP_LNKCTL, &aspm);
+ if (aspm & (PCIE_LINK_STATE_L0S | PCIE_LINK_STATE_L1)) {
+ ah->aspm_enabled = true;
+ /* Initialize PCIe PM and SERDES registers. */
+ ath9k_hw_configpcipowersave(ah, 0, 0);
+ }
+}
+
static const struct ath_bus_ops ath_pci_bus_ops = {
.ath_bus_type = ATH_PCI,
.read_cachesize = ath_pci_read_cachesize,
.eeprom_read = ath_pci_eeprom_read,
.bt_coex_prep = ath_pci_bt_coex_prep,
.extn_synch_en = ath_pci_extn_synch_enable,
+ .aspm_init = ath_pci_aspm_init,
};
static int ath_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
diff --git a/drivers/net/wireless/b43/dma.c b/drivers/net/wireless/b43/dma.c
index 83cba22..481e534 100644
--- a/drivers/net/wireless/b43/dma.c
+++ b/drivers/net/wireless/b43/dma.c
@@ -795,9 +795,23 @@
u32 tmp;
u16 mmio_base;
- tmp = b43_read32(dev, SSB_TMSHIGH);
- if (tmp & SSB_TMSHIGH_DMA64)
- return DMA_BIT_MASK(64);
+ switch (dev->dev->bus_type) {
+#ifdef CONFIG_B43_BCMA
+ case B43_BUS_BCMA:
+ tmp = bcma_aread32(dev->dev->bdev, BCMA_IOST);
+ if (tmp & BCMA_IOST_DMA64)
+ return DMA_BIT_MASK(64);
+ break;
+#endif
+#ifdef CONFIG_B43_SSB
+ case B43_BUS_SSB:
+ tmp = ssb_read32(dev->dev->sdev, SSB_TMSHIGH);
+ if (tmp & SSB_TMSHIGH_DMA64)
+ return DMA_BIT_MASK(64);
+ break;
+#endif
+ }
+
mmio_base = b43_dmacontroller_base(0, 0);
b43_write32(dev, mmio_base + B43_DMA32_TXCTL, B43_DMA32_TXADDREXT_MASK);
tmp = b43_read32(dev, mmio_base + B43_DMA32_TXCTL);
diff --git a/drivers/net/wireless/iwlegacy/iwl-3945.c b/drivers/net/wireless/iwlegacy/iwl-3945.c
index dab67a1..73fe3cd 100644
--- a/drivers/net/wireless/iwlegacy/iwl-3945.c
+++ b/drivers/net/wireless/iwlegacy/iwl-3945.c
@@ -1746,7 +1746,11 @@
}
memcpy(active_rxon, staging_rxon, sizeof(*active_rxon));
-
+ /*
+ * We do not commit tx power settings while channel changing,
+ * do it now if tx power changed.
+ */
+ iwl_legacy_set_tx_power(priv, priv->tx_power_next, false);
return 0;
}
diff --git a/drivers/net/wireless/iwlegacy/iwl-4965.c b/drivers/net/wireless/iwlegacy/iwl-4965.c
index bd4b000..ecdc6e5 100644
--- a/drivers/net/wireless/iwlegacy/iwl-4965.c
+++ b/drivers/net/wireless/iwlegacy/iwl-4965.c
@@ -1235,7 +1235,12 @@
memcpy(active_rxon, &ctx->staging, sizeof(*active_rxon));
iwl_legacy_print_rx_config_cmd(priv, ctx);
- goto set_tx_power;
+ /*
+ * We do not commit tx power settings while channel changing,
+ * do it now if tx power changed.
+ */
+ iwl_legacy_set_tx_power(priv, priv->tx_power_next, false);
+ return 0;
}
/* If we are currently associated and the new config requires
@@ -1315,7 +1320,6 @@
iwl4965_init_sensitivity(priv);
-set_tx_power:
/* If we issue a new RXON command which required a tune then we must
* send a new TXPOWER command or we won't be able to Tx any frames */
ret = iwl_legacy_set_tx_power(priv, priv->tx_power_next, true);
diff --git a/drivers/net/wireless/iwlwifi/iwl-5000.c b/drivers/net/wireless/iwlwifi/iwl-5000.c
index 3eeb12e..c95cefd 100644
--- a/drivers/net/wireless/iwlwifi/iwl-5000.c
+++ b/drivers/net/wireless/iwlwifi/iwl-5000.c
@@ -365,6 +365,7 @@
.chain_noise_scale = 1000,
.wd_timeout = IWL_LONG_WD_TIMEOUT,
.max_event_log_size = 512,
+ .no_idle_support = true,
};
static struct iwl_ht_params iwl5000_ht_params = {
.ht_greenfield_support = true,
diff --git a/drivers/net/wireless/iwlwifi/iwl-core.h b/drivers/net/wireless/iwlwifi/iwl-core.h
index 3e6bb73..02817a4 100644
--- a/drivers/net/wireless/iwlwifi/iwl-core.h
+++ b/drivers/net/wireless/iwlwifi/iwl-core.h
@@ -135,6 +135,7 @@
* @temperature_kelvin: temperature report by uCode in kelvin
* @max_event_log_size: size of event log buffer size for ucode event logging
* @shadow_reg_enable: HW shadhow register bit
+ * @no_idle_support: do not support idle mode
*/
struct iwl_base_params {
int eeprom_size;
@@ -156,6 +157,7 @@
bool temperature_kelvin;
u32 max_event_log_size;
const bool shadow_reg_enable;
+ const bool no_idle_support;
};
/*
* @advanced_bt_coexist: support advanced bt coexist
diff --git a/drivers/net/wireless/iwlwifi/iwl-pci.c b/drivers/net/wireless/iwlwifi/iwl-pci.c
index fb7e436..69d4ec4 100644
--- a/drivers/net/wireless/iwlwifi/iwl-pci.c
+++ b/drivers/net/wireless/iwlwifi/iwl-pci.c
@@ -134,6 +134,7 @@
static void iwl_pci_set_drv_data(struct iwl_bus *bus, void *drv_data)
{
bus->drv_data = drv_data;
+ pci_set_drvdata(IWL_BUS_GET_PCI_DEV(bus), drv_data);
}
static void iwl_pci_get_hw_id(struct iwl_bus *bus, char buf[],
@@ -454,8 +455,6 @@
pci_write_config_word(pdev, PCI_COMMAND, pci_cmd);
}
- pci_set_drvdata(pdev, bus);
-
bus->dev = &pdev->dev;
bus->irq = pdev->irq;
bus->ops = &pci_ops;
@@ -494,11 +493,12 @@
static void __devexit iwl_pci_remove(struct pci_dev *pdev)
{
- struct iwl_bus *bus = pci_get_drvdata(pdev);
+ struct iwl_priv *priv = pci_get_drvdata(pdev);
+ void *bus_specific = priv->bus->bus_specific;
- iwl_remove(bus->drv_data);
+ iwl_remove(priv);
- iwl_pci_down(bus);
+ iwl_pci_down(bus_specific);
}
#ifdef CONFIG_PM
@@ -506,20 +506,20 @@
static int iwl_pci_suspend(struct device *device)
{
struct pci_dev *pdev = to_pci_dev(device);
- struct iwl_bus *bus = pci_get_drvdata(pdev);
+ struct iwl_priv *priv = pci_get_drvdata(pdev);
/* Before you put code here, think about WoWLAN. You cannot check here
* whether WoWLAN is enabled or not, and your code will run even if
* WoWLAN is enabled - don't kill the NIC, someone may need it in Sx.
*/
- return iwl_suspend(bus->drv_data);
+ return iwl_suspend(priv);
}
static int iwl_pci_resume(struct device *device)
{
struct pci_dev *pdev = to_pci_dev(device);
- struct iwl_bus *bus = pci_get_drvdata(pdev);
+ struct iwl_priv *priv = pci_get_drvdata(pdev);
/* Before you put code here, think about WoWLAN. You cannot check here
* whether WoWLAN is enabled or not, and your code will run even if
@@ -532,7 +532,7 @@
*/
pci_write_config_byte(pdev, PCI_CFG_RETRY_TIMEOUT, 0x00);
- return iwl_resume(bus->drv_data);
+ return iwl_resume(priv);
}
static SIMPLE_DEV_PM_OPS(iwl_dev_pm_ops, iwl_pci_suspend, iwl_pci_resume);
diff --git a/drivers/net/wireless/iwlwifi/iwl-power.c b/drivers/net/wireless/iwlwifi/iwl-power.c
index 3ec619c..cd64df0 100644
--- a/drivers/net/wireless/iwlwifi/iwl-power.c
+++ b/drivers/net/wireless/iwlwifi/iwl-power.c
@@ -349,7 +349,8 @@
if (priv->wowlan)
iwl_static_sleep_cmd(priv, cmd, IWL_POWER_INDEX_5, dtimper);
- else if (priv->hw->conf.flags & IEEE80211_CONF_IDLE)
+ else if (!priv->cfg->base_params->no_idle_support &&
+ priv->hw->conf.flags & IEEE80211_CONF_IDLE)
iwl_static_sleep_cmd(priv, cmd, IWL_POWER_INDEX_5, 20);
else if (iwl_tt_is_low_power_state(priv)) {
/* in thermal throttling low power state */
diff --git a/drivers/net/wireless/rt2x00/rt2800lib.c b/drivers/net/wireless/rt2x00/rt2800lib.c
index 84ab7d1..ef67f67 100644
--- a/drivers/net/wireless/rt2x00/rt2800lib.c
+++ b/drivers/net/wireless/rt2x00/rt2800lib.c
@@ -703,8 +703,7 @@
/*
* Add space for the TXWI in front of the skb.
*/
- skb_push(entry->skb, TXWI_DESC_SIZE);
- memset(entry->skb, 0, TXWI_DESC_SIZE);
+ memset(skb_push(entry->skb, TXWI_DESC_SIZE), 0, TXWI_DESC_SIZE);
/*
* Register descriptor details in skb frame descriptor.
diff --git a/drivers/net/wireless/rt2x00/rt2800usb.c b/drivers/net/wireless/rt2x00/rt2800usb.c
index 5075593..9395631 100644
--- a/drivers/net/wireless/rt2x00/rt2800usb.c
+++ b/drivers/net/wireless/rt2x00/rt2800usb.c
@@ -921,6 +921,8 @@
{ USB_DEVICE(0x07d1, 0x3c16) },
/* Draytek */
{ USB_DEVICE(0x07fa, 0x7712) },
+ /* DVICO */
+ { USB_DEVICE(0x0fe9, 0xb307) },
/* Edimax */
{ USB_DEVICE(0x7392, 0x7711) },
{ USB_DEVICE(0x7392, 0x7717) },
diff --git a/drivers/net/wireless/rt2x00/rt2x00lib.h b/drivers/net/wireless/rt2x00/rt2x00lib.h
index 15cdc7e..4cdf247 100644
--- a/drivers/net/wireless/rt2x00/rt2x00lib.h
+++ b/drivers/net/wireless/rt2x00/rt2x00lib.h
@@ -355,7 +355,8 @@
return CIPHER_NONE;
}
-static inline void rt2x00crypto_create_tx_descriptor(struct queue_entry *entry,
+static inline void rt2x00crypto_create_tx_descriptor(struct rt2x00_dev *rt2x00dev,
+ struct sk_buff *skb,
struct txentry_desc *txdesc)
{
}
diff --git a/drivers/net/wireless/rt2x00/rt2x00mac.c b/drivers/net/wireless/rt2x00/rt2x00mac.c
index 8efab39..4ccf238 100644
--- a/drivers/net/wireless/rt2x00/rt2x00mac.c
+++ b/drivers/net/wireless/rt2x00/rt2x00mac.c
@@ -113,7 +113,7 @@
* due to possible race conditions in mac80211.
*/
if (!test_bit(DEVICE_STATE_PRESENT, &rt2x00dev->flags))
- goto exit_fail;
+ goto exit_free_skb;
/*
* Use the ATIM queue if appropriate and present.
@@ -127,7 +127,7 @@
ERROR(rt2x00dev,
"Attempt to send packet over invalid queue %d.\n"
"Please file bug report to %s.\n", qid, DRV_PROJECT);
- goto exit_fail;
+ goto exit_free_skb;
}
/*
@@ -159,6 +159,7 @@
exit_fail:
rt2x00queue_pause_queue(queue);
+ exit_free_skb:
dev_kfree_skb_any(skb);
}
EXPORT_SYMBOL_GPL(rt2x00mac_tx);
diff --git a/drivers/net/wireless/rt2x00/rt73usb.c b/drivers/net/wireless/rt2x00/rt73usb.c
index 6a93939..0baeb89 100644
--- a/drivers/net/wireless/rt2x00/rt73usb.c
+++ b/drivers/net/wireless/rt2x00/rt73usb.c
@@ -2420,6 +2420,7 @@
/* Buffalo */
{ USB_DEVICE(0x0411, 0x00d8) },
{ USB_DEVICE(0x0411, 0x00d9) },
+ { USB_DEVICE(0x0411, 0x00e6) },
{ USB_DEVICE(0x0411, 0x00f4) },
{ USB_DEVICE(0x0411, 0x0116) },
{ USB_DEVICE(0x0411, 0x0119) },
diff --git a/drivers/net/wireless/rtlwifi/pci.c b/drivers/net/wireless/rtlwifi/pci.c
index 5efd578..56f1235 100644
--- a/drivers/net/wireless/rtlwifi/pci.c
+++ b/drivers/net/wireless/rtlwifi/pci.c
@@ -1696,15 +1696,17 @@
pcipriv->ndis_adapter.devnumber = PCI_SLOT(pdev->devfn);
pcipriv->ndis_adapter.funcnumber = PCI_FUNC(pdev->devfn);
- /*find bridge info */
- pcipriv->ndis_adapter.pcibridge_vendorid = bridge_pdev->vendor;
- for (tmp = 0; tmp < PCI_BRIDGE_VENDOR_MAX; tmp++) {
- if (bridge_pdev->vendor == pcibridge_vendors[tmp]) {
- pcipriv->ndis_adapter.pcibridge_vendor = tmp;
- RT_TRACE(rtlpriv, COMP_INIT, DBG_DMESG,
- ("Pci Bridge Vendor is found index: %d\n",
- tmp));
- break;
+ if (bridge_pdev) {
+ /*find bridge info if available */
+ pcipriv->ndis_adapter.pcibridge_vendorid = bridge_pdev->vendor;
+ for (tmp = 0; tmp < PCI_BRIDGE_VENDOR_MAX; tmp++) {
+ if (bridge_pdev->vendor == pcibridge_vendors[tmp]) {
+ pcipriv->ndis_adapter.pcibridge_vendor = tmp;
+ RT_TRACE(rtlpriv, COMP_INIT, DBG_DMESG,
+ ("Pci Bridge Vendor is found index:"
+ " %d\n", tmp));
+ break;
+ }
}
}
diff --git a/drivers/net/wireless/rtlwifi/rtl8192cu/sw.c b/drivers/net/wireless/rtlwifi/rtl8192cu/sw.c
index 942f7a3..ef63c0d 100644
--- a/drivers/net/wireless/rtlwifi/rtl8192cu/sw.c
+++ b/drivers/net/wireless/rtlwifi/rtl8192cu/sw.c
@@ -281,6 +281,8 @@
{RTL_USB_DEVICE(USB_VENDER_ID_REALTEK, 0x817d, rtl92cu_hal_cfg)},
/* 8188CE-VAU USB minCard (b/g mode only) */
{RTL_USB_DEVICE(USB_VENDER_ID_REALTEK, 0x817e, rtl92cu_hal_cfg)},
+ /* 8188RU in Alfa AWUS036NHR */
+ {RTL_USB_DEVICE(USB_VENDER_ID_REALTEK, 0x817f, rtl92cu_hal_cfg)},
/* 8188 Combo for BC4 */
{RTL_USB_DEVICE(USB_VENDER_ID_REALTEK, 0x8754, rtl92cu_hal_cfg)},
@@ -303,20 +305,23 @@
{RTL_USB_DEVICE(0x0eb0, 0x9071, rtl92cu_hal_cfg)}, /*NO Brand - Etop*/
/* HP - Lite-On ,8188CUS Slim Combo */
{RTL_USB_DEVICE(0x103c, 0x1629, rtl92cu_hal_cfg)},
+ {RTL_USB_DEVICE(0x13d3, 0x3357, rtl92cu_hal_cfg)}, /* AzureWave */
{RTL_USB_DEVICE(0x2001, 0x3308, rtl92cu_hal_cfg)}, /*D-Link - Alpha*/
{RTL_USB_DEVICE(0x2019, 0xab2a, rtl92cu_hal_cfg)}, /*Planex - Abocom*/
{RTL_USB_DEVICE(0x2019, 0xed17, rtl92cu_hal_cfg)}, /*PCI - Edimax*/
{RTL_USB_DEVICE(0x20f4, 0x648b, rtl92cu_hal_cfg)}, /*TRENDnet - Cameo*/
{RTL_USB_DEVICE(0x7392, 0x7811, rtl92cu_hal_cfg)}, /*Edimax - Edimax*/
- {RTL_USB_DEVICE(0x3358, 0x13d3, rtl92cu_hal_cfg)}, /*Azwave 8188CE-VAU*/
+ {RTL_USB_DEVICE(0x13d3, 0x3358, rtl92cu_hal_cfg)}, /*Azwave 8188CE-VAU*/
/* Russian customer -Azwave (8188CE-VAU b/g mode only) */
- {RTL_USB_DEVICE(0x3359, 0x13d3, rtl92cu_hal_cfg)},
+ {RTL_USB_DEVICE(0x13d3, 0x3359, rtl92cu_hal_cfg)},
+ {RTL_USB_DEVICE(0x4855, 0x0090, rtl92cu_hal_cfg)}, /* Feixun */
+ {RTL_USB_DEVICE(0x4855, 0x0091, rtl92cu_hal_cfg)}, /* NetweeN-Feixun */
+ {RTL_USB_DEVICE(0x9846, 0x9041, rtl92cu_hal_cfg)}, /* Netgear Cameo */
/****** 8192CU ********/
{RTL_USB_DEVICE(0x0586, 0x341f, rtl92cu_hal_cfg)}, /*Zyxel -Abocom*/
{RTL_USB_DEVICE(0x07aa, 0x0056, rtl92cu_hal_cfg)}, /*ATKK-Gemtek*/
{RTL_USB_DEVICE(0x07b8, 0x8178, rtl92cu_hal_cfg)}, /*Funai -Abocom*/
- {RTL_USB_DEVICE(0x07b8, 0x8178, rtl92cu_hal_cfg)}, /*Abocom -Abocom*/
{RTL_USB_DEVICE(0x2001, 0x3307, rtl92cu_hal_cfg)}, /*D-Link-Cameo*/
{RTL_USB_DEVICE(0x2001, 0x3309, rtl92cu_hal_cfg)}, /*D-Link-Alpha*/
{RTL_USB_DEVICE(0x2001, 0x330a, rtl92cu_hal_cfg)}, /*D-Link-Alpha*/
diff --git a/drivers/net/wireless/wl1251/acx.c b/drivers/net/wireless/wl1251/acx.c
index ef8370e..ad87a1a 100644
--- a/drivers/net/wireless/wl1251/acx.c
+++ b/drivers/net/wireless/wl1251/acx.c
@@ -140,8 +140,6 @@
auth->sleep_auth = sleep_auth;
ret = wl1251_cmd_configure(wl, ACX_SLEEP_AUTH, auth, sizeof(*auth));
- if (ret < 0)
- return ret;
out:
kfree(auth);
@@ -681,10 +679,8 @@
ret = wl1251_cmd_configure(wl, ACX_CCA_THRESHOLD,
detection, sizeof(*detection));
- if (ret < 0) {
+ if (ret < 0)
wl1251_warning("failed to set cca threshold: %d", ret);
- return ret;
- }
out:
kfree(detection);
diff --git a/drivers/net/wireless/wl1251/cmd.c b/drivers/net/wireless/wl1251/cmd.c
index 81f164b..d14d69d 100644
--- a/drivers/net/wireless/wl1251/cmd.c
+++ b/drivers/net/wireless/wl1251/cmd.c
@@ -241,7 +241,7 @@
if (ret < 0) {
wl1251_error("tx %s cmd for channel %d failed",
enable ? "start" : "stop", channel);
- return ret;
+ goto out;
}
wl1251_debug(DEBUG_BOOT, "tx %s cmd channel %d",
diff --git a/drivers/of/address.c b/drivers/of/address.c
index da1f4b9..72c33fb 100644
--- a/drivers/of/address.c
+++ b/drivers/of/address.c
@@ -610,6 +610,6 @@
if (of_address_to_resource(np, index, &res))
return NULL;
- return ioremap(res.start, 1 + res.end - res.start);
+ return ioremap(res.start, resource_size(&res));
}
EXPORT_SYMBOL(of_iomap);
diff --git a/drivers/of/base.c b/drivers/of/base.c
index 02ed367..3ff22e3 100644
--- a/drivers/of/base.c
+++ b/drivers/of/base.c
@@ -610,8 +610,9 @@
*
* The out_value is modified only if a valid u32 value can be decoded.
*/
-int of_property_read_u32_array(const struct device_node *np, char *propname,
- u32 *out_values, size_t sz)
+int of_property_read_u32_array(const struct device_node *np,
+ const char *propname, u32 *out_values,
+ size_t sz)
{
struct property *prop = of_find_property(np, propname, NULL);
const __be32 *val;
@@ -645,7 +646,7 @@
*
* The out_string pointer is modified only if a valid string can be decoded.
*/
-int of_property_read_string(struct device_node *np, char *propname,
+int of_property_read_string(struct device_node *np, const char *propname,
const char **out_string)
{
struct property *prop = of_find_property(np, propname, NULL);
diff --git a/drivers/of/gpio.c b/drivers/of/gpio.c
index 3007662..ef0105f 100644
--- a/drivers/of/gpio.c
+++ b/drivers/of/gpio.c
@@ -127,8 +127,8 @@
* gpio chips. This function performs only one sanity check: whether gpio
* is less than ngpios (that is specified in the gpio_chip).
*/
-static int of_gpio_simple_xlate(struct gpio_chip *gc, struct device_node *np,
- const void *gpio_spec, u32 *flags)
+int of_gpio_simple_xlate(struct gpio_chip *gc, struct device_node *np,
+ const void *gpio_spec, u32 *flags)
{
const __be32 *gpio = gpio_spec;
const u32 n = be32_to_cpup(gpio);
@@ -152,6 +152,7 @@
return n;
}
+EXPORT_SYMBOL(of_gpio_simple_xlate);
/**
* of_mm_gpiochip_add - Add memory mapped GPIO chip (bank)
diff --git a/drivers/pci/hotplug/acpiphp_glue.c b/drivers/pci/hotplug/acpiphp_glue.c
index a70fa89..2202857 100644
--- a/drivers/pci/hotplug/acpiphp_glue.c
+++ b/drivers/pci/hotplug/acpiphp_glue.c
@@ -110,7 +110,7 @@
}
-static struct acpi_dock_ops acpiphp_dock_ops = {
+static const struct acpi_dock_ops acpiphp_dock_ops = {
.handler = handle_hotplug_event_func,
};
diff --git a/drivers/pci/hotplug/pcihp_slot.c b/drivers/pci/hotplug/pcihp_slot.c
index 749fdf0..753b21a 100644
--- a/drivers/pci/hotplug/pcihp_slot.c
+++ b/drivers/pci/hotplug/pcihp_slot.c
@@ -158,47 +158,6 @@
*/
}
-/* Program PCIE MaxPayload setting on device: ensure parent maxpayload <= device */
-static int pci_set_payload(struct pci_dev *dev)
-{
- int pos, ppos;
- u16 pctl, psz;
- u16 dctl, dsz, dcap, dmax;
- struct pci_dev *parent;
-
- parent = dev->bus->self;
- pos = pci_find_capability(dev, PCI_CAP_ID_EXP);
- if (!pos)
- return 0;
-
- /* Read Device MaxPayload capability and setting */
- pci_read_config_word(dev, pos + PCI_EXP_DEVCTL, &dctl);
- pci_read_config_word(dev, pos + PCI_EXP_DEVCAP, &dcap);
- dsz = (dctl & PCI_EXP_DEVCTL_PAYLOAD) >> 5;
- dmax = (dcap & PCI_EXP_DEVCAP_PAYLOAD);
-
- /* Read Parent MaxPayload setting */
- ppos = pci_find_capability(parent, PCI_CAP_ID_EXP);
- if (!ppos)
- return 0;
- pci_read_config_word(parent, ppos + PCI_EXP_DEVCTL, &pctl);
- psz = (pctl & PCI_EXP_DEVCTL_PAYLOAD) >> 5;
-
- /* If parent payload > device max payload -> error
- * If parent payload > device payload -> set speed
- * If parent payload <= device payload -> do nothing
- */
- if (psz > dmax)
- return -1;
- else if (psz > dsz) {
- dev_info(&dev->dev, "Setting MaxPayload to %d\n", 128 << psz);
- pci_write_config_word(dev, pos + PCI_EXP_DEVCTL,
- (dctl & ~PCI_EXP_DEVCTL_PAYLOAD) +
- (psz << 5));
- }
- return 0;
-}
-
void pci_configure_slot(struct pci_dev *dev)
{
struct pci_dev *cdev;
@@ -210,9 +169,7 @@
(dev->class >> 8) == PCI_CLASS_BRIDGE_PCI)))
return;
- ret = pci_set_payload(dev);
- if (ret)
- dev_warn(&dev->dev, "could not set device max payload\n");
+ pcie_bus_configure_settings(dev->bus, dev->bus->self->pcie_mpss);
memset(&hpp, 0, sizeof(hpp));
ret = pci_get_hp_params(dev, &hpp);
diff --git a/drivers/pci/of.c b/drivers/pci/of.c
index c94d37e..f092993 100644
--- a/drivers/pci/of.c
+++ b/drivers/pci/of.c
@@ -55,7 +55,7 @@
*/
if (bus->bridge->of_node)
return of_node_get(bus->bridge->of_node);
- if (bus->bridge->parent->of_node)
+ if (bus->bridge->parent && bus->bridge->parent->of_node)
return of_node_get(bus->bridge->parent->of_node);
return NULL;
}
diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c
index 08a95b3..466fad6 100644
--- a/drivers/pci/pci.c
+++ b/drivers/pci/pci.c
@@ -77,6 +77,8 @@
unsigned long pci_hotplug_io_size = DEFAULT_HOTPLUG_IO_SIZE;
unsigned long pci_hotplug_mem_size = DEFAULT_HOTPLUG_MEM_SIZE;
+enum pcie_bus_config_types pcie_bus_config = PCIE_BUS_PERFORMANCE;
+
/*
* The default CLS is used if arch didn't set CLS explicitly and not
* all pci devices agree on the same value. Arch can override either
@@ -3223,6 +3225,67 @@
EXPORT_SYMBOL(pcie_set_readrq);
/**
+ * pcie_get_mps - get PCI Express maximum payload size
+ * @dev: PCI device to query
+ *
+ * Returns maximum payload size in bytes
+ * or appropriate error value.
+ */
+int pcie_get_mps(struct pci_dev *dev)
+{
+ int ret, cap;
+ u16 ctl;
+
+ cap = pci_pcie_cap(dev);
+ if (!cap)
+ return -EINVAL;
+
+ ret = pci_read_config_word(dev, cap + PCI_EXP_DEVCTL, &ctl);
+ if (!ret)
+ ret = 128 << ((ctl & PCI_EXP_DEVCTL_PAYLOAD) >> 5);
+
+ return ret;
+}
+
+/**
+ * pcie_set_mps - set PCI Express maximum payload size
+ * @dev: PCI device to query
+ * @rq: maximum payload size in bytes
+ * valid values are 128, 256, 512, 1024, 2048, 4096
+ *
+ * If possible sets maximum payload size
+ */
+int pcie_set_mps(struct pci_dev *dev, int mps)
+{
+ int cap, err = -EINVAL;
+ u16 ctl, v;
+
+ if (mps < 128 || mps > 4096 || !is_power_of_2(mps))
+ goto out;
+
+ v = ffs(mps) - 8;
+ if (v > dev->pcie_mpss)
+ goto out;
+ v <<= 5;
+
+ cap = pci_pcie_cap(dev);
+ if (!cap)
+ goto out;
+
+ err = pci_read_config_word(dev, cap + PCI_EXP_DEVCTL, &ctl);
+ if (err)
+ goto out;
+
+ if ((ctl & PCI_EXP_DEVCTL_PAYLOAD) != v) {
+ ctl &= ~PCI_EXP_DEVCTL_PAYLOAD;
+ ctl |= v;
+ err = pci_write_config_word(dev, cap + PCI_EXP_DEVCTL, ctl);
+ }
+out:
+ return err;
+}
+
+/**
* pci_select_bars - Make BAR mask from the type of resource
* @dev: the PCI device for which BAR mask is made
* @flags: resource type mask to be selected
@@ -3505,6 +3568,10 @@
pci_hotplug_io_size = memparse(str + 9, &str);
} else if (!strncmp(str, "hpmemsize=", 10)) {
pci_hotplug_mem_size = memparse(str + 10, &str);
+ } else if (!strncmp(str, "pcie_bus_safe", 13)) {
+ pcie_bus_config = PCIE_BUS_SAFE;
+ } else if (!strncmp(str, "pcie_bus_perf", 13)) {
+ pcie_bus_config = PCIE_BUS_PERFORMANCE;
} else {
printk(KERN_ERR "PCI: Unknown option `%s'\n",
str);
diff --git a/drivers/pci/pci.h b/drivers/pci/pci.h
index c8cee76..b74084e 100644
--- a/drivers/pci/pci.h
+++ b/drivers/pci/pci.h
@@ -283,6 +283,8 @@
#endif /* CONFIG_PCI_IOV */
+extern unsigned long pci_cardbus_resource_alignment(struct resource *);
+
static inline resource_size_t pci_resource_alignment(struct pci_dev *dev,
struct resource *res)
{
@@ -292,6 +294,8 @@
if (resno >= PCI_IOV_RESOURCES && resno <= PCI_IOV_RESOURCE_END)
return pci_sriov_resource_alignment(dev, resno);
#endif
+ if (dev->class >> 8 == PCI_CLASS_BRIDGE_CARDBUS)
+ return pci_cardbus_resource_alignment(res);
return resource_alignment(res);
}
diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c
index 795c902..8473727 100644
--- a/drivers/pci/probe.c
+++ b/drivers/pci/probe.c
@@ -856,6 +856,8 @@
pdev->pcie_cap = pos;
pci_read_config_word(pdev, pos + PCI_EXP_FLAGS, ®16);
pdev->pcie_type = (reg16 & PCI_EXP_FLAGS_TYPE) >> 4;
+ pci_read_config_word(pdev, pos + PCI_EXP_DEVCAP, ®16);
+ pdev->pcie_mpss = reg16 & PCI_EXP_DEVCAP_PAYLOAD;
}
void set_pcie_hotplug_bridge(struct pci_dev *pdev)
@@ -1326,6 +1328,150 @@
return nr;
}
+static int pcie_find_smpss(struct pci_dev *dev, void *data)
+{
+ u8 *smpss = data;
+
+ if (!pci_is_pcie(dev))
+ return 0;
+
+ /* For PCIE hotplug enabled slots not connected directly to a
+ * PCI-E root port, there can be problems when hotplugging
+ * devices. This is due to the possibility of hotplugging a
+ * device into the fabric with a smaller MPS that the devices
+ * currently running have configured. Modifying the MPS on the
+ * running devices could cause a fatal bus error due to an
+ * incoming frame being larger than the newly configured MPS.
+ * To work around this, the MPS for the entire fabric must be
+ * set to the minimum size. Any devices hotplugged into this
+ * fabric will have the minimum MPS set. If the PCI hotplug
+ * slot is directly connected to the root port and there are not
+ * other devices on the fabric (which seems to be the most
+ * common case), then this is not an issue and MPS discovery
+ * will occur as normal.
+ */
+ if (dev->is_hotplug_bridge && (!list_is_singular(&dev->bus->devices) ||
+ dev->bus->self->pcie_type != PCI_EXP_TYPE_ROOT_PORT))
+ *smpss = 0;
+
+ if (*smpss > dev->pcie_mpss)
+ *smpss = dev->pcie_mpss;
+
+ return 0;
+}
+
+static void pcie_write_mps(struct pci_dev *dev, int mps)
+{
+ int rc, dev_mpss;
+
+ dev_mpss = 128 << dev->pcie_mpss;
+
+ if (pcie_bus_config == PCIE_BUS_PERFORMANCE) {
+ if (dev->bus->self) {
+ dev_dbg(&dev->bus->dev, "Bus MPSS %d\n",
+ 128 << dev->bus->self->pcie_mpss);
+
+ /* For "MPS Force Max", the assumption is made that
+ * downstream communication will never be larger than
+ * the MRRS. So, the MPS only needs to be configured
+ * for the upstream communication. This being the case,
+ * walk from the top down and set the MPS of the child
+ * to that of the parent bus.
+ */
+ mps = 128 << dev->bus->self->pcie_mpss;
+ if (mps > dev_mpss)
+ dev_warn(&dev->dev, "MPS configured higher than"
+ " maximum supported by the device. If"
+ " a bus issue occurs, try running with"
+ " pci=pcie_bus_safe.\n");
+ }
+
+ dev->pcie_mpss = ffs(mps) - 8;
+ }
+
+ rc = pcie_set_mps(dev, mps);
+ if (rc)
+ dev_err(&dev->dev, "Failed attempting to set the MPS\n");
+}
+
+static void pcie_write_mrrs(struct pci_dev *dev, int mps)
+{
+ int rc, mrrs;
+
+ if (pcie_bus_config == PCIE_BUS_PERFORMANCE) {
+ int dev_mpss = 128 << dev->pcie_mpss;
+
+ /* For Max performance, the MRRS must be set to the largest
+ * supported value. However, it cannot be configured larger
+ * than the MPS the device or the bus can support. This assumes
+ * that the largest MRRS available on the device cannot be
+ * smaller than the device MPSS.
+ */
+ mrrs = mps < dev_mpss ? mps : dev_mpss;
+ } else
+ /* In the "safe" case, configure the MRRS for fairness on the
+ * bus by making all devices have the same size
+ */
+ mrrs = mps;
+
+
+ /* MRRS is a R/W register. Invalid values can be written, but a
+ * subsiquent read will verify if the value is acceptable or not.
+ * If the MRRS value provided is not acceptable (e.g., too large),
+ * shrink the value until it is acceptable to the HW.
+ */
+ while (mrrs != pcie_get_readrq(dev) && mrrs >= 128) {
+ rc = pcie_set_readrq(dev, mrrs);
+ if (rc)
+ dev_err(&dev->dev, "Failed attempting to set the MRRS\n");
+
+ mrrs /= 2;
+ }
+}
+
+static int pcie_bus_configure_set(struct pci_dev *dev, void *data)
+{
+ int mps = 128 << *(u8 *)data;
+
+ if (!pci_is_pcie(dev))
+ return 0;
+
+ dev_info(&dev->dev, "Dev MPS %d MPSS %d MRRS %d\n",
+ pcie_get_mps(dev), 128<<dev->pcie_mpss, pcie_get_readrq(dev));
+
+ pcie_write_mps(dev, mps);
+ pcie_write_mrrs(dev, mps);
+
+ dev_info(&dev->dev, "Dev MPS %d MPSS %d MRRS %d\n",
+ pcie_get_mps(dev), 128<<dev->pcie_mpss, pcie_get_readrq(dev));
+
+ return 0;
+}
+
+/* pcie_bus_configure_mps requires that pci_walk_bus work in a top-down,
+ * parents then children fashion. If this changes, then this code will not
+ * work as designed.
+ */
+void pcie_bus_configure_settings(struct pci_bus *bus, u8 mpss)
+{
+ u8 smpss = mpss;
+
+ if (!bus->self)
+ return;
+
+ if (!pci_is_pcie(bus->self))
+ return;
+
+ if (pcie_bus_config == PCIE_BUS_SAFE) {
+ pcie_find_smpss(bus->self, &smpss);
+ pci_walk_bus(bus, pcie_find_smpss, &smpss);
+ }
+
+ pcie_bus_configure_set(bus->self, &smpss);
+ pci_walk_bus(bus, pcie_bus_configure_set, &smpss);
+}
+EXPORT_SYMBOL_GPL(pcie_bus_configure_settings);
+
unsigned int __devinit pci_scan_child_bus(struct pci_bus *bus)
{
unsigned int devfn, pass, max = bus->secondary;
diff --git a/drivers/pci/setup-bus.c b/drivers/pci/setup-bus.c
index 8a1d3c7..784da9d 100644
--- a/drivers/pci/setup-bus.c
+++ b/drivers/pci/setup-bus.c
@@ -34,6 +34,7 @@
resource_size_t start;
resource_size_t end;
resource_size_t add_size;
+ resource_size_t min_align;
unsigned long flags;
};
@@ -65,7 +66,7 @@
*/
static void add_to_list(struct resource_list_x *head,
struct pci_dev *dev, struct resource *res,
- resource_size_t add_size)
+ resource_size_t add_size, resource_size_t min_align)
{
struct resource_list_x *list = head;
struct resource_list_x *ln = list->next;
@@ -84,13 +85,16 @@
tmp->end = res->end;
tmp->flags = res->flags;
tmp->add_size = add_size;
+ tmp->min_align = min_align;
list->next = tmp;
}
static void add_to_failed_list(struct resource_list_x *head,
struct pci_dev *dev, struct resource *res)
{
- add_to_list(head, dev, res, 0);
+ add_to_list(head, dev, res,
+ 0 /* dont care */,
+ 0 /* dont care */);
}
static void __dev_sort_resources(struct pci_dev *dev,
@@ -121,18 +125,18 @@
}
/**
- * adjust_resources_sorted() - satisfy any additional resource requests
+ * reassign_resources_sorted() - satisfy any additional resource requests
*
- * @add_head : head of the list tracking requests requiring additional
+ * @realloc_head : head of the list tracking requests requiring additional
* resources
* @head : head of the list tracking requests with allocated
* resources
*
- * Walk through each element of the add_head and try to procure
+ * Walk through each element of the realloc_head and try to procure
* additional resources for the element, provided the element
* is in the head list.
*/
-static void adjust_resources_sorted(struct resource_list_x *add_head,
+static void reassign_resources_sorted(struct resource_list_x *realloc_head,
struct resource_list *head)
{
struct resource *res;
@@ -141,8 +145,8 @@
resource_size_t add_size;
int idx;
- prev = add_head;
- for (list = add_head->next; list;) {
+ prev = realloc_head;
+ for (list = realloc_head->next; list;) {
res = list->res;
/* skip resource that has been reset */
if (!res->flags)
@@ -159,13 +163,17 @@
idx = res - &list->dev->resource[0];
add_size=list->add_size;
- if (!resource_size(res) && add_size) {
- res->end = res->start + add_size - 1;
- if(pci_assign_resource(list->dev, idx))
+ if (!resource_size(res)) {
+ res->start = list->start;
+ res->end = res->start + add_size - 1;
+ if(pci_assign_resource(list->dev, idx))
reset_resource(res);
- } else if (add_size) {
- adjust_resource(res, res->start,
- resource_size(res) + add_size);
+ } else {
+ resource_size_t align = list->min_align;
+ res->flags |= list->flags & (IORESOURCE_STARTALIGN|IORESOURCE_SIZEALIGN);
+ if (pci_reassign_resource(list->dev, idx, add_size, align))
+ dev_printk(KERN_DEBUG, &list->dev->dev, "failed to add optional resources res=%pR\n",
+ res);
}
out:
tmp = list;
@@ -210,16 +218,16 @@
}
static void __assign_resources_sorted(struct resource_list *head,
- struct resource_list_x *add_head,
+ struct resource_list_x *realloc_head,
struct resource_list_x *fail_head)
{
/* Satisfy the must-have resource requests */
assign_requested_resources_sorted(head, fail_head);
- /* Try to satisfy any additional nice-to-have resource
+ /* Try to satisfy any additional optional resource
requests */
- if (add_head)
- adjust_resources_sorted(add_head, head);
+ if (realloc_head)
+ reassign_resources_sorted(realloc_head, head);
free_list(resource_list, head);
}
@@ -235,7 +243,7 @@
}
static void pbus_assign_resources_sorted(const struct pci_bus *bus,
- struct resource_list_x *add_head,
+ struct resource_list_x *realloc_head,
struct resource_list_x *fail_head)
{
struct pci_dev *dev;
@@ -245,7 +253,7 @@
list_for_each_entry(dev, &bus->devices, bus_list)
__dev_sort_resources(dev, &head);
- __assign_resources_sorted(&head, add_head, fail_head);
+ __assign_resources_sorted(&head, realloc_head, fail_head);
}
void pci_setup_cardbus(struct pci_bus *bus)
@@ -540,13 +548,27 @@
return size;
}
+static resource_size_t get_res_add_size(struct resource_list_x *realloc_head,
+ struct resource *res)
+{
+ struct resource_list_x *list;
+
+ /* check if it is in realloc_head list */
+ for (list = realloc_head->next; list && list->res != res;
+ list = list->next);
+ if (list)
+ return list->add_size;
+
+ return 0;
+}
+
/**
* pbus_size_io() - size the io window of a given bus
*
* @bus : the bus
* @min_size : the minimum io window that must to be allocated
* @add_size : additional optional io window
- * @add_head : track the additional io window on this list
+ * @realloc_head : track the additional io window on this list
*
* Sizing the IO windows of the PCI-PCI bridge is trivial,
* since these windows have 4K granularity and the IO ranges
@@ -554,11 +576,12 @@
* We must be careful with the ISA aliasing though.
*/
static void pbus_size_io(struct pci_bus *bus, resource_size_t min_size,
- resource_size_t add_size, struct resource_list_x *add_head)
+ resource_size_t add_size, struct resource_list_x *realloc_head)
{
struct pci_dev *dev;
struct resource *b_res = find_free_bus_resource(bus, IORESOURCE_IO);
unsigned long size = 0, size0 = 0, size1 = 0;
+ resource_size_t children_add_size = 0;
if (!b_res)
return;
@@ -579,11 +602,16 @@
size += r_size;
else
size1 += r_size;
+
+ if (realloc_head)
+ children_add_size += get_res_add_size(realloc_head, r);
}
}
size0 = calculate_iosize(size, min_size, size1,
resource_size(b_res), 4096);
- size1 = (!add_head || (add_head && !add_size)) ? size0 :
+ if (children_add_size > add_size)
+ add_size = children_add_size;
+ size1 = (!realloc_head || (realloc_head && !add_size)) ? size0 :
calculate_iosize(size, min_size+add_size, size1,
resource_size(b_res), 4096);
if (!size0 && !size1) {
@@ -598,8 +626,8 @@
b_res->start = 4096;
b_res->end = b_res->start + size0 - 1;
b_res->flags |= IORESOURCE_STARTALIGN;
- if (size1 > size0 && add_head)
- add_to_list(add_head, bus->self, b_res, size1-size0);
+ if (size1 > size0 && realloc_head)
+ add_to_list(realloc_head, bus->self, b_res, size1-size0, 4096);
}
/**
@@ -608,7 +636,7 @@
* @bus : the bus
* @min_size : the minimum memory window that must to be allocated
* @add_size : additional optional memory window
- * @add_head : track the additional memory window on this list
+ * @realloc_head : track the additional memory window on this list
*
* Calculate the size of the bus and minimal alignment which
* guarantees that all child resources fit in this size.
@@ -616,7 +644,7 @@
static int pbus_size_mem(struct pci_bus *bus, unsigned long mask,
unsigned long type, resource_size_t min_size,
resource_size_t add_size,
- struct resource_list_x *add_head)
+ struct resource_list_x *realloc_head)
{
struct pci_dev *dev;
resource_size_t min_align, align, size, size0, size1;
@@ -624,6 +652,7 @@
int order, max_order;
struct resource *b_res = find_free_bus_resource(bus, type);
unsigned int mem64_mask = 0;
+ resource_size_t children_add_size = 0;
if (!b_res)
return 0;
@@ -645,6 +674,16 @@
if (r->parent || (r->flags & mask) != type)
continue;
r_size = resource_size(r);
+#ifdef CONFIG_PCI_IOV
+ /* put SRIOV requested res to the optional list */
+ if (realloc_head && i >= PCI_IOV_RESOURCES &&
+ i <= PCI_IOV_RESOURCE_END) {
+ r->end = r->start - 1;
+ add_to_list(realloc_head, dev, r, r_size, 0/* dont' care */);
+ children_add_size += r_size;
+ continue;
+ }
+#endif
/* For bridges size != alignment */
align = pci_resource_alignment(dev, r);
order = __ffs(align) - 20;
@@ -665,6 +704,9 @@
if (order > max_order)
max_order = order;
mem64_mask &= r->flags & IORESOURCE_MEM_64;
+
+ if (realloc_head)
+ children_add_size += get_res_add_size(realloc_head, r);
}
}
align = 0;
@@ -681,7 +723,9 @@
align += aligns[order];
}
size0 = calculate_memsize(size, min_size, 0, resource_size(b_res), min_align);
- size1 = (!add_head || (add_head && !add_size)) ? size0 :
+ if (children_add_size > add_size)
+ add_size = children_add_size;
+ size1 = (!realloc_head || (realloc_head && !add_size)) ? size0 :
calculate_memsize(size, min_size+add_size, 0,
resource_size(b_res), min_align);
if (!size0 && !size1) {
@@ -695,12 +739,22 @@
b_res->start = min_align;
b_res->end = size0 + min_align - 1;
b_res->flags |= IORESOURCE_STARTALIGN | mem64_mask;
- if (size1 > size0 && add_head)
- add_to_list(add_head, bus->self, b_res, size1-size0);
+ if (size1 > size0 && realloc_head)
+ add_to_list(realloc_head, bus->self, b_res, size1-size0, min_align);
return 1;
}
-static void pci_bus_size_cardbus(struct pci_bus *bus)
+unsigned long pci_cardbus_resource_alignment(struct resource *res)
+{
+ if (res->flags & IORESOURCE_IO)
+ return pci_cardbus_io_size;
+ if (res->flags & IORESOURCE_MEM)
+ return pci_cardbus_mem_size;
+ return 0;
+}
+
+static void pci_bus_size_cardbus(struct pci_bus *bus,
+ struct resource_list_x *realloc_head)
{
struct pci_dev *bridge = bus->self;
struct resource *b_res = &bridge->resource[PCI_BRIDGE_RESOURCES];
@@ -711,12 +765,14 @@
* a fixed amount of bus space for CardBus bridges.
*/
b_res[0].start = 0;
- b_res[0].end = pci_cardbus_io_size - 1;
b_res[0].flags |= IORESOURCE_IO | IORESOURCE_SIZEALIGN;
+ if (realloc_head)
+ add_to_list(realloc_head, bridge, b_res, pci_cardbus_io_size, 0 /* dont care */);
b_res[1].start = 0;
- b_res[1].end = pci_cardbus_io_size - 1;
b_res[1].flags |= IORESOURCE_IO | IORESOURCE_SIZEALIGN;
+ if (realloc_head)
+ add_to_list(realloc_head, bridge, b_res+1, pci_cardbus_io_size, 0 /* dont care */);
/*
* Check whether prefetchable memory is supported
@@ -736,21 +792,31 @@
*/
if (ctrl & PCI_CB_BRIDGE_CTL_PREFETCH_MEM0) {
b_res[2].start = 0;
- b_res[2].end = pci_cardbus_mem_size - 1;
b_res[2].flags |= IORESOURCE_MEM | IORESOURCE_PREFETCH | IORESOURCE_SIZEALIGN;
+ if (realloc_head)
+ add_to_list(realloc_head, bridge, b_res+2, pci_cardbus_mem_size, 0 /* dont care */);
b_res[3].start = 0;
- b_res[3].end = pci_cardbus_mem_size - 1;
b_res[3].flags |= IORESOURCE_MEM | IORESOURCE_SIZEALIGN;
+ if (realloc_head)
+ add_to_list(realloc_head, bridge, b_res+3, pci_cardbus_mem_size, 0 /* dont care */);
} else {
b_res[3].start = 0;
- b_res[3].end = pci_cardbus_mem_size * 2 - 1;
b_res[3].flags |= IORESOURCE_MEM | IORESOURCE_SIZEALIGN;
+ if (realloc_head)
+ add_to_list(realloc_head, bridge, b_res+3, pci_cardbus_mem_size * 2, 0 /* dont care */);
}
+
+ /* set the size of the resource to zero, so that the resource does not
+ * get assigned during required-resource allocation cycle but gets assigned
+ * during the optional-resource allocation cycle.
+ */
+ b_res[0].start = b_res[1].start = b_res[2].start = b_res[3].start = 1;
+ b_res[0].end = b_res[1].end = b_res[2].end = b_res[3].end = 0;
}
void __ref __pci_bus_size_bridges(struct pci_bus *bus,
- struct resource_list_x *add_head)
+ struct resource_list_x *realloc_head)
{
struct pci_dev *dev;
unsigned long mask, prefmask;
@@ -763,12 +829,12 @@
switch (dev->class >> 8) {
case PCI_CLASS_BRIDGE_CARDBUS:
- pci_bus_size_cardbus(b);
+ pci_bus_size_cardbus(b, realloc_head);
break;
case PCI_CLASS_BRIDGE_PCI:
default:
- __pci_bus_size_bridges(b, add_head);
+ __pci_bus_size_bridges(b, realloc_head);
break;
}
}
@@ -792,7 +858,7 @@
* Follow thru
*/
default:
- pbus_size_io(bus, 0, additional_io_size, add_head);
+ pbus_size_io(bus, 0, additional_io_size, realloc_head);
/* If the bridge supports prefetchable range, size it
separately. If it doesn't, or its prefetchable window
has already been allocated by arch code, try
@@ -800,11 +866,11 @@
resources. */
mask = IORESOURCE_MEM;
prefmask = IORESOURCE_MEM | IORESOURCE_PREFETCH;
- if (pbus_size_mem(bus, prefmask, prefmask, 0, additional_mem_size, add_head))
+ if (pbus_size_mem(bus, prefmask, prefmask, 0, additional_mem_size, realloc_head))
mask = prefmask; /* Success, size non-prefetch only. */
else
additional_mem_size += additional_mem_size;
- pbus_size_mem(bus, mask, IORESOURCE_MEM, 0, additional_mem_size, add_head);
+ pbus_size_mem(bus, mask, IORESOURCE_MEM, 0, additional_mem_size, realloc_head);
break;
}
}
@@ -816,20 +882,20 @@
EXPORT_SYMBOL(pci_bus_size_bridges);
static void __ref __pci_bus_assign_resources(const struct pci_bus *bus,
- struct resource_list_x *add_head,
+ struct resource_list_x *realloc_head,
struct resource_list_x *fail_head)
{
struct pci_bus *b;
struct pci_dev *dev;
- pbus_assign_resources_sorted(bus, add_head, fail_head);
+ pbus_assign_resources_sorted(bus, realloc_head, fail_head);
list_for_each_entry(dev, &bus->devices, bus_list) {
b = dev->subordinate;
if (!b)
continue;
- __pci_bus_assign_resources(b, add_head, fail_head);
+ __pci_bus_assign_resources(b, realloc_head, fail_head);
switch (dev->class >> 8) {
case PCI_CLASS_BRIDGE_PCI:
@@ -1039,7 +1105,7 @@
pci_assign_unassigned_resources(void)
{
struct pci_bus *bus;
- struct resource_list_x add_list; /* list of resources that
+ struct resource_list_x realloc_list; /* list of resources that
want additional resources */
int tried_times = 0;
enum release_type rel_type = leaf_only;
@@ -1052,7 +1118,7 @@
head.next = NULL;
- add_list.next = NULL;
+ realloc_list.next = NULL;
pci_try_num = max_depth + 1;
printk(KERN_DEBUG "PCI: max bus depth: %d pci_try_num: %d\n",
@@ -1062,12 +1128,12 @@
/* Depth first, calculate sizes and alignments of all
subordinate buses. */
list_for_each_entry(bus, &pci_root_buses, node)
- __pci_bus_size_bridges(bus, &add_list);
+ __pci_bus_size_bridges(bus, &realloc_list);
/* Depth last, allocate resources and update the hardware. */
list_for_each_entry(bus, &pci_root_buses, node)
- __pci_bus_assign_resources(bus, &add_list, &head);
- BUG_ON(add_list.next);
+ __pci_bus_assign_resources(bus, &realloc_list, &head);
+ BUG_ON(realloc_list.next);
tried_times++;
/* any device complain? */
diff --git a/drivers/pci/setup-res.c b/drivers/pci/setup-res.c
index 319f359..51a9095 100644
--- a/drivers/pci/setup-res.c
+++ b/drivers/pci/setup-res.c
@@ -128,16 +128,16 @@
}
#endif /* CONFIG_PCI_QUIRKS */
+
+
static int __pci_assign_resource(struct pci_bus *bus, struct pci_dev *dev,
- int resno)
+ int resno, resource_size_t size, resource_size_t align)
{
struct resource *res = dev->resource + resno;
- resource_size_t size, min, align;
+ resource_size_t min;
int ret;
- size = resource_size(res);
min = (res->flags & IORESOURCE_IO) ? PCIBIOS_MIN_IO : PCIBIOS_MIN_MEM;
- align = pci_resource_alignment(dev, res);
/* First, try exact prefetching match.. */
ret = pci_bus_alloc_resource(bus, res, size, align, min,
@@ -154,73 +154,51 @@
ret = pci_bus_alloc_resource(bus, res, size, align, min, 0,
pcibios_align_resource, dev);
}
-
- if (ret < 0 && dev->fw_addr[resno]) {
- struct resource *root, *conflict;
- resource_size_t start, end;
-
- /*
- * If we failed to assign anything, let's try the address
- * where firmware left it. That at least has a chance of
- * working, which is better than just leaving it disabled.
- */
-
- if (res->flags & IORESOURCE_IO)
- root = &ioport_resource;
- else
- root = &iomem_resource;
-
- start = res->start;
- end = res->end;
- res->start = dev->fw_addr[resno];
- res->end = res->start + size - 1;
- dev_info(&dev->dev, "BAR %d: trying firmware assignment %pR\n",
- resno, res);
- conflict = request_resource_conflict(root, res);
- if (conflict) {
- dev_info(&dev->dev,
- "BAR %d: %pR conflicts with %s %pR\n", resno,
- res, conflict->name, conflict);
- res->start = start;
- res->end = end;
- } else
- ret = 0;
- }
-
- if (!ret) {
- res->flags &= ~IORESOURCE_STARTALIGN;
- dev_info(&dev->dev, "BAR %d: assigned %pR\n", resno, res);
- if (resno < PCI_BRIDGE_RESOURCES)
- pci_update_resource(dev, resno);
- }
-
return ret;
}
-int pci_assign_resource(struct pci_dev *dev, int resno)
+static int pci_revert_fw_address(struct resource *res, struct pci_dev *dev,
+ int resno, resource_size_t size)
+{
+ struct resource *root, *conflict;
+ resource_size_t start, end;
+ int ret = 0;
+
+ if (res->flags & IORESOURCE_IO)
+ root = &ioport_resource;
+ else
+ root = &iomem_resource;
+
+ start = res->start;
+ end = res->end;
+ res->start = dev->fw_addr[resno];
+ res->end = res->start + size - 1;
+ dev_info(&dev->dev, "BAR %d: trying firmware assignment %pR\n",
+ resno, res);
+ conflict = request_resource_conflict(root, res);
+ if (conflict) {
+ dev_info(&dev->dev,
+ "BAR %d: %pR conflicts with %s %pR\n", resno,
+ res, conflict->name, conflict);
+ res->start = start;
+ res->end = end;
+ ret = 1;
+ }
+ return ret;
+}
+
+static int _pci_assign_resource(struct pci_dev *dev, int resno, int size, resource_size_t min_align)
{
struct resource *res = dev->resource + resno;
- resource_size_t align;
struct pci_bus *bus;
int ret;
char *type;
- align = pci_resource_alignment(dev, res);
- if (!align) {
- dev_info(&dev->dev, "BAR %d: can't assign %pR "
- "(bogus alignment)\n", resno, res);
- return -EINVAL;
- }
-
bus = dev->bus;
- while ((ret = __pci_assign_resource(bus, dev, resno))) {
- if (bus->parent && bus->self->transparent)
- bus = bus->parent;
- else
- bus = NULL;
- if (bus)
- continue;
- break;
+ while ((ret = __pci_assign_resource(bus, dev, resno, size, min_align))) {
+ if (!bus->parent || !bus->self->transparent)
+ break;
+ bus = bus->parent;
}
if (ret) {
@@ -241,6 +219,66 @@
return ret;
}
+int pci_reassign_resource(struct pci_dev *dev, int resno, resource_size_t addsize,
+ resource_size_t min_align)
+{
+ struct resource *res = dev->resource + resno;
+ resource_size_t new_size;
+ int ret;
+
+ if (!res->parent) {
+ dev_info(&dev->dev, "BAR %d: can't reassign an unassigned resouce %pR "
+ "\n", resno, res);
+ return -EINVAL;
+ }
+
+ new_size = resource_size(res) + addsize + min_align;
+ ret = _pci_assign_resource(dev, resno, new_size, min_align);
+ if (!ret) {
+ res->flags &= ~IORESOURCE_STARTALIGN;
+ dev_info(&dev->dev, "BAR %d: assigned %pR\n", resno, res);
+ if (resno < PCI_BRIDGE_RESOURCES)
+ pci_update_resource(dev, resno);
+ }
+ return ret;
+}
+
+int pci_assign_resource(struct pci_dev *dev, int resno)
+{
+ struct resource *res = dev->resource + resno;
+ resource_size_t align, size;
+ struct pci_bus *bus;
+ int ret;
+
+ align = pci_resource_alignment(dev, res);
+ if (!align) {
+ dev_info(&dev->dev, "BAR %d: can't assign %pR "
+ "(bogus alignment)\n", resno, res);
+ return -EINVAL;
+ }
+
+ bus = dev->bus;
+ size = resource_size(res);
+ ret = _pci_assign_resource(dev, resno, size, align);
+
+ /*
+ * If we failed to assign anything, let's try the address
+ * where firmware left it. That at least has a chance of
+ * working, which is better than just leaving it disabled.
+ */
+ if (ret < 0 && dev->fw_addr[resno])
+ ret = pci_revert_fw_address(res, dev, resno, size);
+
+ if (!ret) {
+ res->flags &= ~IORESOURCE_STARTALIGN;
+ dev_info(&dev->dev, "BAR %d: assigned %pR\n", resno, res);
+ if (resno < PCI_BRIDGE_RESOURCES)
+ pci_update_resource(dev, resno);
+ }
+ return ret;
+}
+
+
/* Sort resources by alignment */
void pdev_sort_resources(struct pci_dev *dev, struct resource_list *head)
{
diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig
index 45e0191..1e88d47 100644
--- a/drivers/platform/x86/Kconfig
+++ b/drivers/platform/x86/Kconfig
@@ -769,4 +769,12 @@
enable/disable the Camera, WiFi, BT etc. devices. If in doubt, say Y
here; it will only load on supported platforms.
+config SAMSUNG_Q10
+ tristate "Samsung Q10 Extras"
+ depends on SERIO_I8042
+ select BACKLIGHT_CLASS_DEVICE
+ ---help---
+ This driver provides support for backlight control on Samsung Q10
+ and related laptops, including Dell Latitude X200.
+
endif # X86_PLATFORM_DEVICES
diff --git a/drivers/platform/x86/Makefile b/drivers/platform/x86/Makefile
index afc1f83..293a320 100644
--- a/drivers/platform/x86/Makefile
+++ b/drivers/platform/x86/Makefile
@@ -44,3 +44,4 @@
obj-$(CONFIG_MXM_WMI) += mxm-wmi.o
obj-$(CONFIG_INTEL_MID_POWER_BUTTON) += intel_mid_powerbtn.o
obj-$(CONFIG_INTEL_OAKTRAIL) += intel_oaktrail.o
+obj-$(CONFIG_SAMSUNG_Q10) += samsung-q10.o
diff --git a/drivers/platform/x86/acer-wmi.c b/drivers/platform/x86/acer-wmi.c
index e1c4938..af2bb20 100644
--- a/drivers/platform/x86/acer-wmi.c
+++ b/drivers/platform/x86/acer-wmi.c
@@ -99,6 +99,7 @@
static const struct key_entry acer_wmi_keymap[] = {
{KE_KEY, 0x01, {KEY_WLAN} }, /* WiFi */
{KE_KEY, 0x03, {KEY_WLAN} }, /* WiFi */
+ {KE_KEY, 0x04, {KEY_WLAN} }, /* WiFi */
{KE_KEY, 0x12, {KEY_BLUETOOTH} }, /* BT */
{KE_KEY, 0x21, {KEY_PROG1} }, /* Backup */
{KE_KEY, 0x22, {KEY_PROG2} }, /* Arcade */
@@ -304,6 +305,10 @@
.wireless = 2,
};
+static struct quirk_entry quirk_lenovo_ideapad_s205 = {
+ .wireless = 3,
+};
+
/* The Aspire One has a dummy ACPI-WMI interface - disable it */
static struct dmi_system_id __devinitdata acer_blacklist[] = {
{
@@ -450,6 +455,15 @@
},
.driver_data = &quirk_medion_md_98300,
},
+ {
+ .callback = dmi_matched,
+ .ident = "Lenovo Ideapad S205",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "10382LG"),
+ },
+ .driver_data = &quirk_lenovo_ideapad_s205,
+ },
{}
};
@@ -542,6 +556,12 @@
return AE_ERROR;
*value = result & 0x1;
return AE_OK;
+ case 3:
+ err = ec_read(0x78, &result);
+ if (err)
+ return AE_ERROR;
+ *value = result & 0x1;
+ return AE_OK;
default:
err = ec_read(0xA, &result);
if (err)
@@ -1266,8 +1286,13 @@
acpi_status status;
status = get_u32(&state, ACER_CAP_WIRELESS);
- if (ACPI_SUCCESS(status))
- rfkill_set_sw_state(wireless_rfkill, !state);
+ if (ACPI_SUCCESS(status)) {
+ if (quirks->wireless == 3) {
+ rfkill_set_hw_state(wireless_rfkill, !state);
+ } else {
+ rfkill_set_sw_state(wireless_rfkill, !state);
+ }
+ }
if (has_cap(ACER_CAP_BLUETOOTH)) {
status = get_u32(&state, ACER_CAP_BLUETOOTH);
@@ -1400,6 +1425,9 @@
{
u32 result; \
acpi_status status;
+
+ pr_info("This threeg sysfs will be removed in 2012"
+ " - used by: %s\n", current->comm);
if (wmi_has_guid(WMID_GUID3))
status = wmid3_get_device_status(&result,
ACER_WMID3_GDS_THREEG);
@@ -1415,8 +1443,10 @@
{
u32 tmp = simple_strtoul(buf, NULL, 10);
acpi_status status = set_u32(tmp, ACER_CAP_THREEG);
- if (ACPI_FAILURE(status))
- return -EINVAL;
+ pr_info("This threeg sysfs will be removed in 2012"
+ " - used by: %s\n", current->comm);
+ if (ACPI_FAILURE(status))
+ return -EINVAL;
return count;
}
static DEVICE_ATTR(threeg, S_IRUGO | S_IWUSR, show_bool_threeg,
@@ -1425,6 +1455,8 @@
static ssize_t show_interface(struct device *dev, struct device_attribute *attr,
char *buf)
{
+ pr_info("This interface sysfs will be removed in 2012"
+ " - used by: %s\n", current->comm);
switch (interface->type) {
case ACER_AMW0:
return sprintf(buf, "AMW0\n");
diff --git a/drivers/platform/x86/acerhdf.c b/drivers/platform/x86/acerhdf.c
index fca3489..760c6d7 100644
--- a/drivers/platform/x86/acerhdf.c
+++ b/drivers/platform/x86/acerhdf.c
@@ -182,6 +182,7 @@
{"Acer", "Aspire 1810T", "v1.3308", 0x55, 0x58, {0x9e, 0x00} },
{"Acer", "Aspire 1810TZ", "v1.3310", 0x55, 0x58, {0x9e, 0x00} },
{"Acer", "Aspire 1810T", "v1.3310", 0x55, 0x58, {0x9e, 0x00} },
+ {"Acer", "Aspire 1810TZ", "v1.3314", 0x55, 0x58, {0x9e, 0x00} },
/* Acer 531 */
{"Acer", "AO531h", "v0.3201", 0x55, 0x58, {0x20, 0x00} },
/* Gateway */
@@ -703,15 +704,15 @@
MODULE_AUTHOR("Peter Feuerer");
MODULE_DESCRIPTION("Aspire One temperature and fan driver");
MODULE_ALIAS("dmi:*:*Acer*:pnAOA*:");
-MODULE_ALIAS("dmi:*:*Acer*:pnAspire 1410*:");
-MODULE_ALIAS("dmi:*:*Acer*:pnAspire 1810*:");
+MODULE_ALIAS("dmi:*:*Acer*:pnAspire*1410*:");
+MODULE_ALIAS("dmi:*:*Acer*:pnAspire*1810*:");
MODULE_ALIAS("dmi:*:*Acer*:pnAO531*:");
MODULE_ALIAS("dmi:*:*Gateway*:pnAOA*:");
MODULE_ALIAS("dmi:*:*Gateway*:pnLT31*:");
-MODULE_ALIAS("dmi:*:*Packard Bell*:pnAOA*:");
-MODULE_ALIAS("dmi:*:*Packard Bell*:pnDOA*:");
-MODULE_ALIAS("dmi:*:*Packard Bell*:pnDOTMU*:");
-MODULE_ALIAS("dmi:*:*Packard Bell*:pnDOTMA*:");
+MODULE_ALIAS("dmi:*:*Packard*Bell*:pnAOA*:");
+MODULE_ALIAS("dmi:*:*Packard*Bell*:pnDOA*:");
+MODULE_ALIAS("dmi:*:*Packard*Bell*:pnDOTMU*:");
+MODULE_ALIAS("dmi:*:*Packard*Bell*:pnDOTMA*:");
module_init(acerhdf_init);
module_exit(acerhdf_exit);
diff --git a/drivers/platform/x86/asus-laptop.c b/drivers/platform/x86/asus-laptop.c
index d65df92..fa6d7ec 100644
--- a/drivers/platform/x86/asus-laptop.c
+++ b/drivers/platform/x86/asus-laptop.c
@@ -70,11 +70,10 @@
* WAPF defines the behavior of the Fn+Fx wlan key
* The significance of values is yet to be found, but
* most of the time:
- * 0x0 will do nothing
- * 0x1 will allow to control the device with Fn+Fx key.
- * 0x4 will send an ACPI event (0x88) while pressing the Fn+Fx key
- * 0x5 like 0x1 or 0x4
- * So, if something doesn't work as you want, just try other values =)
+ * Bit | Bluetooth | WLAN
+ * 0 | Hardware | Hardware
+ * 1 | Hardware | Software
+ * 4 | Software | Software
*/
static uint wapf = 1;
module_param(wapf, uint, 0444);
diff --git a/drivers/platform/x86/asus-nb-wmi.c b/drivers/platform/x86/asus-nb-wmi.c
index 0580d99..b0859d4 100644
--- a/drivers/platform/x86/asus-nb-wmi.c
+++ b/drivers/platform/x86/asus-nb-wmi.c
@@ -38,6 +38,24 @@
MODULE_ALIAS("wmi:"ASUS_NB_WMI_EVENT_GUID);
+/*
+ * WAPF defines the behavior of the Fn+Fx wlan key
+ * The significance of values is yet to be found, but
+ * most of the time:
+ * Bit | Bluetooth | WLAN
+ * 0 | Hardware | Hardware
+ * 1 | Hardware | Software
+ * 4 | Software | Software
+ */
+static uint wapf;
+module_param(wapf, uint, 0444);
+MODULE_PARM_DESC(wapf, "WAPF value");
+
+static void asus_nb_wmi_quirks(struct asus_wmi_driver *driver)
+{
+ driver->wapf = wapf;
+}
+
static const struct key_entry asus_nb_wmi_keymap[] = {
{ KE_KEY, 0x30, { KEY_VOLUMEUP } },
{ KE_KEY, 0x31, { KEY_VOLUMEDOWN } },
@@ -53,16 +71,16 @@
{ KE_KEY, 0x51, { KEY_WWW } },
{ KE_KEY, 0x55, { KEY_CALC } },
{ KE_KEY, 0x5C, { KEY_F15 } }, /* Power Gear key */
- { KE_KEY, 0x5D, { KEY_WLAN } },
- { KE_KEY, 0x5E, { KEY_WLAN } },
- { KE_KEY, 0x5F, { KEY_WLAN } },
+ { KE_KEY, 0x5D, { KEY_WLAN } }, /* Wireless console Toggle */
+ { KE_KEY, 0x5E, { KEY_WLAN } }, /* Wireless console Enable */
+ { KE_KEY, 0x5F, { KEY_WLAN } }, /* Wireless console Disable */
{ KE_KEY, 0x60, { KEY_SWITCHVIDEOMODE } },
{ KE_KEY, 0x61, { KEY_SWITCHVIDEOMODE } },
{ KE_KEY, 0x62, { KEY_SWITCHVIDEOMODE } },
{ KE_KEY, 0x63, { KEY_SWITCHVIDEOMODE } },
{ KE_KEY, 0x6B, { KEY_TOUCHPAD_TOGGLE } },
- { KE_KEY, 0x7E, { KEY_BLUETOOTH } },
{ KE_KEY, 0x7D, { KEY_BLUETOOTH } },
+ { KE_KEY, 0x7E, { KEY_BLUETOOTH } },
{ KE_KEY, 0x82, { KEY_CAMERA } },
{ KE_KEY, 0x88, { KEY_RFKILL } },
{ KE_KEY, 0x8A, { KEY_PROG1 } },
@@ -81,6 +99,7 @@
.keymap = asus_nb_wmi_keymap,
.input_name = "Asus WMI hotkeys",
.input_phys = ASUS_NB_WMI_FILE "/input0",
+ .quirks = asus_nb_wmi_quirks,
};
diff --git a/drivers/platform/x86/asus-wmi.c b/drivers/platform/x86/asus-wmi.c
index 65b66aa..95cba9e 100644
--- a/drivers/platform/x86/asus-wmi.c
+++ b/drivers/platform/x86/asus-wmi.c
@@ -44,6 +44,7 @@
#include <linux/debugfs.h>
#include <linux/seq_file.h>
#include <linux/platform_device.h>
+#include <linux/thermal.h>
#include <acpi/acpi_bus.h>
#include <acpi/acpi_drivers.h>
@@ -66,6 +67,8 @@
#define NOTIFY_BRNUP_MAX 0x1f
#define NOTIFY_BRNDOWN_MIN 0x20
#define NOTIFY_BRNDOWN_MAX 0x2e
+#define NOTIFY_KBD_BRTUP 0xc4
+#define NOTIFY_KBD_BRTDWN 0xc5
/* WMI Methods */
#define ASUS_WMI_METHODID_SPEC 0x43455053 /* BIOS SPECification */
@@ -93,6 +96,7 @@
/* Wireless */
#define ASUS_WMI_DEVID_HW_SWITCH 0x00010001
#define ASUS_WMI_DEVID_WIRELESS_LED 0x00010002
+#define ASUS_WMI_DEVID_CWAP 0x00010003
#define ASUS_WMI_DEVID_WLAN 0x00010011
#define ASUS_WMI_DEVID_BLUETOOTH 0x00010013
#define ASUS_WMI_DEVID_GPS 0x00010015
@@ -102,6 +106,12 @@
/* Leds */
/* 0x000200XX and 0x000400XX */
+#define ASUS_WMI_DEVID_LED1 0x00020011
+#define ASUS_WMI_DEVID_LED2 0x00020012
+#define ASUS_WMI_DEVID_LED3 0x00020013
+#define ASUS_WMI_DEVID_LED4 0x00020014
+#define ASUS_WMI_DEVID_LED5 0x00020015
+#define ASUS_WMI_DEVID_LED6 0x00020016
/* Backlight and Brightness */
#define ASUS_WMI_DEVID_BACKLIGHT 0x00050011
@@ -174,13 +184,18 @@
struct led_classdev tpd_led;
int tpd_led_wk;
+ struct led_classdev kbd_led;
+ int kbd_led_wk;
struct workqueue_struct *led_workqueue;
struct work_struct tpd_led_work;
+ struct work_struct kbd_led_work;
struct asus_rfkill wlan;
struct asus_rfkill bluetooth;
struct asus_rfkill wimax;
struct asus_rfkill wwan3g;
+ struct asus_rfkill gps;
+ struct asus_rfkill uwb;
struct hotplug_slot *hotplug_slot;
struct mutex hotplug_lock;
@@ -205,6 +220,7 @@
asus->inputdev->phys = asus->driver->input_phys;
asus->inputdev->id.bustype = BUS_HOST;
asus->inputdev->dev.parent = &asus->platform_device->dev;
+ set_bit(EV_REP, asus->inputdev->evbit);
err = sparse_keymap_setup(asus->inputdev, asus->driver->keymap, NULL);
if (err)
@@ -359,30 +375,80 @@
return read_tpd_led_state(asus);
}
-static int asus_wmi_led_init(struct asus_wmi *asus)
+static void kbd_led_update(struct work_struct *work)
{
- int rv;
+ int ctrl_param = 0;
+ struct asus_wmi *asus;
- if (read_tpd_led_state(asus) < 0)
- return 0;
+ asus = container_of(work, struct asus_wmi, kbd_led_work);
- asus->led_workqueue = create_singlethread_workqueue("led_workqueue");
- if (!asus->led_workqueue)
- return -ENOMEM;
- INIT_WORK(&asus->tpd_led_work, tpd_led_update);
+ /*
+ * bits 0-2: level
+ * bit 7: light on/off
+ */
+ if (asus->kbd_led_wk > 0)
+ ctrl_param = 0x80 | (asus->kbd_led_wk & 0x7F);
- asus->tpd_led.name = "asus::touchpad";
- asus->tpd_led.brightness_set = tpd_led_set;
- asus->tpd_led.brightness_get = tpd_led_get;
- asus->tpd_led.max_brightness = 1;
+ asus_wmi_set_devstate(ASUS_WMI_DEVID_KBD_BACKLIGHT, ctrl_param, NULL);
+}
- rv = led_classdev_register(&asus->platform_device->dev, &asus->tpd_led);
- if (rv) {
- destroy_workqueue(asus->led_workqueue);
- return rv;
+static int kbd_led_read(struct asus_wmi *asus, int *level, int *env)
+{
+ int retval;
+
+ /*
+ * bits 0-2: level
+ * bit 7: light on/off
+ * bit 8-10: environment (0: dark, 1: normal, 2: light)
+ * bit 17: status unknown
+ */
+ retval = asus_wmi_get_devstate_bits(asus, ASUS_WMI_DEVID_KBD_BACKLIGHT,
+ 0xFFFF);
+
+ /* Unknown status is considered as off */
+ if (retval == 0x8000)
+ retval = 0;
+
+ if (retval >= 0) {
+ if (level)
+ *level = retval & 0x80 ? retval & 0x7F : 0;
+ if (env)
+ *env = (retval >> 8) & 0x7F;
+ retval = 0;
}
- return 0;
+ return retval;
+}
+
+static void kbd_led_set(struct led_classdev *led_cdev,
+ enum led_brightness value)
+{
+ struct asus_wmi *asus;
+
+ asus = container_of(led_cdev, struct asus_wmi, kbd_led);
+
+ if (value > asus->kbd_led.max_brightness)
+ value = asus->kbd_led.max_brightness;
+ else if (value < 0)
+ value = 0;
+
+ asus->kbd_led_wk = value;
+ queue_work(asus->led_workqueue, &asus->kbd_led_work);
+}
+
+static enum led_brightness kbd_led_get(struct led_classdev *led_cdev)
+{
+ struct asus_wmi *asus;
+ int retval, value;
+
+ asus = container_of(led_cdev, struct asus_wmi, kbd_led);
+
+ retval = kbd_led_read(asus, &value, NULL);
+
+ if (retval < 0)
+ return retval;
+
+ return value;
}
static void asus_wmi_led_exit(struct asus_wmi *asus)
@@ -393,6 +459,48 @@
destroy_workqueue(asus->led_workqueue);
}
+static int asus_wmi_led_init(struct asus_wmi *asus)
+{
+ int rv = 0;
+
+ asus->led_workqueue = create_singlethread_workqueue("led_workqueue");
+ if (!asus->led_workqueue)
+ return -ENOMEM;
+
+ if (read_tpd_led_state(asus) >= 0) {
+ INIT_WORK(&asus->tpd_led_work, tpd_led_update);
+
+ asus->tpd_led.name = "asus::touchpad";
+ asus->tpd_led.brightness_set = tpd_led_set;
+ asus->tpd_led.brightness_get = tpd_led_get;
+ asus->tpd_led.max_brightness = 1;
+
+ rv = led_classdev_register(&asus->platform_device->dev,
+ &asus->tpd_led);
+ if (rv)
+ goto error;
+ }
+
+ if (kbd_led_read(asus, NULL, NULL) >= 0) {
+ INIT_WORK(&asus->kbd_led_work, kbd_led_update);
+
+ asus->kbd_led.name = "asus::kbd_backlight";
+ asus->kbd_led.brightness_set = kbd_led_set;
+ asus->kbd_led.brightness_get = kbd_led_get;
+ asus->kbd_led.max_brightness = 3;
+
+ rv = led_classdev_register(&asus->platform_device->dev,
+ &asus->kbd_led);
+ }
+
+error:
+ if (rv)
+ asus_wmi_led_exit(asus);
+
+ return rv;
+}
+
+
/*
* PCI hotplug (for wlan rfkill)
*/
@@ -729,6 +837,16 @@
rfkill_destroy(asus->wwan3g.rfkill);
asus->wwan3g.rfkill = NULL;
}
+ if (asus->gps.rfkill) {
+ rfkill_unregister(asus->gps.rfkill);
+ rfkill_destroy(asus->gps.rfkill);
+ asus->gps.rfkill = NULL;
+ }
+ if (asus->uwb.rfkill) {
+ rfkill_unregister(asus->uwb.rfkill);
+ rfkill_destroy(asus->uwb.rfkill);
+ asus->uwb.rfkill = NULL;
+ }
}
static int asus_wmi_rfkill_init(struct asus_wmi *asus)
@@ -763,6 +881,18 @@
if (result && result != -ENODEV)
goto exit;
+ result = asus_new_rfkill(asus, &asus->gps, "asus-gps",
+ RFKILL_TYPE_GPS, ASUS_WMI_DEVID_GPS);
+
+ if (result && result != -ENODEV)
+ goto exit;
+
+ result = asus_new_rfkill(asus, &asus->uwb, "asus-uwb",
+ RFKILL_TYPE_UWB, ASUS_WMI_DEVID_UWB);
+
+ if (result && result != -ENODEV)
+ goto exit;
+
if (!asus->driver->hotplug_wireless)
goto exit;
@@ -797,8 +927,8 @@
* Hwmon device
*/
static ssize_t asus_hwmon_pwm1(struct device *dev,
- struct device_attribute *attr,
- char *buf)
+ struct device_attribute *attr,
+ char *buf)
{
struct asus_wmi *asus = dev_get_drvdata(dev);
u32 value;
@@ -809,7 +939,7 @@
if (err < 0)
return err;
- value |= 0xFF;
+ value &= 0xFF;
if (value == 1) /* Low Speed */
value = 85;
@@ -825,7 +955,26 @@
return sprintf(buf, "%d\n", value);
}
+static ssize_t asus_hwmon_temp1(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct asus_wmi *asus = dev_get_drvdata(dev);
+ u32 value;
+ int err;
+
+ err = asus_wmi_get_devstate(asus, ASUS_WMI_DEVID_THERMAL_CTRL, &value);
+
+ if (err < 0)
+ return err;
+
+ value = KELVIN_TO_CELSIUS((value & 0xFFFF)) * 1000;
+
+ return sprintf(buf, "%d\n", value);
+}
+
static SENSOR_DEVICE_ATTR(pwm1, S_IRUGO, asus_hwmon_pwm1, NULL, 0);
+static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, asus_hwmon_temp1, NULL, 0);
static ssize_t
show_name(struct device *dev, struct device_attribute *attr, char *buf)
@@ -836,12 +985,13 @@
static struct attribute *hwmon_attributes[] = {
&sensor_dev_attr_pwm1.dev_attr.attr,
+ &sensor_dev_attr_temp1_input.dev_attr.attr,
&sensor_dev_attr_name.dev_attr.attr,
NULL
};
static mode_t asus_hwmon_sysfs_is_visible(struct kobject *kobj,
- struct attribute *attr, int idx)
+ struct attribute *attr, int idx)
{
struct device *dev = container_of(kobj, struct device, kobj);
struct platform_device *pdev = to_platform_device(dev->parent);
@@ -852,6 +1002,8 @@
if (attr == &sensor_dev_attr_pwm1.dev_attr.attr)
dev_id = ASUS_WMI_DEVID_FAN_CTRL;
+ else if (attr == &sensor_dev_attr_temp1_input.dev_attr.attr)
+ dev_id = ASUS_WMI_DEVID_THERMAL_CTRL;
if (dev_id != -1) {
int err = asus_wmi_get_devstate(asus, dev_id, &value);
@@ -869,9 +1021,13 @@
* - reverved bits are non-zero
* - sfun and presence bit are not set
*/
- if (value != ASUS_WMI_UNSUPPORTED_METHOD || value & 0xFFF80000
+ if (value == ASUS_WMI_UNSUPPORTED_METHOD || value & 0xFFF80000
|| (!asus->sfun && !(value & ASUS_WMI_DSTS_PRESENCE_BIT)))
ok = false;
+ } else if (dev_id == ASUS_WMI_DEVID_THERMAL_CTRL) {
+ /* If value is zero, something is clearly wrong */
+ if (value == 0)
+ ok = false;
}
return ok ? attr->mode : 0;
@@ -904,6 +1060,7 @@
pr_err("Could not register asus hwmon device\n");
return PTR_ERR(hwmon);
}
+ dev_set_drvdata(hwmon, asus);
asus->hwmon_device = hwmon;
result = sysfs_create_group(&hwmon->kobj, &hwmon_attribute_group);
if (result)
@@ -1060,6 +1217,8 @@
acpi_status status;
int code;
int orig_code;
+ unsigned int key_value = 1;
+ bool autorelease = 1;
status = wmi_get_event_data(value, &response);
if (status != AE_OK) {
@@ -1075,6 +1234,13 @@
code = obj->integer.value;
orig_code = code;
+ if (asus->driver->key_filter) {
+ asus->driver->key_filter(asus->driver, &code, &key_value,
+ &autorelease);
+ if (code == ASUS_WMI_KEY_IGNORE)
+ goto exit;
+ }
+
if (code >= NOTIFY_BRNUP_MIN && code <= NOTIFY_BRNUP_MAX)
code = NOTIFY_BRNUP_MIN;
else if (code >= NOTIFY_BRNDOWN_MIN &&
@@ -1084,7 +1250,8 @@
if (code == NOTIFY_BRNUP_MIN || code == NOTIFY_BRNDOWN_MIN) {
if (!acpi_video_backlight_support())
asus_wmi_backlight_notify(asus, orig_code);
- } else if (!sparse_keymap_report_event(asus->inputdev, code, 1, true))
+ } else if (!sparse_keymap_report_event(asus->inputdev, code,
+ key_value, autorelease))
pr_info("Unknown key %x pressed\n", code);
exit:
@@ -1164,14 +1331,18 @@
static ssize_t store_cpufv(struct device *dev, struct device_attribute *attr,
const char *buf, size_t count)
{
- int value;
+ int value, rv;
if (!count || sscanf(buf, "%i", &value) != 1)
return -EINVAL;
if (value < 0 || value > 2)
return -EINVAL;
- return asus_wmi_evaluate_method(ASUS_WMI_METHODID_CFVS, value, 0, NULL);
+ rv = asus_wmi_evaluate_method(ASUS_WMI_METHODID_CFVS, value, 0, NULL);
+ if (rv < 0)
+ return rv;
+
+ return count;
}
static DEVICE_ATTR(cpufv, S_IRUGO | S_IWUSR, NULL, store_cpufv);
@@ -1234,7 +1405,7 @@
/* We don't know yet what to do with this version... */
if (!asus_wmi_evaluate_method(ASUS_WMI_METHODID_SPEC, 0, 0x9, &rv)) {
- pr_info("BIOS WMI version: %d.%d", rv >> 8, rv & 0xFF);
+ pr_info("BIOS WMI version: %d.%d", rv >> 16, rv & 0xFF);
asus->spec = rv;
}
@@ -1266,6 +1437,12 @@
return -ENODEV;
}
+ /* CWAP allow to define the behavior of the Fn+F2 key,
+ * this method doesn't seems to be present on Eee PCs */
+ if (asus->driver->wapf >= 0)
+ asus_wmi_set_devstate(ASUS_WMI_DEVID_CWAP,
+ asus->driver->wapf, NULL);
+
return asus_wmi_sysfs_init(asus->platform_device);
}
@@ -1568,6 +1745,14 @@
bl = !asus_wmi_get_devstate_simple(asus, ASUS_WMI_DEVID_WWAN3G);
rfkill_set_sw_state(asus->wwan3g.rfkill, bl);
}
+ if (asus->gps.rfkill) {
+ bl = !asus_wmi_get_devstate_simple(asus, ASUS_WMI_DEVID_GPS);
+ rfkill_set_sw_state(asus->gps.rfkill, bl);
+ }
+ if (asus->uwb.rfkill) {
+ bl = !asus_wmi_get_devstate_simple(asus, ASUS_WMI_DEVID_UWB);
+ rfkill_set_sw_state(asus->uwb.rfkill, bl);
+ }
return 0;
}
@@ -1604,7 +1789,7 @@
static bool used;
-int asus_wmi_register_driver(struct asus_wmi_driver *driver)
+int __init_or_module asus_wmi_register_driver(struct asus_wmi_driver *driver)
{
struct platform_driver *platform_driver;
struct platform_device *platform_device;
diff --git a/drivers/platform/x86/asus-wmi.h b/drivers/platform/x86/asus-wmi.h
index c044522..8147c10 100644
--- a/drivers/platform/x86/asus-wmi.h
+++ b/drivers/platform/x86/asus-wmi.h
@@ -29,12 +29,15 @@
#include <linux/platform_device.h>
+#define ASUS_WMI_KEY_IGNORE (-1)
+
struct module;
struct key_entry;
struct asus_wmi;
struct asus_wmi_driver {
bool hotplug_wireless;
+ int wapf;
const char *name;
struct module *owner;
@@ -44,6 +47,10 @@
const struct key_entry *keymap;
const char *input_name;
const char *input_phys;
+ /* Returns new code, value, and autorelease values in arguments.
+ * Return ASUS_WMI_KEY_IGNORE in code if event should be ignored. */
+ void (*key_filter) (struct asus_wmi_driver *driver, int *code,
+ unsigned int *value, bool *autorelease);
int (*probe) (struct platform_device *device);
void (*quirks) (struct asus_wmi_driver *driver);
diff --git a/drivers/platform/x86/dell-laptop.c b/drivers/platform/x86/dell-laptop.c
index e39ab1d..f31fa4e 100644
--- a/drivers/platform/x86/dell-laptop.c
+++ b/drivers/platform/x86/dell-laptop.c
@@ -612,7 +612,6 @@
if (!bufferpage)
goto fail_buffer;
buffer = page_address(bufferpage);
- mutex_init(&buffer_mutex);
ret = dell_setup_rfkill();
diff --git a/drivers/platform/x86/dell-wmi.c b/drivers/platform/x86/dell-wmi.c
index ce79082..fa9a217 100644
--- a/drivers/platform/x86/dell-wmi.c
+++ b/drivers/platform/x86/dell-wmi.c
@@ -54,6 +54,8 @@
*/
static const struct key_entry dell_wmi_legacy_keymap[] __initconst = {
+ { KE_IGNORE, 0x003a, { KEY_CAPSLOCK } },
+
{ KE_KEY, 0xe045, { KEY_PROG1 } },
{ KE_KEY, 0xe009, { KEY_EJECTCD } },
@@ -85,6 +87,11 @@
{ KE_IGNORE, 0xe013, { KEY_RESERVED } },
{ KE_IGNORE, 0xe020, { KEY_MUTE } },
+
+ /* Shortcut and audio panel keys */
+ { KE_IGNORE, 0xe025, { KEY_RESERVED } },
+ { KE_IGNORE, 0xe026, { KEY_RESERVED } },
+
{ KE_IGNORE, 0xe02e, { KEY_VOLUMEDOWN } },
{ KE_IGNORE, 0xe030, { KEY_VOLUMEUP } },
{ KE_IGNORE, 0xe033, { KEY_KBDILLUMUP } },
@@ -92,6 +99,9 @@
{ KE_IGNORE, 0xe03a, { KEY_CAPSLOCK } },
{ KE_IGNORE, 0xe045, { KEY_NUMLOCK } },
{ KE_IGNORE, 0xe046, { KEY_SCROLLLOCK } },
+ { KE_IGNORE, 0xe0f7, { KEY_MUTE } },
+ { KE_IGNORE, 0xe0f8, { KEY_VOLUMEDOWN } },
+ { KE_IGNORE, 0xe0f9, { KEY_VOLUMEUP } },
{ KE_END, 0 }
};
diff --git a/drivers/platform/x86/eeepc-wmi.c b/drivers/platform/x86/eeepc-wmi.c
index 4aa867a..9f6e643 100644
--- a/drivers/platform/x86/eeepc-wmi.c
+++ b/drivers/platform/x86/eeepc-wmi.c
@@ -56,6 +56,11 @@
"If your laptop needs that, please report to "
"acpi4asus-user@lists.sourceforge.net.");
+/* Values for T101MT "Home" key */
+#define HOME_PRESS 0xe4
+#define HOME_HOLD 0xea
+#define HOME_RELEASE 0xe5
+
static const struct key_entry eeepc_wmi_keymap[] = {
/* Sleep already handled via generic ACPI code */
{ KE_KEY, 0x30, { KEY_VOLUMEUP } },
@@ -71,6 +76,7 @@
{ KE_KEY, 0xcc, { KEY_SWITCHVIDEOMODE } },
{ KE_KEY, 0xe0, { KEY_PROG1 } }, /* Task Manager */
{ KE_KEY, 0xe1, { KEY_F14 } }, /* Change Resolution */
+ { KE_KEY, HOME_PRESS, { KEY_CONFIG } }, /* Home/Express gate key */
{ KE_KEY, 0xe8, { KEY_SCREENLOCK } },
{ KE_KEY, 0xe9, { KEY_BRIGHTNESS_ZERO } },
{ KE_KEY, 0xeb, { KEY_CAMERA_ZOOMOUT } },
@@ -81,6 +87,25 @@
{ KE_END, 0},
};
+static void eeepc_wmi_key_filter(struct asus_wmi_driver *asus_wmi, int *code,
+ unsigned int *value, bool *autorelease)
+{
+ switch (*code) {
+ case HOME_PRESS:
+ *value = 1;
+ *autorelease = 0;
+ break;
+ case HOME_HOLD:
+ *code = ASUS_WMI_KEY_IGNORE;
+ break;
+ case HOME_RELEASE:
+ *code = HOME_PRESS;
+ *value = 0;
+ *autorelease = 0;
+ break;
+ }
+}
+
static acpi_status eeepc_wmi_parse_device(acpi_handle handle, u32 level,
void *context, void **retval)
{
@@ -141,6 +166,7 @@
static void eeepc_wmi_quirks(struct asus_wmi_driver *driver)
{
driver->hotplug_wireless = hotplug_wireless;
+ driver->wapf = -1;
eeepc_dmi_check(driver);
}
@@ -151,6 +177,7 @@
.keymap = eeepc_wmi_keymap,
.input_name = "Eee PC WMI hotkeys",
.input_phys = EEEPC_WMI_FILE "/input0",
+ .key_filter = eeepc_wmi_key_filter,
.probe = eeepc_wmi_probe,
.quirks = eeepc_wmi_quirks,
};
diff --git a/drivers/platform/x86/ideapad-laptop.c b/drivers/platform/x86/ideapad-laptop.c
index bfdda33..0c59541 100644
--- a/drivers/platform/x86/ideapad-laptop.c
+++ b/drivers/platform/x86/ideapad-laptop.c
@@ -32,13 +32,22 @@
#include <linux/platform_device.h>
#include <linux/input.h>
#include <linux/input/sparse-keymap.h>
+#include <linux/backlight.h>
+#include <linux/fb.h>
#define IDEAPAD_RFKILL_DEV_NUM (3)
+#define CFG_BT_BIT (16)
+#define CFG_3G_BIT (17)
+#define CFG_WIFI_BIT (18)
+#define CFG_CAMERA_BIT (19)
+
struct ideapad_private {
struct rfkill *rfk[IDEAPAD_RFKILL_DEV_NUM];
struct platform_device *platform_device;
struct input_dev *inputdev;
+ struct backlight_device *blightdev;
+ unsigned long cfg;
};
static acpi_handle ideapad_handle;
@@ -155,7 +164,7 @@
}
/*
- * camera power
+ * sysfs
*/
static ssize_t show_ideapad_cam(struct device *dev,
struct device_attribute *attr,
@@ -186,6 +195,44 @@
static DEVICE_ATTR(camera_power, 0644, show_ideapad_cam, store_ideapad_cam);
+static ssize_t show_ideapad_cfg(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct ideapad_private *priv = dev_get_drvdata(dev);
+
+ return sprintf(buf, "0x%.8lX\n", priv->cfg);
+}
+
+static DEVICE_ATTR(cfg, 0444, show_ideapad_cfg, NULL);
+
+static struct attribute *ideapad_attributes[] = {
+ &dev_attr_camera_power.attr,
+ &dev_attr_cfg.attr,
+ NULL
+};
+
+static mode_t ideapad_is_visible(struct kobject *kobj,
+ struct attribute *attr,
+ int idx)
+{
+ struct device *dev = container_of(kobj, struct device, kobj);
+ struct ideapad_private *priv = dev_get_drvdata(dev);
+ bool supported;
+
+ if (attr == &dev_attr_camera_power.attr)
+ supported = test_bit(CFG_CAMERA_BIT, &(priv->cfg));
+ else
+ supported = true;
+
+ return supported ? attr->mode : 0;
+}
+
+static struct attribute_group ideapad_attribute_group = {
+ .is_visible = ideapad_is_visible,
+ .attrs = ideapad_attributes
+};
+
/*
* Rfkill
*/
@@ -197,9 +244,9 @@
};
const struct ideapad_rfk_data ideapad_rfk_data[] = {
- { "ideapad_wlan", 18, 0x15, RFKILL_TYPE_WLAN },
- { "ideapad_bluetooth", 16, 0x17, RFKILL_TYPE_BLUETOOTH },
- { "ideapad_3g", 17, 0x20, RFKILL_TYPE_WWAN },
+ { "ideapad_wlan", CFG_WIFI_BIT, 0x15, RFKILL_TYPE_WLAN },
+ { "ideapad_bluetooth", CFG_BT_BIT, 0x17, RFKILL_TYPE_BLUETOOTH },
+ { "ideapad_3g", CFG_3G_BIT, 0x20, RFKILL_TYPE_WWAN },
};
static int ideapad_rfk_set(void *data, bool blocked)
@@ -265,8 +312,7 @@
return 0;
}
-static void __devexit ideapad_unregister_rfkill(struct acpi_device *adevice,
- int dev)
+static void ideapad_unregister_rfkill(struct acpi_device *adevice, int dev)
{
struct ideapad_private *priv = dev_get_drvdata(&adevice->dev);
@@ -280,15 +326,6 @@
/*
* Platform device
*/
-static struct attribute *ideapad_attributes[] = {
- &dev_attr_camera_power.attr,
- NULL
-};
-
-static struct attribute_group ideapad_attribute_group = {
- .attrs = ideapad_attributes
-};
-
static int __devinit ideapad_platform_init(struct ideapad_private *priv)
{
int result;
@@ -369,7 +406,7 @@
return error;
}
-static void __devexit ideapad_input_exit(struct ideapad_private *priv)
+static void ideapad_input_exit(struct ideapad_private *priv)
{
sparse_keymap_free(priv->inputdev);
input_unregister_device(priv->inputdev);
@@ -383,6 +420,98 @@
}
/*
+ * backlight
+ */
+static int ideapad_backlight_get_brightness(struct backlight_device *blightdev)
+{
+ unsigned long now;
+
+ if (read_ec_data(ideapad_handle, 0x12, &now))
+ return -EIO;
+ return now;
+}
+
+static int ideapad_backlight_update_status(struct backlight_device *blightdev)
+{
+ if (write_ec_cmd(ideapad_handle, 0x13, blightdev->props.brightness))
+ return -EIO;
+ if (write_ec_cmd(ideapad_handle, 0x33,
+ blightdev->props.power == FB_BLANK_POWERDOWN ? 0 : 1))
+ return -EIO;
+
+ return 0;
+}
+
+static const struct backlight_ops ideapad_backlight_ops = {
+ .get_brightness = ideapad_backlight_get_brightness,
+ .update_status = ideapad_backlight_update_status,
+};
+
+static int ideapad_backlight_init(struct ideapad_private *priv)
+{
+ struct backlight_device *blightdev;
+ struct backlight_properties props;
+ unsigned long max, now, power;
+
+ if (read_ec_data(ideapad_handle, 0x11, &max))
+ return -EIO;
+ if (read_ec_data(ideapad_handle, 0x12, &now))
+ return -EIO;
+ if (read_ec_data(ideapad_handle, 0x18, &power))
+ return -EIO;
+
+ memset(&props, 0, sizeof(struct backlight_properties));
+ props.max_brightness = max;
+ props.type = BACKLIGHT_PLATFORM;
+ blightdev = backlight_device_register("ideapad",
+ &priv->platform_device->dev,
+ priv,
+ &ideapad_backlight_ops,
+ &props);
+ if (IS_ERR(blightdev)) {
+ pr_err("Could not register backlight device\n");
+ return PTR_ERR(blightdev);
+ }
+
+ priv->blightdev = blightdev;
+ blightdev->props.brightness = now;
+ blightdev->props.power = power ? FB_BLANK_UNBLANK : FB_BLANK_POWERDOWN;
+ backlight_update_status(blightdev);
+
+ return 0;
+}
+
+static void ideapad_backlight_exit(struct ideapad_private *priv)
+{
+ if (priv->blightdev)
+ backlight_device_unregister(priv->blightdev);
+ priv->blightdev = NULL;
+}
+
+static void ideapad_backlight_notify_power(struct ideapad_private *priv)
+{
+ unsigned long power;
+ struct backlight_device *blightdev = priv->blightdev;
+
+ if (read_ec_data(ideapad_handle, 0x18, &power))
+ return;
+ blightdev->props.power = power ? FB_BLANK_UNBLANK : FB_BLANK_POWERDOWN;
+}
+
+static void ideapad_backlight_notify_brightness(struct ideapad_private *priv)
+{
+ unsigned long now;
+
+ /* if we control brightness via acpi video driver */
+ if (priv->blightdev == NULL) {
+ read_ec_data(ideapad_handle, 0x12, &now);
+ return;
+ }
+
+ backlight_force_update(priv->blightdev, BACKLIGHT_UPDATE_HOTKEY);
+}
+
+/*
* module init/exit
*/
static const struct acpi_device_id ideapad_device_ids[] = {
@@ -393,10 +522,11 @@
static int __devinit ideapad_acpi_add(struct acpi_device *adevice)
{
- int ret, i, cfg;
+ int ret, i;
+ unsigned long cfg;
struct ideapad_private *priv;
- if (read_method_int(adevice->handle, "_CFG", &cfg))
+ if (read_method_int(adevice->handle, "_CFG", (int *)&cfg))
return -ENODEV;
priv = kzalloc(sizeof(*priv), GFP_KERNEL);
@@ -404,6 +534,7 @@
return -ENOMEM;
dev_set_drvdata(&adevice->dev, priv);
ideapad_handle = adevice->handle;
+ priv->cfg = cfg;
ret = ideapad_platform_init(priv);
if (ret)
@@ -414,15 +545,25 @@
goto input_failed;
for (i = 0; i < IDEAPAD_RFKILL_DEV_NUM; i++) {
- if (test_bit(ideapad_rfk_data[i].cfgbit, (unsigned long *)&cfg))
+ if (test_bit(ideapad_rfk_data[i].cfgbit, &cfg))
ideapad_register_rfkill(adevice, i);
else
priv->rfk[i] = NULL;
}
ideapad_sync_rfk_state(adevice);
+ if (!acpi_video_backlight_support()) {
+ ret = ideapad_backlight_init(priv);
+ if (ret && ret != -ENODEV)
+ goto backlight_failed;
+ }
+
return 0;
+backlight_failed:
+ for (i = 0; i < IDEAPAD_RFKILL_DEV_NUM; i++)
+ ideapad_unregister_rfkill(adevice, i);
+ ideapad_input_exit(priv);
input_failed:
ideapad_platform_exit(priv);
platform_failed:
@@ -435,6 +576,7 @@
struct ideapad_private *priv = dev_get_drvdata(&adevice->dev);
int i;
+ ideapad_backlight_exit(priv);
for (i = 0; i < IDEAPAD_RFKILL_DEV_NUM; i++)
ideapad_unregister_rfkill(adevice, i);
ideapad_input_exit(priv);
@@ -459,12 +601,19 @@
vpc1 = (vpc2 << 8) | vpc1;
for (vpc_bit = 0; vpc_bit < 16; vpc_bit++) {
if (test_bit(vpc_bit, &vpc1)) {
- if (vpc_bit == 9)
+ switch (vpc_bit) {
+ case 9:
ideapad_sync_rfk_state(adevice);
- else if (vpc_bit == 4)
- read_ec_data(handle, 0x12, &vpc2);
- else
+ break;
+ case 4:
+ ideapad_backlight_notify_brightness(priv);
+ break;
+ case 2:
+ ideapad_backlight_notify_power(priv);
+ break;
+ default:
ideapad_input_report(priv, vpc_bit);
+ }
}
}
}
diff --git a/drivers/platform/x86/intel_ips.c b/drivers/platform/x86/intel_ips.c
index 5ffe7c3..809a3ae 100644
--- a/drivers/platform/x86/intel_ips.c
+++ b/drivers/platform/x86/intel_ips.c
@@ -403,7 +403,7 @@
thm_writew(THM_MPCPC, (new_tdp_limit * 10) / 8);
- turbo_override |= TURBO_TDC_OVR_EN | TURBO_TDC_OVR_EN;
+ turbo_override |= TURBO_TDC_OVR_EN | TURBO_TDP_OVR_EN;
wrmsrl(TURBO_POWER_CURRENT_LIMIT, turbo_override);
turbo_override &= ~TURBO_TDP_MASK;
@@ -438,7 +438,7 @@
thm_writew(THM_MPCPC, (new_limit * 10) / 8);
- turbo_override |= TURBO_TDC_OVR_EN | TURBO_TDC_OVR_EN;
+ turbo_override |= TURBO_TDC_OVR_EN | TURBO_TDP_OVR_EN;
wrmsrl(TURBO_POWER_CURRENT_LIMIT, turbo_override);
turbo_override &= ~TURBO_TDP_MASK;
diff --git a/drivers/platform/x86/intel_menlow.c b/drivers/platform/x86/intel_menlow.c
index 809adea..abddc83 100644
--- a/drivers/platform/x86/intel_menlow.c
+++ b/drivers/platform/x86/intel_menlow.c
@@ -477,6 +477,8 @@
return AE_ERROR;
}
+ return AE_OK;
+
aux1_not_found:
if (status == AE_NOT_FOUND)
return AE_OK;
diff --git a/drivers/platform/x86/intel_mid_thermal.c b/drivers/platform/x86/intel_mid_thermal.c
index 3a57832..ccd7b1f 100644
--- a/drivers/platform/x86/intel_mid_thermal.c
+++ b/drivers/platform/x86/intel_mid_thermal.c
@@ -493,20 +493,30 @@
/* Register each sensor with the generic thermal framework*/
for (i = 0; i < MSIC_THERMAL_SENSORS; i++) {
+ struct thermal_device_info *td_info = initialize_sensor(i);
+
+ if (!td_info) {
+ ret = -ENOMEM;
+ goto err;
+ }
pinfo->tzd[i] = thermal_zone_device_register(name[i],
- 0, initialize_sensor(i), &tzd_ops, 0, 0, 0, 0);
- if (IS_ERR(pinfo->tzd[i]))
- goto reg_fail;
+ 0, td_info, &tzd_ops, 0, 0, 0, 0);
+ if (IS_ERR(pinfo->tzd[i])) {
+ kfree(td_info);
+ ret = PTR_ERR(pinfo->tzd[i]);
+ goto err;
+ }
}
pinfo->pdev = pdev;
platform_set_drvdata(pdev, pinfo);
return 0;
-reg_fail:
- ret = PTR_ERR(pinfo->tzd[i]);
- while (--i >= 0)
+err:
+ while (--i >= 0) {
+ kfree(pinfo->tzd[i]->devdata);
thermal_zone_device_unregister(pinfo->tzd[i]);
+ }
configure_adc(0);
kfree(pinfo);
return ret;
@@ -524,8 +534,10 @@
int i;
struct platform_info *pinfo = platform_get_drvdata(pdev);
- for (i = 0; i < MSIC_THERMAL_SENSORS; i++)
+ for (i = 0; i < MSIC_THERMAL_SENSORS; i++) {
+ kfree(pinfo->tzd[i]->devdata);
thermal_zone_device_unregister(pinfo->tzd[i]);
+ }
kfree(pinfo);
platform_set_drvdata(pdev, NULL);
diff --git a/drivers/platform/x86/intel_rar_register.c b/drivers/platform/x86/intel_rar_register.c
index bde47e9..c8a6aed 100644
--- a/drivers/platform/x86/intel_rar_register.c
+++ b/drivers/platform/x86/intel_rar_register.c
@@ -637,15 +637,13 @@
return error;
}
-const struct pci_device_id rar_pci_id_tbl[] = {
+static DEFINE_PCI_DEVICE_TABLE(rar_pci_id_tbl) = {
{ PCI_VDEVICE(INTEL, 0x4110) },
{ 0 }
};
MODULE_DEVICE_TABLE(pci, rar_pci_id_tbl);
-const struct pci_device_id *my_id_table = rar_pci_id_tbl;
-
/* field for registering driver to PCI device */
static struct pci_driver rar_pci_driver = {
.name = "rar_register_driver",
diff --git a/drivers/platform/x86/intel_scu_ipc.c b/drivers/platform/x86/intel_scu_ipc.c
index 940accb..c866653 100644
--- a/drivers/platform/x86/intel_scu_ipc.c
+++ b/drivers/platform/x86/intel_scu_ipc.c
@@ -725,7 +725,7 @@
intel_scu_devices_destroy();
}
-static const struct pci_device_id pci_ids[] = {
+static DEFINE_PCI_DEVICE_TABLE(pci_ids) = {
{PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x080e)},
{PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x082a)},
{ 0,}
diff --git a/drivers/platform/x86/msi-laptop.c b/drivers/platform/x86/msi-laptop.c
index 3ff629d..f204643 100644
--- a/drivers/platform/x86/msi-laptop.c
+++ b/drivers/platform/x86/msi-laptop.c
@@ -538,6 +538,15 @@
},
.callback = dmi_check_cb
},
+ {
+ .ident = "MSI U270",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR,
+ "Micro-Star International Co., Ltd."),
+ DMI_MATCH(DMI_PRODUCT_NAME, "U270 series"),
+ },
+ .callback = dmi_check_cb
+ },
{ }
};
@@ -996,3 +1005,4 @@
MODULE_ALIAS("dmi:*:svnMICRO-STARINTERNATIONAL*:pnMS-N051:*");
MODULE_ALIAS("dmi:*:svnMICRO-STARINTERNATIONAL*:pnMS-N014:*");
MODULE_ALIAS("dmi:*:svnMicro-StarInternational*:pnCR620:*");
+MODULE_ALIAS("dmi:*:svnMicro-StarInternational*:pnU270series:*");
diff --git a/drivers/platform/x86/msi-wmi.c b/drivers/platform/x86/msi-wmi.c
index c832e33..6f40bf2 100644
--- a/drivers/platform/x86/msi-wmi.c
+++ b/drivers/platform/x86/msi-wmi.c
@@ -272,6 +272,7 @@
err_free_backlight:
backlight_device_unregister(backlight);
err_free_input:
+ sparse_keymap_free(msi_wmi_input_dev);
input_unregister_device(msi_wmi_input_dev);
err_uninstall_notifier:
wmi_remove_notify_handler(MSIWMI_EVENT_GUID);
diff --git a/drivers/platform/x86/samsung-laptop.c b/drivers/platform/x86/samsung-laptop.c
index d347116..3591630 100644
--- a/drivers/platform/x86/samsung-laptop.c
+++ b/drivers/platform/x86/samsung-laptop.c
@@ -521,6 +521,16 @@
.callback = dmi_check_cb,
},
{
+ .ident = "N510",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR,
+ "SAMSUNG ELECTRONICS CO., LTD."),
+ DMI_MATCH(DMI_PRODUCT_NAME, "N510"),
+ DMI_MATCH(DMI_BOARD_NAME, "N510"),
+ },
+ .callback = dmi_check_cb,
+ },
+ {
.ident = "X125",
.matches = {
DMI_MATCH(DMI_SYS_VENDOR,
@@ -601,6 +611,16 @@
.callback = dmi_check_cb,
},
{
+ .ident = "N150/N210/N220",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR,
+ "SAMSUNG ELECTRONICS CO., LTD."),
+ DMI_MATCH(DMI_PRODUCT_NAME, "N150/N210/N220"),
+ DMI_MATCH(DMI_BOARD_NAME, "N150/N210/N220"),
+ },
+ .callback = dmi_check_cb,
+ },
+ {
.ident = "N150/N210/N220/N230",
.matches = {
DMI_MATCH(DMI_SYS_VENDOR,
diff --git a/drivers/platform/x86/samsung-q10.c b/drivers/platform/x86/samsung-q10.c
new file mode 100644
index 0000000..1e54ae7
--- /dev/null
+++ b/drivers/platform/x86/samsung-q10.c
@@ -0,0 +1,196 @@
+/*
+ * Driver for Samsung Q10 and related laptops: controls the backlight
+ *
+ * Copyright (c) 2011 Frederick van der Wyck <fvanderwyck@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.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/platform_device.h>
+#include <linux/backlight.h>
+#include <linux/i8042.h>
+#include <linux/dmi.h>
+
+#define SAMSUNGQ10_BL_MAX_INTENSITY 255
+#define SAMSUNGQ10_BL_DEFAULT_INTENSITY 185
+
+#define SAMSUNGQ10_BL_8042_CMD 0xbe
+#define SAMSUNGQ10_BL_8042_DATA { 0x89, 0x91 }
+
+static int samsungq10_bl_brightness;
+
+static bool force;
+module_param(force, bool, 0);
+MODULE_PARM_DESC(force,
+ "Disable the DMI check and force the driver to be loaded");
+
+static int samsungq10_bl_set_intensity(struct backlight_device *bd)
+{
+
+ int brightness = bd->props.brightness;
+ unsigned char c[3] = SAMSUNGQ10_BL_8042_DATA;
+
+ c[2] = (unsigned char)brightness;
+ i8042_lock_chip();
+ i8042_command(c, (0x30 << 8) | SAMSUNGQ10_BL_8042_CMD);
+ i8042_unlock_chip();
+ samsungq10_bl_brightness = brightness;
+
+ return 0;
+}
+
+static int samsungq10_bl_get_intensity(struct backlight_device *bd)
+{
+ return samsungq10_bl_brightness;
+}
+
+static const struct backlight_ops samsungq10_bl_ops = {
+ .get_brightness = samsungq10_bl_get_intensity,
+ .update_status = samsungq10_bl_set_intensity,
+};
+
+#ifdef CONFIG_PM_SLEEP
+static int samsungq10_suspend(struct device *dev)
+{
+ return 0;
+}
+
+static int samsungq10_resume(struct device *dev)
+{
+
+ struct backlight_device *bd = dev_get_drvdata(dev);
+
+ samsungq10_bl_set_intensity(bd);
+ return 0;
+}
+#else
+#define samsungq10_suspend NULL
+#define samsungq10_resume NULL
+#endif
+
+static SIMPLE_DEV_PM_OPS(samsungq10_pm_ops,
+ samsungq10_suspend, samsungq10_resume);
+
+static int __devinit samsungq10_probe(struct platform_device *pdev)
+{
+
+ struct backlight_properties props;
+ struct backlight_device *bd;
+
+ memset(&props, 0, sizeof(struct backlight_properties));
+ props.type = BACKLIGHT_PLATFORM;
+ props.max_brightness = SAMSUNGQ10_BL_MAX_INTENSITY;
+ bd = backlight_device_register("samsung", &pdev->dev, NULL,
+ &samsungq10_bl_ops, &props);
+ if (IS_ERR(bd))
+ return PTR_ERR(bd);
+
+ platform_set_drvdata(pdev, bd);
+
+ bd->props.brightness = SAMSUNGQ10_BL_DEFAULT_INTENSITY;
+ samsungq10_bl_set_intensity(bd);
+
+ return 0;
+}
+
+static int __devexit samsungq10_remove(struct platform_device *pdev)
+{
+
+ struct backlight_device *bd = platform_get_drvdata(pdev);
+
+ bd->props.brightness = SAMSUNGQ10_BL_DEFAULT_INTENSITY;
+ samsungq10_bl_set_intensity(bd);
+
+ backlight_device_unregister(bd);
+
+ return 0;
+}
+
+static struct platform_driver samsungq10_driver = {
+ .driver = {
+ .name = KBUILD_MODNAME,
+ .owner = THIS_MODULE,
+ .pm = &samsungq10_pm_ops,
+ },
+ .probe = samsungq10_probe,
+ .remove = __devexit_p(samsungq10_remove),
+};
+
+static struct platform_device *samsungq10_device;
+
+static int __init dmi_check_callback(const struct dmi_system_id *id)
+{
+ printk(KERN_INFO KBUILD_MODNAME ": found model '%s'\n", id->ident);
+ return 1;
+}
+
+static struct dmi_system_id __initdata samsungq10_dmi_table[] = {
+ {
+ .ident = "Samsung Q10",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Samsung"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "SQ10"),
+ },
+ .callback = dmi_check_callback,
+ },
+ {
+ .ident = "Samsung Q20",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "SAMSUNG Electronics"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "SENS Q20"),
+ },
+ .callback = dmi_check_callback,
+ },
+ {
+ .ident = "Samsung Q25",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "SAMSUNG Electronics"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "NQ25"),
+ },
+ .callback = dmi_check_callback,
+ },
+ {
+ .ident = "Dell Latitude X200",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Dell Computer Corporation"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "X200"),
+ },
+ .callback = dmi_check_callback,
+ },
+ { },
+};
+MODULE_DEVICE_TABLE(dmi, samsungq10_dmi_table);
+
+static int __init samsungq10_init(void)
+{
+ if (!force && !dmi_check_system(samsungq10_dmi_table))
+ return -ENODEV;
+
+ samsungq10_device = platform_create_bundle(&samsungq10_driver,
+ samsungq10_probe,
+ NULL, 0, NULL, 0);
+
+ if (IS_ERR(samsungq10_device))
+ return PTR_ERR(samsungq10_device);
+
+ return 0;
+}
+
+static void __exit samsungq10_exit(void)
+{
+ platform_device_unregister(samsungq10_device);
+ platform_driver_unregister(&samsungq10_driver);
+}
+
+module_init(samsungq10_init);
+module_exit(samsungq10_exit);
+
+MODULE_AUTHOR("Frederick van der Wyck <fvanderwyck@gmail.com>");
+MODULE_DESCRIPTION("Samsung Q10 Driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/platform/x86/thinkpad_acpi.c b/drivers/platform/x86/thinkpad_acpi.c
index 26c5b11..7bd829f 100644
--- a/drivers/platform/x86/thinkpad_acpi.c
+++ b/drivers/platform/x86/thinkpad_acpi.c
@@ -3186,8 +3186,17 @@
KEY_VENDOR, /* 0x17: Thinkpad/AccessIBM/Lenovo */
/* (assignments unknown, please report if found) */
+ KEY_UNKNOWN, KEY_UNKNOWN,
+
+ /*
+ * The mic mute button only sends 0x1a. It does not
+ * automatically mute the mic or change the mute light.
+ */
+ KEY_MICMUTE, /* 0x1a: Mic mute (since ?400 or so) */
+
+ /* (assignments unknown, please report if found) */
KEY_UNKNOWN, KEY_UNKNOWN, KEY_UNKNOWN, KEY_UNKNOWN,
- KEY_UNKNOWN, KEY_UNKNOWN, KEY_UNKNOWN, KEY_UNKNOWN,
+ KEY_UNKNOWN,
},
};
diff --git a/drivers/regulator/Kconfig b/drivers/regulator/Kconfig
index 118eb21..c7fd2c0 100644
--- a/drivers/regulator/Kconfig
+++ b/drivers/regulator/Kconfig
@@ -249,6 +249,12 @@
three step-down converters and two general-purpose LDO voltage regulators.
It supports TI's software based Class-2 SmartReflex implementation.
+config REGULATOR_TPS65912
+ tristate "TI TPS65912 Power regulator"
+ depends on (MFD_TPS65912_I2C || MFD_TPS65912_SPI)
+ help
+ This driver supports TPS65912 voltage regulator chip.
+
config REGULATOR_88PM8607
bool "Marvell 88PM8607 Power regulators"
depends on MFD_88PM860X=y
@@ -304,5 +310,12 @@
help
This driver supports TPS65910 voltage regulator chips.
+config REGULATOR_AAT2870
+ tristate "AnalogicTech AAT2870 Regulators"
+ depends on MFD_AAT2870_CORE
+ help
+ If you have a AnalogicTech AAT2870 say Y to enable the
+ regulator driver.
+
endif
diff --git a/drivers/regulator/Makefile b/drivers/regulator/Makefile
index 3932d2e..040d5aa 100644
--- a/drivers/regulator/Makefile
+++ b/drivers/regulator/Makefile
@@ -38,10 +38,12 @@
obj-$(CONFIG_REGULATOR_TPS65023) += tps65023-regulator.o
obj-$(CONFIG_REGULATOR_TPS6507X) += tps6507x-regulator.o
obj-$(CONFIG_REGULATOR_TPS6524X) += tps6524x-regulator.o
+obj-$(CONFIG_REGULATOR_TPS65912) += tps65912-regulator.o
obj-$(CONFIG_REGULATOR_88PM8607) += 88pm8607.o
obj-$(CONFIG_REGULATOR_ISL6271A) += isl6271a-regulator.o
obj-$(CONFIG_REGULATOR_AB8500) += ab8500.o
obj-$(CONFIG_REGULATOR_DB8500_PRCMU) += db8500-prcmu.o
obj-$(CONFIG_REGULATOR_TPS65910) += tps65910-regulator.o
+obj-$(CONFIG_REGULATOR_AAT2870) += aat2870-regulator.o
ccflags-$(CONFIG_REGULATOR_DEBUG) += -DDEBUG
diff --git a/drivers/regulator/aat2870-regulator.c b/drivers/regulator/aat2870-regulator.c
new file mode 100644
index 0000000..cd41045
--- /dev/null
+++ b/drivers/regulator/aat2870-regulator.c
@@ -0,0 +1,232 @@
+/*
+ * linux/drivers/regulator/aat2870-regulator.c
+ *
+ * Copyright (c) 2011, NVIDIA Corporation.
+ * Author: Jin Park <jinyoungp@nvidia.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., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ */
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/err.h>
+#include <linux/slab.h>
+#include <linux/delay.h>
+#include <linux/platform_device.h>
+#include <linux/regulator/driver.h>
+#include <linux/regulator/machine.h>
+#include <linux/mfd/aat2870.h>
+
+struct aat2870_regulator {
+ struct platform_device *pdev;
+ struct regulator_desc desc;
+
+ const int *voltages; /* uV */
+
+ int min_uV;
+ int max_uV;
+
+ u8 enable_addr;
+ u8 enable_shift;
+ u8 enable_mask;
+
+ u8 voltage_addr;
+ u8 voltage_shift;
+ u8 voltage_mask;
+};
+
+static int aat2870_ldo_list_voltage(struct regulator_dev *rdev,
+ unsigned selector)
+{
+ struct aat2870_regulator *ri = rdev_get_drvdata(rdev);
+
+ return ri->voltages[selector];
+}
+
+static int aat2870_ldo_set_voltage_sel(struct regulator_dev *rdev,
+ unsigned selector)
+{
+ struct aat2870_regulator *ri = rdev_get_drvdata(rdev);
+ struct aat2870_data *aat2870 = dev_get_drvdata(ri->pdev->dev.parent);
+
+ return aat2870->update(aat2870, ri->voltage_addr, ri->voltage_mask,
+ (selector << ri->voltage_shift) & ri->voltage_mask);
+}
+
+static int aat2870_ldo_get_voltage_sel(struct regulator_dev *rdev)
+{
+ struct aat2870_regulator *ri = rdev_get_drvdata(rdev);
+ struct aat2870_data *aat2870 = dev_get_drvdata(ri->pdev->dev.parent);
+ u8 val;
+ int ret;
+
+ ret = aat2870->read(aat2870, ri->voltage_addr, &val);
+ if (ret)
+ return ret;
+
+ return (val & ri->voltage_mask) >> ri->voltage_shift;
+}
+
+static int aat2870_ldo_enable(struct regulator_dev *rdev)
+{
+ struct aat2870_regulator *ri = rdev_get_drvdata(rdev);
+ struct aat2870_data *aat2870 = dev_get_drvdata(ri->pdev->dev.parent);
+
+ return aat2870->update(aat2870, ri->enable_addr, ri->enable_mask,
+ ri->enable_mask);
+}
+
+static int aat2870_ldo_disable(struct regulator_dev *rdev)
+{
+ struct aat2870_regulator *ri = rdev_get_drvdata(rdev);
+ struct aat2870_data *aat2870 = dev_get_drvdata(ri->pdev->dev.parent);
+
+ return aat2870->update(aat2870, ri->enable_addr, ri->enable_mask, 0);
+}
+
+static int aat2870_ldo_is_enabled(struct regulator_dev *rdev)
+{
+ struct aat2870_regulator *ri = rdev_get_drvdata(rdev);
+ struct aat2870_data *aat2870 = dev_get_drvdata(ri->pdev->dev.parent);
+ u8 val;
+ int ret;
+
+ ret = aat2870->read(aat2870, ri->enable_addr, &val);
+ if (ret)
+ return ret;
+
+ return val & ri->enable_mask ? 1 : 0;
+}
+
+static struct regulator_ops aat2870_ldo_ops = {
+ .list_voltage = aat2870_ldo_list_voltage,
+ .set_voltage_sel = aat2870_ldo_set_voltage_sel,
+ .get_voltage_sel = aat2870_ldo_get_voltage_sel,
+ .enable = aat2870_ldo_enable,
+ .disable = aat2870_ldo_disable,
+ .is_enabled = aat2870_ldo_is_enabled,
+};
+
+static const int aat2870_ldo_voltages[] = {
+ 1200000, 1300000, 1500000, 1600000,
+ 1800000, 2000000, 2200000, 2500000,
+ 2600000, 2700000, 2800000, 2900000,
+ 3000000, 3100000, 3200000, 3300000,
+};
+
+#define AAT2870_LDO(ids) \
+ { \
+ .desc = { \
+ .name = #ids, \
+ .id = AAT2870_ID_##ids, \
+ .n_voltages = ARRAY_SIZE(aat2870_ldo_voltages), \
+ .ops = &aat2870_ldo_ops, \
+ .type = REGULATOR_VOLTAGE, \
+ .owner = THIS_MODULE, \
+ }, \
+ .voltages = aat2870_ldo_voltages, \
+ .min_uV = 1200000, \
+ .max_uV = 3300000, \
+ }
+
+static struct aat2870_regulator aat2870_regulators[] = {
+ AAT2870_LDO(LDOA),
+ AAT2870_LDO(LDOB),
+ AAT2870_LDO(LDOC),
+ AAT2870_LDO(LDOD),
+};
+
+static struct aat2870_regulator *aat2870_get_regulator(int id)
+{
+ struct aat2870_regulator *ri = NULL;
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(aat2870_regulators); i++) {
+ ri = &aat2870_regulators[i];
+ if (ri->desc.id == id)
+ break;
+ }
+
+ if (!ri)
+ return NULL;
+
+ ri->enable_addr = AAT2870_LDO_EN;
+ ri->enable_shift = id - AAT2870_ID_LDOA;
+ ri->enable_mask = 0x1 << ri->enable_shift;
+
+ ri->voltage_addr = (id - AAT2870_ID_LDOA) / 2 ?
+ AAT2870_LDO_CD : AAT2870_LDO_AB;
+ ri->voltage_shift = (id - AAT2870_ID_LDOA) % 2 ? 0 : 4;
+ ri->voltage_mask = 0xF << ri->voltage_shift;
+
+ return ri;
+}
+
+static int aat2870_regulator_probe(struct platform_device *pdev)
+{
+ struct aat2870_regulator *ri;
+ struct regulator_dev *rdev;
+
+ ri = aat2870_get_regulator(pdev->id);
+ if (!ri) {
+ dev_err(&pdev->dev, "Invalid device ID, %d\n", pdev->id);
+ return -EINVAL;
+ }
+ ri->pdev = pdev;
+
+ rdev = regulator_register(&ri->desc, &pdev->dev,
+ pdev->dev.platform_data, ri);
+ if (IS_ERR(rdev)) {
+ dev_err(&pdev->dev, "Failed to register regulator %s\n",
+ ri->desc.name);
+ return PTR_ERR(rdev);
+ }
+ platform_set_drvdata(pdev, rdev);
+
+ return 0;
+}
+
+static int __devexit aat2870_regulator_remove(struct platform_device *pdev)
+{
+ struct regulator_dev *rdev = platform_get_drvdata(pdev);
+
+ regulator_unregister(rdev);
+ return 0;
+}
+
+static struct platform_driver aat2870_regulator_driver = {
+ .driver = {
+ .name = "aat2870-regulator",
+ .owner = THIS_MODULE,
+ },
+ .probe = aat2870_regulator_probe,
+ .remove = __devexit_p(aat2870_regulator_remove),
+};
+
+static int __init aat2870_regulator_init(void)
+{
+ return platform_driver_register(&aat2870_regulator_driver);
+}
+subsys_initcall(aat2870_regulator_init);
+
+static void __exit aat2870_regulator_exit(void)
+{
+ platform_driver_unregister(&aat2870_regulator_driver);
+}
+module_exit(aat2870_regulator_exit);
+
+MODULE_DESCRIPTION("AnalogicTech AAT2870 Regulator");
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Jin Park <jinyoungp@nvidia.com>");
diff --git a/drivers/regulator/core.c b/drivers/regulator/core.c
index d3e3879..d8e6a42 100644
--- a/drivers/regulator/core.c
+++ b/drivers/regulator/core.c
@@ -20,6 +20,7 @@
#include <linux/debugfs.h>
#include <linux/device.h>
#include <linux/slab.h>
+#include <linux/async.h>
#include <linux/err.h>
#include <linux/mutex.h>
#include <linux/suspend.h>
@@ -33,6 +34,8 @@
#include "dummy.h"
+#define rdev_crit(rdev, fmt, ...) \
+ pr_crit("%s: " fmt, rdev_get_name(rdev), ##__VA_ARGS__)
#define rdev_err(rdev, fmt, ...) \
pr_err("%s: " fmt, rdev_get_name(rdev), ##__VA_ARGS__)
#define rdev_warn(rdev, fmt, ...) \
@@ -78,11 +81,13 @@
char *supply_name;
struct device_attribute dev_attr;
struct regulator_dev *rdev;
+#ifdef CONFIG_DEBUG_FS
+ struct dentry *debugfs;
+#endif
};
static int _regulator_is_enabled(struct regulator_dev *rdev);
-static int _regulator_disable(struct regulator_dev *rdev,
- struct regulator_dev **supply_rdev_ptr);
+static int _regulator_disable(struct regulator_dev *rdev);
static int _regulator_get_voltage(struct regulator_dev *rdev);
static int _regulator_get_current_limit(struct regulator_dev *rdev);
static unsigned int _regulator_get_mode(struct regulator_dev *rdev);
@@ -90,6 +95,9 @@
unsigned long event, void *data);
static int _regulator_do_set_voltage(struct regulator_dev *rdev,
int min_uV, int max_uV);
+static struct regulator *create_regulator(struct regulator_dev *rdev,
+ struct device *dev,
+ const char *supply_name);
static const char *rdev_get_name(struct regulator_dev *rdev)
{
@@ -143,8 +151,11 @@
if (*min_uV < rdev->constraints->min_uV)
*min_uV = rdev->constraints->min_uV;
- if (*min_uV > *max_uV)
+ if (*min_uV > *max_uV) {
+ rdev_err(rdev, "unsupportable voltage range: %d-%duV\n",
+ *min_uV, *max_uV);
return -EINVAL;
+ }
return 0;
}
@@ -197,8 +208,11 @@
if (*min_uA < rdev->constraints->min_uA)
*min_uA = rdev->constraints->min_uA;
- if (*min_uA > *max_uA)
+ if (*min_uA > *max_uA) {
+ rdev_err(rdev, "unsupportable current range: %d-%duA\n",
+ *min_uA, *max_uA);
return -EINVAL;
+ }
return 0;
}
@@ -213,6 +227,7 @@
case REGULATOR_MODE_STANDBY:
break;
default:
+ rdev_err(rdev, "invalid mode %x specified\n", *mode);
return -EINVAL;
}
@@ -779,7 +794,6 @@
if (ret < 0) {
rdev_err(rdev, "failed to apply %duV constraint\n",
rdev->constraints->min_uV);
- rdev->constraints = NULL;
return ret;
}
}
@@ -882,7 +896,6 @@
ret = suspend_prepare(rdev, rdev->constraints->initial_state);
if (ret < 0) {
rdev_err(rdev, "failed to set suspend state\n");
- rdev->constraints = NULL;
goto out;
}
}
@@ -909,13 +922,15 @@
ret = ops->enable(rdev);
if (ret < 0) {
rdev_err(rdev, "failed to enable\n");
- rdev->constraints = NULL;
goto out;
}
}
print_constraints(rdev);
+ return 0;
out:
+ kfree(rdev->constraints);
+ rdev->constraints = NULL;
return ret;
}
@@ -929,21 +944,20 @@
* core if it's child is enabled.
*/
static int set_supply(struct regulator_dev *rdev,
- struct regulator_dev *supply_rdev)
+ struct regulator_dev *supply_rdev)
{
int err;
- err = sysfs_create_link(&rdev->dev.kobj, &supply_rdev->dev.kobj,
- "supply");
- if (err) {
- rdev_err(rdev, "could not add device link %s err %d\n",
- supply_rdev->dev.kobj.name, err);
- goto out;
+ rdev_info(rdev, "supplied by %s\n", rdev_get_name(supply_rdev));
+
+ rdev->supply = create_regulator(supply_rdev, &rdev->dev, "SUPPLY");
+ if (IS_ERR(rdev->supply)) {
+ err = PTR_ERR(rdev->supply);
+ rdev->supply = NULL;
+ return err;
}
- rdev->supply = supply_rdev;
- list_add(&rdev->slist, &supply_rdev->supply_list);
-out:
- return err;
+
+ return 0;
}
/**
@@ -1032,7 +1046,7 @@
}
}
-#define REG_STR_SIZE 32
+#define REG_STR_SIZE 64
static struct regulator *create_regulator(struct regulator_dev *rdev,
struct device *dev,
@@ -1052,8 +1066,9 @@
if (dev) {
/* create a 'requested_microamps_name' sysfs entry */
- size = scnprintf(buf, REG_STR_SIZE, "microamps_requested_%s",
- supply_name);
+ size = scnprintf(buf, REG_STR_SIZE,
+ "microamps_requested_%s-%s",
+ dev_name(dev), supply_name);
if (size >= REG_STR_SIZE)
goto overflow_err;
@@ -1088,7 +1103,28 @@
dev->kobj.name, err);
goto link_name_err;
}
+ } else {
+ regulator->supply_name = kstrdup(supply_name, GFP_KERNEL);
+ if (regulator->supply_name == NULL)
+ goto attr_err;
}
+
+#ifdef CONFIG_DEBUG_FS
+ regulator->debugfs = debugfs_create_dir(regulator->supply_name,
+ rdev->debugfs);
+ if (IS_ERR_OR_NULL(regulator->debugfs)) {
+ rdev_warn(rdev, "Failed to create debugfs directory\n");
+ regulator->debugfs = NULL;
+ } else {
+ debugfs_create_u32("uA_load", 0444, regulator->debugfs,
+ ®ulator->uA_load);
+ debugfs_create_u32("min_uV", 0444, regulator->debugfs,
+ ®ulator->min_uV);
+ debugfs_create_u32("max_uV", 0444, regulator->debugfs,
+ ®ulator->max_uV);
+ }
+#endif
+
mutex_unlock(&rdev->mutex);
return regulator;
link_name_err:
@@ -1267,13 +1303,17 @@
mutex_lock(®ulator_list_mutex);
rdev = regulator->rdev;
+#ifdef CONFIG_DEBUG_FS
+ debugfs_remove_recursive(regulator->debugfs);
+#endif
+
/* remove any sysfs entries */
if (regulator->dev) {
sysfs_remove_link(&rdev->dev.kobj, regulator->supply_name);
- kfree(regulator->supply_name);
device_remove_file(regulator->dev, ®ulator->dev_attr);
kfree(regulator->dev_attr.attr.name);
}
+ kfree(regulator->supply_name);
list_del(®ulator->list);
kfree(regulator);
@@ -1301,19 +1341,6 @@
{
int ret, delay;
- if (rdev->use_count == 0) {
- /* do we need to enable the supply regulator first */
- if (rdev->supply) {
- mutex_lock(&rdev->supply->mutex);
- ret = _regulator_enable(rdev->supply);
- mutex_unlock(&rdev->supply->mutex);
- if (ret < 0) {
- rdev_err(rdev, "failed to enable: %d\n", ret);
- return ret;
- }
- }
- }
-
/* check voltage and requested load before enabling */
if (rdev->constraints &&
(rdev->constraints->valid_ops_mask & REGULATOR_CHANGE_DRMS))
@@ -1388,19 +1415,27 @@
struct regulator_dev *rdev = regulator->rdev;
int ret = 0;
+ if (rdev->supply) {
+ ret = regulator_enable(rdev->supply);
+ if (ret != 0)
+ return ret;
+ }
+
mutex_lock(&rdev->mutex);
ret = _regulator_enable(rdev);
mutex_unlock(&rdev->mutex);
+
+ if (ret != 0)
+ regulator_disable(rdev->supply);
+
return ret;
}
EXPORT_SYMBOL_GPL(regulator_enable);
/* locks held by regulator_disable() */
-static int _regulator_disable(struct regulator_dev *rdev,
- struct regulator_dev **supply_rdev_ptr)
+static int _regulator_disable(struct regulator_dev *rdev)
{
int ret = 0;
- *supply_rdev_ptr = NULL;
if (WARN(rdev->use_count <= 0,
"unbalanced disables for %s\n", rdev_get_name(rdev)))
@@ -1427,9 +1462,6 @@
NULL);
}
- /* decrease our supplies ref count and disable if required */
- *supply_rdev_ptr = rdev->supply;
-
rdev->use_count = 0;
} else if (rdev->use_count > 1) {
@@ -1440,6 +1472,7 @@
rdev->use_count--;
}
+
return ret;
}
@@ -1458,29 +1491,21 @@
int regulator_disable(struct regulator *regulator)
{
struct regulator_dev *rdev = regulator->rdev;
- struct regulator_dev *supply_rdev = NULL;
int ret = 0;
mutex_lock(&rdev->mutex);
- ret = _regulator_disable(rdev, &supply_rdev);
+ ret = _regulator_disable(rdev);
mutex_unlock(&rdev->mutex);
- /* decrease our supplies ref count and disable if required */
- while (supply_rdev != NULL) {
- rdev = supply_rdev;
-
- mutex_lock(&rdev->mutex);
- _regulator_disable(rdev, &supply_rdev);
- mutex_unlock(&rdev->mutex);
- }
+ if (ret == 0 && rdev->supply)
+ regulator_disable(rdev->supply);
return ret;
}
EXPORT_SYMBOL_GPL(regulator_disable);
/* locks held by regulator_force_disable() */
-static int _regulator_force_disable(struct regulator_dev *rdev,
- struct regulator_dev **supply_rdev_ptr)
+static int _regulator_force_disable(struct regulator_dev *rdev)
{
int ret = 0;
@@ -1497,10 +1522,6 @@
REGULATOR_EVENT_DISABLE, NULL);
}
- /* decrease our supplies ref count and disable if required */
- *supply_rdev_ptr = rdev->supply;
-
- rdev->use_count = 0;
return ret;
}
@@ -1516,16 +1537,16 @@
int regulator_force_disable(struct regulator *regulator)
{
struct regulator_dev *rdev = regulator->rdev;
- struct regulator_dev *supply_rdev = NULL;
int ret;
mutex_lock(&rdev->mutex);
regulator->uA_load = 0;
- ret = _regulator_force_disable(rdev, &supply_rdev);
+ ret = _regulator_force_disable(regulator->rdev);
mutex_unlock(&rdev->mutex);
- if (supply_rdev)
- regulator_disable(get_device_regulator(rdev_get_dev(supply_rdev)));
+ if (rdev->supply)
+ while (rdev->open_count--)
+ regulator_disable(rdev->supply);
return ret;
}
@@ -2136,7 +2157,7 @@
/* get input voltage */
input_uV = 0;
if (rdev->supply)
- input_uV = _regulator_get_voltage(rdev->supply);
+ input_uV = regulator_get_voltage(rdev->supply);
if (input_uV <= 0)
input_uV = rdev->constraints->input_uV;
if (input_uV <= 0) {
@@ -2206,17 +2227,8 @@
static void _notifier_call_chain(struct regulator_dev *rdev,
unsigned long event, void *data)
{
- struct regulator_dev *_rdev;
-
/* call rdev chain first */
blocking_notifier_call_chain(&rdev->notifier, event, NULL);
-
- /* now notify regulator we supply */
- list_for_each_entry(_rdev, &rdev->supply_list, slist) {
- mutex_lock(&_rdev->mutex);
- _notifier_call_chain(_rdev, event, data);
- mutex_unlock(&_rdev->mutex);
- }
}
/**
@@ -2264,6 +2276,13 @@
}
EXPORT_SYMBOL_GPL(regulator_bulk_get);
+static void regulator_bulk_enable_async(void *data, async_cookie_t cookie)
+{
+ struct regulator_bulk_data *bulk = data;
+
+ bulk->ret = regulator_enable(bulk->consumer);
+}
+
/**
* regulator_bulk_enable - enable multiple regulator consumers
*
@@ -2279,21 +2298,33 @@
int regulator_bulk_enable(int num_consumers,
struct regulator_bulk_data *consumers)
{
+ LIST_HEAD(async_domain);
int i;
- int ret;
+ int ret = 0;
+ for (i = 0; i < num_consumers; i++)
+ async_schedule_domain(regulator_bulk_enable_async,
+ &consumers[i], &async_domain);
+
+ async_synchronize_full_domain(&async_domain);
+
+ /* If any consumer failed we need to unwind any that succeeded */
for (i = 0; i < num_consumers; i++) {
- ret = regulator_enable(consumers[i].consumer);
- if (ret != 0)
+ if (consumers[i].ret != 0) {
+ ret = consumers[i].ret;
goto err;
+ }
}
return 0;
err:
- pr_err("Failed to enable %s: %d\n", consumers[i].supply, ret);
- for (--i; i >= 0; --i)
- regulator_disable(consumers[i].consumer);
+ for (i = 0; i < num_consumers; i++)
+ if (consumers[i].ret == 0)
+ regulator_disable(consumers[i].consumer);
+ else
+ pr_err("Failed to enable %s: %d\n",
+ consumers[i].supply, consumers[i].ret);
return ret;
}
@@ -2589,9 +2620,7 @@
rdev->owner = regulator_desc->owner;
rdev->desc = regulator_desc;
INIT_LIST_HEAD(&rdev->consumer_list);
- INIT_LIST_HEAD(&rdev->supply_list);
INIT_LIST_HEAD(&rdev->list);
- INIT_LIST_HEAD(&rdev->slist);
BLOCKING_INIT_NOTIFIER_HEAD(&rdev->notifier);
/* preform any regulator specific init */
@@ -2672,6 +2701,7 @@
unset_regulator_supplies(rdev);
scrub:
+ kfree(rdev->constraints);
device_unregister(&rdev->dev);
/* device core frees rdev */
rdev = ERR_PTR(ret);
@@ -2703,7 +2733,7 @@
unset_regulator_supplies(rdev);
list_del(&rdev->list);
if (rdev->supply)
- sysfs_remove_link(&rdev->dev.kobj, "supply");
+ regulator_put(rdev->supply);
device_unregister(&rdev->dev);
kfree(rdev->constraints);
mutex_unlock(®ulator_list_mutex);
diff --git a/drivers/regulator/dummy.c b/drivers/regulator/dummy.c
index c7410bd..f6ef669 100644
--- a/drivers/regulator/dummy.c
+++ b/drivers/regulator/dummy.c
@@ -36,6 +36,29 @@
.ops = &dummy_ops,
};
+static int __devinit dummy_regulator_probe(struct platform_device *pdev)
+{
+ int ret;
+
+ dummy_regulator_rdev = regulator_register(&dummy_desc, NULL,
+ &dummy_initdata, NULL);
+ if (IS_ERR(dummy_regulator_rdev)) {
+ ret = PTR_ERR(dummy_regulator_rdev);
+ pr_err("Failed to register regulator: %d\n", ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static struct platform_driver dummy_regulator_driver = {
+ .probe = dummy_regulator_probe,
+ .driver = {
+ .name = "reg-dummy",
+ .owner = THIS_MODULE,
+ },
+};
+
static struct platform_device *dummy_pdev;
void __init regulator_dummy_init(void)
@@ -55,12 +78,9 @@
return;
}
- dummy_regulator_rdev = regulator_register(&dummy_desc, NULL,
- &dummy_initdata, NULL);
- if (IS_ERR(dummy_regulator_rdev)) {
- ret = PTR_ERR(dummy_regulator_rdev);
- pr_err("Failed to register regulator: %d\n", ret);
+ ret = platform_driver_register(&dummy_regulator_driver);
+ if (ret != 0) {
+ pr_err("Failed to register dummy regulator driver: %d\n", ret);
platform_device_unregister(dummy_pdev);
- return;
}
}
diff --git a/drivers/regulator/tps65910-regulator.c b/drivers/regulator/tps65910-regulator.c
index 55dd4e6..66d2d60 100644
--- a/drivers/regulator/tps65910-regulator.c
+++ b/drivers/regulator/tps65910-regulator.c
@@ -49,7 +49,6 @@
#define TPS65911_REG_LDO7 11
#define TPS65911_REG_LDO8 12
-#define TPS65910_NUM_REGULATOR 13
#define TPS65910_SUPPLY_STATE_ENABLED 0x1
/* supported VIO voltages in milivolts */
@@ -264,11 +263,12 @@
};
struct tps65910_reg {
- struct regulator_desc desc[TPS65910_NUM_REGULATOR];
+ struct regulator_desc *desc;
struct tps65910 *mfd;
- struct regulator_dev *rdev[TPS65910_NUM_REGULATOR];
- struct tps_info *info[TPS65910_NUM_REGULATOR];
+ struct regulator_dev **rdev;
+ struct tps_info **info;
struct mutex mutex;
+ int num_regulators;
int mode;
int (*get_ctrl_reg)(int);
};
@@ -759,8 +759,13 @@
mult = (selector / VDD1_2_NUM_VOLTS) + 1;
volt = VDD1_2_MIN_VOLT +
(selector % VDD1_2_NUM_VOLTS) * VDD1_2_OFFSET;
+ break;
case TPS65911_REG_VDDCTRL:
volt = VDDCTRL_MIN_VOLT + (selector * VDDCTRL_OFFSET);
+ break;
+ default:
+ BUG();
+ return -EINVAL;
}
return volt * 100 * mult;
@@ -897,16 +902,42 @@
switch(tps65910_chip_id(tps65910)) {
case TPS65910:
pmic->get_ctrl_reg = &tps65910_get_ctrl_register;
+ pmic->num_regulators = ARRAY_SIZE(tps65910_regs);
info = tps65910_regs;
+ break;
case TPS65911:
pmic->get_ctrl_reg = &tps65911_get_ctrl_register;
+ pmic->num_regulators = ARRAY_SIZE(tps65911_regs);
info = tps65911_regs;
+ break;
default:
pr_err("Invalid tps chip version\n");
+ kfree(pmic);
return -ENODEV;
}
- for (i = 0; i < TPS65910_NUM_REGULATOR; i++, info++, reg_data++) {
+ pmic->desc = kcalloc(pmic->num_regulators,
+ sizeof(struct regulator_desc), GFP_KERNEL);
+ if (!pmic->desc) {
+ err = -ENOMEM;
+ goto err_free_pmic;
+ }
+
+ pmic->info = kcalloc(pmic->num_regulators,
+ sizeof(struct tps_info *), GFP_KERNEL);
+ if (!pmic->info) {
+ err = -ENOMEM;
+ goto err_free_desc;
+ }
+
+ pmic->rdev = kcalloc(pmic->num_regulators,
+ sizeof(struct regulator_dev *), GFP_KERNEL);
+ if (!pmic->rdev) {
+ err = -ENOMEM;
+ goto err_free_info;
+ }
+
+ for (i = 0; i < pmic->num_regulators; i++, info++, reg_data++) {
/* Register the regulators */
pmic->info[i] = info;
@@ -938,7 +969,7 @@
"failed to register %s regulator\n",
pdev->name);
err = PTR_ERR(rdev);
- goto err;
+ goto err_unregister_regulator;
}
/* Save regulator for cleanup */
@@ -946,23 +977,31 @@
}
return 0;
-err:
+err_unregister_regulator:
while (--i >= 0)
regulator_unregister(pmic->rdev[i]);
-
+ kfree(pmic->rdev);
+err_free_info:
+ kfree(pmic->info);
+err_free_desc:
+ kfree(pmic->desc);
+err_free_pmic:
kfree(pmic);
return err;
}
static int __devexit tps65910_remove(struct platform_device *pdev)
{
- struct tps65910_reg *tps65910_reg = platform_get_drvdata(pdev);
+ struct tps65910_reg *pmic = platform_get_drvdata(pdev);
int i;
- for (i = 0; i < TPS65910_NUM_REGULATOR; i++)
- regulator_unregister(tps65910_reg->rdev[i]);
+ for (i = 0; i < pmic->num_regulators; i++)
+ regulator_unregister(pmic->rdev[i]);
- kfree(tps65910_reg);
+ kfree(pmic->rdev);
+ kfree(pmic->info);
+ kfree(pmic->desc);
+ kfree(pmic);
return 0;
}
diff --git a/drivers/regulator/tps65912-regulator.c b/drivers/regulator/tps65912-regulator.c
new file mode 100644
index 0000000..3a9313e
--- /dev/null
+++ b/drivers/regulator/tps65912-regulator.c
@@ -0,0 +1,800 @@
+/*
+ * tps65912.c -- TI tps65912
+ *
+ * Copyright 2011 Texas Instruments Inc.
+ *
+ * Author: Margarita Olaya Cabrera <magi@slimlogic.co.uk>
+ *
+ * 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 driver is based on wm8350 implementation.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/err.h>
+#include <linux/platform_device.h>
+#include <linux/regulator/driver.h>
+#include <linux/regulator/machine.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/gpio.h>
+#include <linux/mfd/tps65912.h>
+
+/* DCDC's */
+#define TPS65912_REG_DCDC1 0
+#define TPS65912_REG_DCDC2 1
+#define TPS65912_REG_DCDC3 2
+#define TPS65912_REG_DCDC4 3
+
+/* LDOs */
+#define TPS65912_REG_LDO1 4
+#define TPS65912_REG_LDO2 5
+#define TPS65912_REG_LDO3 6
+#define TPS65912_REG_LDO4 7
+#define TPS65912_REG_LDO5 8
+#define TPS65912_REG_LDO6 9
+#define TPS65912_REG_LDO7 10
+#define TPS65912_REG_LDO8 11
+#define TPS65912_REG_LDO9 12
+#define TPS65912_REG_LDO10 13
+
+#define TPS65912_MAX_REG_ID TPS65912_REG_LDO_10
+
+/* Number of step-down converters available */
+#define TPS65912_NUM_DCDC 4
+
+/* Number of LDO voltage regulators available */
+#define TPS65912_NUM_LDO 10
+
+/* Number of total regulators available */
+#define TPS65912_NUM_REGULATOR (TPS65912_NUM_DCDC + TPS65912_NUM_LDO)
+
+#define TPS65912_REG_ENABLED 0x80
+#define OP_SELREG_MASK 0x40
+#define OP_SELREG_SHIFT 6
+
+struct tps_info {
+ const char *name;
+};
+
+static struct tps_info tps65912_regs[] = {
+ {
+ .name = "DCDC1",
+ },
+ {
+ .name = "DCDC2",
+ },
+ {
+ .name = "DCDC3",
+ },
+ {
+ .name = "DCDC4",
+ },
+ {
+ .name = "LDO1",
+ },
+ {
+ .name = "LDO2",
+ },
+ {
+ .name = "LDO3",
+ },
+ {
+ .name = "LDO4",
+ },
+ {
+ .name = "LDO5",
+ },
+ {
+ .name = "LDO6",
+ },
+ {
+ .name = "LDO7",
+ },
+ {
+ .name = "LDO8",
+ },
+ {
+ .name = "LDO9",
+ },
+ {
+ .name = "LDO10",
+ },
+};
+
+struct tps65912_reg {
+ struct regulator_desc desc[TPS65912_NUM_REGULATOR];
+ struct tps65912 *mfd;
+ struct regulator_dev *rdev[TPS65912_NUM_REGULATOR];
+ struct tps_info *info[TPS65912_NUM_REGULATOR];
+ /* for read/write access */
+ struct mutex io_lock;
+ int mode;
+ int (*get_ctrl_reg)(int);
+ int dcdc1_range;
+ int dcdc2_range;
+ int dcdc3_range;
+ int dcdc4_range;
+ int pwm_mode_reg;
+ int eco_reg;
+};
+
+static int tps65912_get_range(struct tps65912_reg *pmic, int id)
+{
+ struct tps65912 *mfd = pmic->mfd;
+
+ if (id > TPS65912_REG_DCDC4)
+ return 0;
+
+ switch (id) {
+ case TPS65912_REG_DCDC1:
+ pmic->dcdc1_range = tps65912_reg_read(mfd,
+ TPS65912_DCDC1_LIMIT);
+ if (pmic->dcdc1_range < 0)
+ return pmic->dcdc1_range;
+ pmic->dcdc1_range = (pmic->dcdc1_range &
+ DCDC_LIMIT_RANGE_MASK) >> DCDC_LIMIT_RANGE_SHIFT;
+ return pmic->dcdc1_range;
+ case TPS65912_REG_DCDC2:
+ pmic->dcdc2_range = tps65912_reg_read(mfd,
+ TPS65912_DCDC2_LIMIT);
+ if (pmic->dcdc2_range < 0)
+ return pmic->dcdc2_range;
+ pmic->dcdc2_range = (pmic->dcdc2_range &
+ DCDC_LIMIT_RANGE_MASK) >> DCDC_LIMIT_RANGE_SHIFT;
+ return pmic->dcdc2_range;
+ case TPS65912_REG_DCDC3:
+ pmic->dcdc3_range = tps65912_reg_read(mfd,
+ TPS65912_DCDC3_LIMIT);
+ if (pmic->dcdc3_range < 0)
+ return pmic->dcdc3_range;
+ pmic->dcdc3_range = (pmic->dcdc3_range &
+ DCDC_LIMIT_RANGE_MASK) >> DCDC_LIMIT_RANGE_SHIFT;
+ return pmic->dcdc3_range;
+ case TPS65912_REG_DCDC4:
+ pmic->dcdc4_range = tps65912_reg_read(mfd,
+ TPS65912_DCDC4_LIMIT);
+ if (pmic->dcdc4_range < 0)
+ return pmic->dcdc4_range;
+ pmic->dcdc4_range = (pmic->dcdc4_range &
+ DCDC_LIMIT_RANGE_MASK) >> DCDC_LIMIT_RANGE_SHIFT;
+ return pmic->dcdc4_range;
+ default:
+ return 0;
+ }
+}
+
+static unsigned long tps65912_vsel_to_uv_range0(u8 vsel)
+{
+ unsigned long uv;
+
+ uv = ((vsel * 12500) + 500000);
+ return uv;
+}
+
+static unsigned long tps65912_vsel_to_uv_range1(u8 vsel)
+{
+ unsigned long uv;
+
+ uv = ((vsel * 12500) + 700000);
+ return uv;
+}
+
+static unsigned long tps65912_vsel_to_uv_range2(u8 vsel)
+{
+ unsigned long uv;
+
+ uv = ((vsel * 25000) + 500000);
+ return uv;
+}
+
+static unsigned long tps65912_vsel_to_uv_range3(u8 vsel)
+{
+ unsigned long uv;
+
+ if (vsel == 0x3f)
+ uv = 3800000;
+ else
+ uv = ((vsel * 50000) + 500000);
+
+ return uv;
+}
+
+static unsigned long tps65912_vsel_to_uv_ldo(u8 vsel)
+{
+ unsigned long uv = 0;
+
+ if (vsel <= 32)
+ uv = ((vsel * 25000) + 800000);
+ else if (vsel > 32 && vsel <= 60)
+ uv = (((vsel - 32) * 50000) + 1600000);
+ else if (vsel > 60)
+ uv = (((vsel - 60) * 100000) + 3000000);
+
+ return uv;
+}
+
+static int tps65912_get_ctrl_register(int id)
+{
+ switch (id) {
+ case TPS65912_REG_DCDC1:
+ return TPS65912_DCDC1_AVS;
+ case TPS65912_REG_DCDC2:
+ return TPS65912_DCDC2_AVS;
+ case TPS65912_REG_DCDC3:
+ return TPS65912_DCDC3_AVS;
+ case TPS65912_REG_DCDC4:
+ return TPS65912_DCDC4_AVS;
+ case TPS65912_REG_LDO1:
+ return TPS65912_LDO1_AVS;
+ case TPS65912_REG_LDO2:
+ return TPS65912_LDO2_AVS;
+ case TPS65912_REG_LDO3:
+ return TPS65912_LDO3_AVS;
+ case TPS65912_REG_LDO4:
+ return TPS65912_LDO4_AVS;
+ case TPS65912_REG_LDO5:
+ return TPS65912_LDO5;
+ case TPS65912_REG_LDO6:
+ return TPS65912_LDO6;
+ case TPS65912_REG_LDO7:
+ return TPS65912_LDO7;
+ case TPS65912_REG_LDO8:
+ return TPS65912_LDO8;
+ case TPS65912_REG_LDO9:
+ return TPS65912_LDO9;
+ case TPS65912_REG_LDO10:
+ return TPS65912_LDO10;
+ default:
+ return -EINVAL;
+ }
+}
+
+static int tps65912_get_dcdc_sel_register(struct tps65912_reg *pmic, int id)
+{
+ struct tps65912 *mfd = pmic->mfd;
+ int opvsel = 0, sr = 0;
+ u8 reg = 0;
+
+ if (id < TPS65912_REG_DCDC1 || id > TPS65912_REG_DCDC4)
+ return -EINVAL;
+
+ switch (id) {
+ case TPS65912_REG_DCDC1:
+ opvsel = tps65912_reg_read(mfd, TPS65912_DCDC1_OP);
+ sr = ((opvsel & OP_SELREG_MASK) >> OP_SELREG_SHIFT);
+ if (sr)
+ reg = TPS65912_DCDC1_AVS;
+ else
+ reg = TPS65912_DCDC1_OP;
+ break;
+ case TPS65912_REG_DCDC2:
+ opvsel = tps65912_reg_read(mfd, TPS65912_DCDC2_OP);
+ sr = (opvsel & OP_SELREG_MASK) >> OP_SELREG_SHIFT;
+ if (sr)
+ reg = TPS65912_DCDC2_AVS;
+ else
+ reg = TPS65912_DCDC2_OP;
+ break;
+ case TPS65912_REG_DCDC3:
+ opvsel = tps65912_reg_read(mfd, TPS65912_DCDC3_OP);
+ sr = (opvsel & OP_SELREG_MASK) >> OP_SELREG_SHIFT;
+ if (sr)
+ reg = TPS65912_DCDC3_AVS;
+ else
+ reg = TPS65912_DCDC3_OP;
+ break;
+ case TPS65912_REG_DCDC4:
+ opvsel = tps65912_reg_read(mfd, TPS65912_DCDC4_OP);
+ sr = (opvsel & OP_SELREG_MASK) >> OP_SELREG_SHIFT;
+ if (sr)
+ reg = TPS65912_DCDC4_AVS;
+ else
+ reg = TPS65912_DCDC4_OP;
+ break;
+ }
+ return reg;
+}
+
+static int tps65912_get_ldo_sel_register(struct tps65912_reg *pmic, int id)
+{
+ struct tps65912 *mfd = pmic->mfd;
+ int opvsel = 0, sr = 0;
+ u8 reg = 0;
+
+ if (id < TPS65912_REG_LDO1 || id > TPS65912_REG_LDO10)
+ return -EINVAL;
+
+ switch (id) {
+ case TPS65912_REG_LDO1:
+ opvsel = tps65912_reg_read(mfd, TPS65912_LDO1_OP);
+ sr = (opvsel & OP_SELREG_MASK) >> OP_SELREG_SHIFT;
+ if (sr)
+ reg = TPS65912_LDO1_AVS;
+ else
+ reg = TPS65912_LDO1_OP;
+ break;
+ case TPS65912_REG_LDO2:
+ opvsel = tps65912_reg_read(mfd, TPS65912_LDO2_OP);
+ sr = (opvsel & OP_SELREG_MASK) >> OP_SELREG_SHIFT;
+ if (sr)
+ reg = TPS65912_LDO2_AVS;
+ else
+ reg = TPS65912_LDO2_OP;
+ break;
+ case TPS65912_REG_LDO3:
+ opvsel = tps65912_reg_read(mfd, TPS65912_LDO3_OP);
+ sr = (opvsel & OP_SELREG_MASK) >> OP_SELREG_SHIFT;
+ if (sr)
+ reg = TPS65912_LDO3_AVS;
+ else
+ reg = TPS65912_LDO3_OP;
+ break;
+ case TPS65912_REG_LDO4:
+ opvsel = tps65912_reg_read(mfd, TPS65912_LDO4_OP);
+ sr = (opvsel & OP_SELREG_MASK) >> OP_SELREG_SHIFT;
+ if (sr)
+ reg = TPS65912_LDO4_AVS;
+ else
+ reg = TPS65912_LDO4_OP;
+ break;
+ case TPS65912_REG_LDO5:
+ reg = TPS65912_LDO5;
+ break;
+ case TPS65912_REG_LDO6:
+ reg = TPS65912_LDO6;
+ break;
+ case TPS65912_REG_LDO7:
+ reg = TPS65912_LDO7;
+ break;
+ case TPS65912_REG_LDO8:
+ reg = TPS65912_LDO8;
+ break;
+ case TPS65912_REG_LDO9:
+ reg = TPS65912_LDO9;
+ break;
+ case TPS65912_REG_LDO10:
+ reg = TPS65912_LDO10;
+ break;
+ }
+
+ return reg;
+}
+
+static int tps65912_get_mode_regiters(struct tps65912_reg *pmic, int id)
+{
+ switch (id) {
+ case TPS65912_REG_DCDC1:
+ pmic->pwm_mode_reg = TPS65912_DCDC1_CTRL;
+ pmic->eco_reg = TPS65912_DCDC1_AVS;
+ break;
+ case TPS65912_REG_DCDC2:
+ pmic->pwm_mode_reg = TPS65912_DCDC2_CTRL;
+ pmic->eco_reg = TPS65912_DCDC2_AVS;
+ break;
+ case TPS65912_REG_DCDC3:
+ pmic->pwm_mode_reg = TPS65912_DCDC3_CTRL;
+ pmic->eco_reg = TPS65912_DCDC3_AVS;
+ break;
+ case TPS65912_REG_DCDC4:
+ pmic->pwm_mode_reg = TPS65912_DCDC4_CTRL;
+ pmic->eco_reg = TPS65912_DCDC4_AVS;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int tps65912_reg_is_enabled(struct regulator_dev *dev)
+{
+ struct tps65912_reg *pmic = rdev_get_drvdata(dev);
+ struct tps65912 *mfd = pmic->mfd;
+ int reg, value, id = rdev_get_id(dev);
+
+ if (id < TPS65912_REG_DCDC1 || id > TPS65912_REG_LDO10)
+ return -EINVAL;
+
+ reg = pmic->get_ctrl_reg(id);
+ if (reg < 0)
+ return reg;
+
+ value = tps65912_reg_read(mfd, reg);
+ if (value < 0)
+ return value;
+
+ return value & TPS65912_REG_ENABLED;
+}
+
+static int tps65912_reg_enable(struct regulator_dev *dev)
+{
+ struct tps65912_reg *pmic = rdev_get_drvdata(dev);
+ struct tps65912 *mfd = pmic->mfd;
+ int id = rdev_get_id(dev);
+ int reg;
+
+ if (id < TPS65912_REG_DCDC1 || id > TPS65912_REG_LDO10)
+ return -EINVAL;
+
+ reg = pmic->get_ctrl_reg(id);
+ if (reg < 0)
+ return reg;
+
+ return tps65912_set_bits(mfd, reg, TPS65912_REG_ENABLED);
+}
+
+static int tps65912_reg_disable(struct regulator_dev *dev)
+{
+ struct tps65912_reg *pmic = rdev_get_drvdata(dev);
+ struct tps65912 *mfd = pmic->mfd;
+ int id = rdev_get_id(dev), reg;
+
+ reg = pmic->get_ctrl_reg(id);
+ if (reg < 0)
+ return reg;
+
+ return tps65912_clear_bits(mfd, reg, TPS65912_REG_ENABLED);
+}
+
+static int tps65912_set_mode(struct regulator_dev *dev, unsigned int mode)
+{
+ struct tps65912_reg *pmic = rdev_get_drvdata(dev);
+ struct tps65912 *mfd = pmic->mfd;
+ int pwm_mode, eco, id = rdev_get_id(dev);
+
+ tps65912_get_mode_regiters(pmic, id);
+
+ pwm_mode = tps65912_reg_read(mfd, pmic->pwm_mode_reg);
+ eco = tps65912_reg_read(mfd, pmic->eco_reg);
+
+ pwm_mode &= DCDCCTRL_DCDC_MODE_MASK;
+ eco &= DCDC_AVS_ECO_MASK;
+
+ switch (mode) {
+ case REGULATOR_MODE_FAST:
+ /* Verify if mode alredy set */
+ if (pwm_mode && !eco)
+ break;
+ tps65912_set_bits(mfd, pmic->pwm_mode_reg, DCDCCTRL_DCDC_MODE_MASK);
+ tps65912_clear_bits(mfd, pmic->eco_reg, DCDC_AVS_ECO_MASK);
+ break;
+ case REGULATOR_MODE_NORMAL:
+ case REGULATOR_MODE_IDLE:
+ if (!pwm_mode && !eco)
+ break;
+ tps65912_clear_bits(mfd, pmic->pwm_mode_reg, DCDCCTRL_DCDC_MODE_MASK);
+ tps65912_clear_bits(mfd, pmic->eco_reg, DCDC_AVS_ECO_MASK);
+ break;
+ case REGULATOR_MODE_STANDBY:
+ if (!pwm_mode && eco)
+ break;
+ tps65912_clear_bits(mfd, pmic->pwm_mode_reg, DCDCCTRL_DCDC_MODE_MASK);
+ tps65912_set_bits(mfd, pmic->eco_reg, DCDC_AVS_ECO_MASK);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static unsigned int tps65912_get_mode(struct regulator_dev *dev)
+{
+ struct tps65912_reg *pmic = rdev_get_drvdata(dev);
+ struct tps65912 *mfd = pmic->mfd;
+ int pwm_mode, eco, mode = 0, id = rdev_get_id(dev);
+
+ tps65912_get_mode_regiters(pmic, id);
+
+ pwm_mode = tps65912_reg_read(mfd, pmic->pwm_mode_reg);
+ eco = tps65912_reg_read(mfd, pmic->eco_reg);
+
+ pwm_mode &= DCDCCTRL_DCDC_MODE_MASK;
+ eco &= DCDC_AVS_ECO_MASK;
+
+ if (pwm_mode && !eco)
+ mode = REGULATOR_MODE_FAST;
+ else if (!pwm_mode && !eco)
+ mode = REGULATOR_MODE_NORMAL;
+ else if (!pwm_mode && eco)
+ mode = REGULATOR_MODE_STANDBY;
+
+ return mode;
+}
+
+static int tps65912_get_voltage_dcdc(struct regulator_dev *dev)
+{
+ struct tps65912_reg *pmic = rdev_get_drvdata(dev);
+ struct tps65912 *mfd = pmic->mfd;
+ int id = rdev_get_id(dev), voltage = 0, range;
+ int opvsel = 0, avsel = 0, sr, vsel;
+
+ switch (id) {
+ case TPS65912_REG_DCDC1:
+ opvsel = tps65912_reg_read(mfd, TPS65912_DCDC1_OP);
+ avsel = tps65912_reg_read(mfd, TPS65912_DCDC1_AVS);
+ range = pmic->dcdc1_range;
+ break;
+ case TPS65912_REG_DCDC2:
+ opvsel = tps65912_reg_read(mfd, TPS65912_DCDC2_OP);
+ avsel = tps65912_reg_read(mfd, TPS65912_DCDC2_AVS);
+ range = pmic->dcdc2_range;
+ break;
+ case TPS65912_REG_DCDC3:
+ opvsel = tps65912_reg_read(mfd, TPS65912_DCDC3_OP);
+ avsel = tps65912_reg_read(mfd, TPS65912_DCDC3_AVS);
+ range = pmic->dcdc3_range;
+ break;
+ case TPS65912_REG_DCDC4:
+ opvsel = tps65912_reg_read(mfd, TPS65912_DCDC4_OP);
+ avsel = tps65912_reg_read(mfd, TPS65912_DCDC4_AVS);
+ range = pmic->dcdc4_range;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ sr = (opvsel & OP_SELREG_MASK) >> OP_SELREG_SHIFT;
+ if (sr)
+ vsel = avsel;
+ else
+ vsel = opvsel;
+ vsel &= 0x3F;
+
+ switch (range) {
+ case 0:
+ /* 0.5 - 1.2875V in 12.5mV steps */
+ voltage = tps65912_vsel_to_uv_range0(vsel);
+ break;
+ case 1:
+ /* 0.7 - 1.4875V in 12.5mV steps */
+ voltage = tps65912_vsel_to_uv_range1(vsel);
+ break;
+ case 2:
+ /* 0.5 - 2.075V in 25mV steps */
+ voltage = tps65912_vsel_to_uv_range2(vsel);
+ break;
+ case 3:
+ /* 0.5 - 3.8V in 50mV steps */
+ voltage = tps65912_vsel_to_uv_range3(vsel);
+ break;
+ }
+ return voltage;
+}
+
+static int tps65912_set_voltage_dcdc(struct regulator_dev *dev,
+ unsigned selector)
+{
+ struct tps65912_reg *pmic = rdev_get_drvdata(dev);
+ struct tps65912 *mfd = pmic->mfd;
+ int id = rdev_get_id(dev);
+ int value;
+ u8 reg;
+
+ reg = tps65912_get_dcdc_sel_register(pmic, id);
+ value = tps65912_reg_read(mfd, reg);
+ value &= 0xC0;
+ return tps65912_reg_write(mfd, reg, selector | value);
+}
+
+static int tps65912_get_voltage_ldo(struct regulator_dev *dev)
+{
+ struct tps65912_reg *pmic = rdev_get_drvdata(dev);
+ struct tps65912 *mfd = pmic->mfd;
+ int id = rdev_get_id(dev);
+ int vsel = 0;
+ u8 reg;
+
+ reg = tps65912_get_ldo_sel_register(pmic, id);
+ vsel = tps65912_reg_read(mfd, reg);
+ vsel &= 0x3F;
+
+ return tps65912_vsel_to_uv_ldo(vsel);
+}
+
+static int tps65912_set_voltage_ldo(struct regulator_dev *dev,
+ unsigned selector)
+{
+ struct tps65912_reg *pmic = rdev_get_drvdata(dev);
+ struct tps65912 *mfd = pmic->mfd;
+ int id = rdev_get_id(dev), reg, value;
+
+ reg = tps65912_get_ldo_sel_register(pmic, id);
+ value = tps65912_reg_read(mfd, reg);
+ value &= 0xC0;
+ return tps65912_reg_write(mfd, reg, selector | value);
+}
+
+static int tps65912_list_voltage_dcdc(struct regulator_dev *dev,
+ unsigned selector)
+{
+ struct tps65912_reg *pmic = rdev_get_drvdata(dev);
+ int range, voltage = 0, id = rdev_get_id(dev);
+
+ switch (id) {
+ case TPS65912_REG_DCDC1:
+ range = pmic->dcdc1_range;
+ break;
+ case TPS65912_REG_DCDC2:
+ range = pmic->dcdc2_range;
+ break;
+ case TPS65912_REG_DCDC3:
+ range = pmic->dcdc3_range;
+ break;
+ case TPS65912_REG_DCDC4:
+ range = pmic->dcdc4_range;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (range) {
+ case 0:
+ /* 0.5 - 1.2875V in 12.5mV steps */
+ voltage = tps65912_vsel_to_uv_range0(selector);
+ break;
+ case 1:
+ /* 0.7 - 1.4875V in 12.5mV steps */
+ voltage = tps65912_vsel_to_uv_range1(selector);
+ break;
+ case 2:
+ /* 0.5 - 2.075V in 25mV steps */
+ voltage = tps65912_vsel_to_uv_range2(selector);
+ break;
+ case 3:
+ /* 0.5 - 3.8V in 50mV steps */
+ voltage = tps65912_vsel_to_uv_range3(selector);
+ break;
+ }
+ return voltage;
+}
+
+static int tps65912_list_voltage_ldo(struct regulator_dev *dev,
+ unsigned selector)
+{
+ int ldo = rdev_get_id(dev);
+
+ if (ldo < TPS65912_REG_LDO1 || ldo > TPS65912_REG_LDO10)
+ return -EINVAL;
+
+ return tps65912_vsel_to_uv_ldo(selector);
+}
+
+/* Operations permitted on DCDCx */
+static struct regulator_ops tps65912_ops_dcdc = {
+ .is_enabled = tps65912_reg_is_enabled,
+ .enable = tps65912_reg_enable,
+ .disable = tps65912_reg_disable,
+ .set_mode = tps65912_set_mode,
+ .get_mode = tps65912_get_mode,
+ .get_voltage = tps65912_get_voltage_dcdc,
+ .set_voltage_sel = tps65912_set_voltage_dcdc,
+ .list_voltage = tps65912_list_voltage_dcdc,
+};
+
+/* Operations permitted on LDOx */
+static struct regulator_ops tps65912_ops_ldo = {
+ .is_enabled = tps65912_reg_is_enabled,
+ .enable = tps65912_reg_enable,
+ .disable = tps65912_reg_disable,
+ .get_voltage = tps65912_get_voltage_ldo,
+ .set_voltage_sel = tps65912_set_voltage_ldo,
+ .list_voltage = tps65912_list_voltage_ldo,
+};
+
+static __devinit int tps65912_probe(struct platform_device *pdev)
+{
+ struct tps65912 *tps65912 = dev_get_drvdata(pdev->dev.parent);
+ struct tps_info *info;
+ struct regulator_init_data *reg_data;
+ struct regulator_dev *rdev;
+ struct tps65912_reg *pmic;
+ struct tps65912_board *pmic_plat_data;
+ int i, err;
+
+ pmic_plat_data = dev_get_platdata(tps65912->dev);
+ if (!pmic_plat_data)
+ return -EINVAL;
+
+ reg_data = pmic_plat_data->tps65912_pmic_init_data;
+
+ pmic = kzalloc(sizeof(*pmic), GFP_KERNEL);
+ if (!pmic)
+ return -ENOMEM;
+
+ mutex_init(&pmic->io_lock);
+ pmic->mfd = tps65912;
+ platform_set_drvdata(pdev, pmic);
+
+ pmic->get_ctrl_reg = &tps65912_get_ctrl_register;
+ info = tps65912_regs;
+
+ for (i = 0; i < TPS65912_NUM_REGULATOR; i++, info++, reg_data++) {
+ int range = 0;
+ /* Register the regulators */
+ pmic->info[i] = info;
+
+ pmic->desc[i].name = info->name;
+ pmic->desc[i].id = i;
+ pmic->desc[i].n_voltages = 64;
+ pmic->desc[i].ops = (i > TPS65912_REG_DCDC4 ?
+ &tps65912_ops_ldo : &tps65912_ops_dcdc);
+ pmic->desc[i].type = REGULATOR_VOLTAGE;
+ pmic->desc[i].owner = THIS_MODULE;
+ range = tps65912_get_range(pmic, i);
+ rdev = regulator_register(&pmic->desc[i],
+ tps65912->dev, reg_data, pmic);
+ if (IS_ERR(rdev)) {
+ dev_err(tps65912->dev,
+ "failed to register %s regulator\n",
+ pdev->name);
+ err = PTR_ERR(rdev);
+ goto err;
+ }
+
+ /* Save regulator for cleanup */
+ pmic->rdev[i] = rdev;
+ }
+ return 0;
+
+err:
+ while (--i >= 0)
+ regulator_unregister(pmic->rdev[i]);
+
+ kfree(pmic);
+ return err;
+}
+
+static int __devexit tps65912_remove(struct platform_device *pdev)
+{
+ struct tps65912_reg *tps65912_reg = platform_get_drvdata(pdev);
+ int i;
+
+ for (i = 0; i < TPS65912_NUM_REGULATOR; i++)
+ regulator_unregister(tps65912_reg->rdev[i]);
+
+ kfree(tps65912_reg);
+ return 0;
+}
+
+static struct platform_driver tps65912_driver = {
+ .driver = {
+ .name = "tps65912-pmic",
+ .owner = THIS_MODULE,
+ },
+ .probe = tps65912_probe,
+ .remove = __devexit_p(tps65912_remove),
+};
+
+/**
+ * tps65912_init
+ *
+ * Module init function
+ */
+static int __init tps65912_init(void)
+{
+ return platform_driver_register(&tps65912_driver);
+}
+subsys_initcall(tps65912_init);
+
+/**
+ * tps65912_cleanup
+ *
+ * Module exit function
+ */
+static void __exit tps65912_cleanup(void)
+{
+ platform_driver_unregister(&tps65912_driver);
+}
+module_exit(tps65912_cleanup);
+
+MODULE_AUTHOR("Margarita Olaya Cabrera <magi@slimlogic.co.uk>");
+MODULE_DESCRIPTION("TPS65912 voltage regulator driver");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:tps65912-pmic");
diff --git a/drivers/regulator/twl-regulator.c b/drivers/regulator/twl-regulator.c
index 87fe0f7..ee8747f 100644
--- a/drivers/regulator/twl-regulator.c
+++ b/drivers/regulator/twl-regulator.c
@@ -835,8 +835,8 @@
remap_conf) \
TWL_FIXED_LDO(label, offset, mVolts, num, turnon_delay, \
remap_conf, TWL4030, twl4030fixed_ops)
-#define TWL6030_FIXED_LDO(label, offset, mVolts, num, turnon_delay) \
- TWL_FIXED_LDO(label, offset, mVolts, num, turnon_delay, \
+#define TWL6030_FIXED_LDO(label, offset, mVolts, turnon_delay) \
+ TWL_FIXED_LDO(label, offset, mVolts, 0x0, turnon_delay, \
0x0, TWL6030, twl6030fixed_ops)
#define TWL4030_ADJUSTABLE_LDO(label, offset, num, turnon_delay, remap_conf) { \
@@ -856,24 +856,22 @@
}, \
}
-#define TWL6030_ADJUSTABLE_LDO(label, offset, min_mVolts, max_mVolts, num) { \
+#define TWL6030_ADJUSTABLE_LDO(label, offset, min_mVolts, max_mVolts) { \
.base = offset, \
- .id = num, \
.min_mV = min_mVolts, \
.max_mV = max_mVolts, \
.desc = { \
.name = #label, \
.id = TWL6030_REG_##label, \
- .n_voltages = (max_mVolts - min_mVolts)/100, \
+ .n_voltages = (max_mVolts - min_mVolts)/100 + 1, \
.ops = &twl6030ldo_ops, \
.type = REGULATOR_VOLTAGE, \
.owner = THIS_MODULE, \
}, \
}
-#define TWL6025_ADJUSTABLE_LDO(label, offset, min_mVolts, max_mVolts, num) { \
+#define TWL6025_ADJUSTABLE_LDO(label, offset, min_mVolts, max_mVolts) { \
.base = offset, \
- .id = num, \
.min_mV = min_mVolts, \
.max_mV = max_mVolts, \
.desc = { \
@@ -903,9 +901,8 @@
}, \
}
-#define TWL6030_FIXED_RESOURCE(label, offset, num, turnon_delay) { \
+#define TWL6030_FIXED_RESOURCE(label, offset, turnon_delay) { \
.base = offset, \
- .id = num, \
.delay = turnon_delay, \
.desc = { \
.name = #label, \
@@ -916,9 +913,8 @@
}, \
}
-#define TWL6025_ADJUSTABLE_SMPS(label, offset, num) { \
+#define TWL6025_ADJUSTABLE_SMPS(label, offset) { \
.base = offset, \
- .id = num, \
.min_mV = 600, \
.max_mV = 2100, \
.desc = { \
@@ -961,32 +957,32 @@
/* 6030 REG with base as PMC Slave Misc : 0x0030 */
/* Turnon-delay and remap configuration values for 6030 are not
verified since the specification is not public */
- TWL6030_ADJUSTABLE_LDO(VAUX1_6030, 0x54, 1000, 3300, 1),
- TWL6030_ADJUSTABLE_LDO(VAUX2_6030, 0x58, 1000, 3300, 2),
- TWL6030_ADJUSTABLE_LDO(VAUX3_6030, 0x5c, 1000, 3300, 3),
- TWL6030_ADJUSTABLE_LDO(VMMC, 0x68, 1000, 3300, 4),
- TWL6030_ADJUSTABLE_LDO(VPP, 0x6c, 1000, 3300, 5),
- TWL6030_ADJUSTABLE_LDO(VUSIM, 0x74, 1000, 3300, 7),
- TWL6030_FIXED_LDO(VANA, 0x50, 2100, 15, 0),
- TWL6030_FIXED_LDO(VCXIO, 0x60, 1800, 16, 0),
- TWL6030_FIXED_LDO(VDAC, 0x64, 1800, 17, 0),
- TWL6030_FIXED_LDO(VUSB, 0x70, 3300, 18, 0),
- TWL6030_FIXED_RESOURCE(CLK32KG, 0x8C, 48, 0),
+ TWL6030_ADJUSTABLE_LDO(VAUX1_6030, 0x54, 1000, 3300),
+ TWL6030_ADJUSTABLE_LDO(VAUX2_6030, 0x58, 1000, 3300),
+ TWL6030_ADJUSTABLE_LDO(VAUX3_6030, 0x5c, 1000, 3300),
+ TWL6030_ADJUSTABLE_LDO(VMMC, 0x68, 1000, 3300),
+ TWL6030_ADJUSTABLE_LDO(VPP, 0x6c, 1000, 3300),
+ TWL6030_ADJUSTABLE_LDO(VUSIM, 0x74, 1000, 3300),
+ TWL6030_FIXED_LDO(VANA, 0x50, 2100, 0),
+ TWL6030_FIXED_LDO(VCXIO, 0x60, 1800, 0),
+ TWL6030_FIXED_LDO(VDAC, 0x64, 1800, 0),
+ TWL6030_FIXED_LDO(VUSB, 0x70, 3300, 0),
+ TWL6030_FIXED_RESOURCE(CLK32KG, 0x8C, 0),
/* 6025 are renamed compared to 6030 versions */
- TWL6025_ADJUSTABLE_LDO(LDO2, 0x54, 1000, 3300, 1),
- TWL6025_ADJUSTABLE_LDO(LDO4, 0x58, 1000, 3300, 2),
- TWL6025_ADJUSTABLE_LDO(LDO3, 0x5c, 1000, 3300, 3),
- TWL6025_ADJUSTABLE_LDO(LDO5, 0x68, 1000, 3300, 4),
- TWL6025_ADJUSTABLE_LDO(LDO1, 0x6c, 1000, 3300, 5),
- TWL6025_ADJUSTABLE_LDO(LDO7, 0x74, 1000, 3300, 7),
- TWL6025_ADJUSTABLE_LDO(LDO6, 0x60, 1000, 3300, 16),
- TWL6025_ADJUSTABLE_LDO(LDOLN, 0x64, 1000, 3300, 17),
- TWL6025_ADJUSTABLE_LDO(LDOUSB, 0x70, 1000, 3300, 18),
+ TWL6025_ADJUSTABLE_LDO(LDO2, 0x54, 1000, 3300),
+ TWL6025_ADJUSTABLE_LDO(LDO4, 0x58, 1000, 3300),
+ TWL6025_ADJUSTABLE_LDO(LDO3, 0x5c, 1000, 3300),
+ TWL6025_ADJUSTABLE_LDO(LDO5, 0x68, 1000, 3300),
+ TWL6025_ADJUSTABLE_LDO(LDO1, 0x6c, 1000, 3300),
+ TWL6025_ADJUSTABLE_LDO(LDO7, 0x74, 1000, 3300),
+ TWL6025_ADJUSTABLE_LDO(LDO6, 0x60, 1000, 3300),
+ TWL6025_ADJUSTABLE_LDO(LDOLN, 0x64, 1000, 3300),
+ TWL6025_ADJUSTABLE_LDO(LDOUSB, 0x70, 1000, 3300),
- TWL6025_ADJUSTABLE_SMPS(SMPS3, 0x34, 1),
- TWL6025_ADJUSTABLE_SMPS(SMPS4, 0x10, 2),
- TWL6025_ADJUSTABLE_SMPS(VIO, 0x16, 3),
+ TWL6025_ADJUSTABLE_SMPS(SMPS3, 0x34),
+ TWL6025_ADJUSTABLE_SMPS(SMPS4, 0x10),
+ TWL6025_ADJUSTABLE_SMPS(VIO, 0x16),
};
static u8 twl_get_smps_offset(void)
diff --git a/drivers/regulator/wm831x-dcdc.c b/drivers/regulator/wm831x-dcdc.c
index a0982e8..bd3531d 100644
--- a/drivers/regulator/wm831x-dcdc.c
+++ b/drivers/regulator/wm831x-dcdc.c
@@ -267,23 +267,6 @@
return vsel;
}
-static int wm831x_buckv_select_max_voltage(struct regulator_dev *rdev,
- int min_uV, int max_uV)
-{
- u16 vsel;
-
- if (max_uV < 600000 || max_uV > 1800000)
- return -EINVAL;
-
- vsel = ((max_uV - 600000) / 12500) + 8;
-
- if (wm831x_buckv_list_voltage(rdev, vsel) < min_uV ||
- wm831x_buckv_list_voltage(rdev, vsel) < max_uV)
- return -EINVAL;
-
- return vsel;
-}
-
static int wm831x_buckv_set_dvs(struct regulator_dev *rdev, int state)
{
struct wm831x_dcdc *dcdc = rdev_get_drvdata(rdev);
@@ -338,28 +321,23 @@
if (ret < 0)
return ret;
- /* Set the high voltage as the DVS voltage. This is optimised
- * for CPUfreq usage, most processors will keep the maximum
- * voltage constant and lower the minimum with the frequency. */
- vsel = wm831x_buckv_select_max_voltage(rdev, min_uV, max_uV);
- if (vsel < 0) {
- /* This should never happen - at worst the same vsel
- * should be chosen */
- WARN_ON(vsel < 0);
- return 0;
+ /*
+ * If this VSEL is higher than the last one we've seen then
+ * remember it as the DVS VSEL. This is optimised for CPUfreq
+ * usage where we want to get to the highest voltage very
+ * quickly.
+ */
+ if (vsel > dcdc->dvs_vsel) {
+ ret = wm831x_set_bits(wm831x, dvs_reg,
+ WM831X_DC1_DVS_VSEL_MASK,
+ dcdc->dvs_vsel);
+ if (ret == 0)
+ dcdc->dvs_vsel = vsel;
+ else
+ dev_warn(wm831x->dev,
+ "Failed to set DCDC DVS VSEL: %d\n", ret);
}
- /* Don't bother if it's the same VSEL we're already using */
- if (vsel == dcdc->on_vsel)
- return 0;
-
- ret = wm831x_set_bits(wm831x, dvs_reg, WM831X_DC1_DVS_VSEL_MASK, vsel);
- if (ret == 0)
- dcdc->dvs_vsel = vsel;
- else
- dev_warn(wm831x->dev, "Failed to set DCDC DVS VSEL: %d\n",
- ret);
-
return 0;
}
@@ -456,27 +434,6 @@
if (!pdata || !pdata->dvs_gpio)
return;
- switch (pdata->dvs_control_src) {
- case 1:
- ctrl = 2 << WM831X_DC1_DVS_SRC_SHIFT;
- break;
- case 2:
- ctrl = 3 << WM831X_DC1_DVS_SRC_SHIFT;
- break;
- default:
- dev_err(wm831x->dev, "Invalid DVS control source %d for %s\n",
- pdata->dvs_control_src, dcdc->name);
- return;
- }
-
- ret = wm831x_set_bits(wm831x, dcdc->base + WM831X_DCDC_DVS_CONTROL,
- WM831X_DC1_DVS_SRC_MASK, ctrl);
- if (ret < 0) {
- dev_err(wm831x->dev, "Failed to set %s DVS source: %d\n",
- dcdc->name, ret);
- return;
- }
-
ret = gpio_request(pdata->dvs_gpio, "DCDC DVS");
if (ret < 0) {
dev_err(wm831x->dev, "Failed to get %s DVS GPIO: %d\n",
@@ -498,17 +455,57 @@
}
dcdc->dvs_gpio = pdata->dvs_gpio;
+
+ switch (pdata->dvs_control_src) {
+ case 1:
+ ctrl = 2 << WM831X_DC1_DVS_SRC_SHIFT;
+ break;
+ case 2:
+ ctrl = 3 << WM831X_DC1_DVS_SRC_SHIFT;
+ break;
+ default:
+ dev_err(wm831x->dev, "Invalid DVS control source %d for %s\n",
+ pdata->dvs_control_src, dcdc->name);
+ return;
+ }
+
+ /* If DVS_VSEL is set to the minimum value then raise it to ON_VSEL
+ * to make bootstrapping a bit smoother.
+ */
+ if (!dcdc->dvs_vsel) {
+ ret = wm831x_set_bits(wm831x,
+ dcdc->base + WM831X_DCDC_DVS_CONTROL,
+ WM831X_DC1_DVS_VSEL_MASK, dcdc->on_vsel);
+ if (ret == 0)
+ dcdc->dvs_vsel = dcdc->on_vsel;
+ else
+ dev_warn(wm831x->dev, "Failed to set DVS_VSEL: %d\n",
+ ret);
+ }
+
+ ret = wm831x_set_bits(wm831x, dcdc->base + WM831X_DCDC_DVS_CONTROL,
+ WM831X_DC1_DVS_SRC_MASK, ctrl);
+ if (ret < 0) {
+ dev_err(wm831x->dev, "Failed to set %s DVS source: %d\n",
+ dcdc->name, ret);
+ }
}
static __devinit int wm831x_buckv_probe(struct platform_device *pdev)
{
struct wm831x *wm831x = dev_get_drvdata(pdev->dev.parent);
struct wm831x_pdata *pdata = wm831x->dev->platform_data;
- int id = pdev->id % ARRAY_SIZE(pdata->dcdc);
+ int id;
struct wm831x_dcdc *dcdc;
struct resource *res;
int ret, irq;
+ if (pdata && pdata->wm831x_num)
+ id = (pdata->wm831x_num * 10) + 1;
+ else
+ id = 0;
+ id = pdev->id - id;
+
dev_dbg(&pdev->dev, "Probing DCDC%d\n", id + 1);
if (pdata == NULL || pdata->dcdc[id] == NULL)
@@ -545,7 +542,7 @@
}
dcdc->on_vsel = ret & WM831X_DC1_ON_VSEL_MASK;
- ret = wm831x_reg_read(wm831x, dcdc->base + WM831X_DCDC_ON_CONFIG);
+ ret = wm831x_reg_read(wm831x, dcdc->base + WM831X_DCDC_DVS_CONTROL);
if (ret < 0) {
dev_err(wm831x->dev, "Failed to read DVS VSEL: %d\n", ret);
goto err;
@@ -709,11 +706,17 @@
{
struct wm831x *wm831x = dev_get_drvdata(pdev->dev.parent);
struct wm831x_pdata *pdata = wm831x->dev->platform_data;
- int id = pdev->id % ARRAY_SIZE(pdata->dcdc);
+ int id;
struct wm831x_dcdc *dcdc;
struct resource *res;
int ret, irq;
+ if (pdata && pdata->wm831x_num)
+ id = (pdata->wm831x_num * 10) + 1;
+ else
+ id = 0;
+ id = pdev->id - id;
+
dev_dbg(&pdev->dev, "Probing DCDC%d\n", id + 1);
if (pdata == NULL || pdata->dcdc[id] == NULL)
@@ -1046,3 +1049,4 @@
MODULE_LICENSE("GPL");
MODULE_ALIAS("platform:wm831x-buckv");
MODULE_ALIAS("platform:wm831x-buckp");
+MODULE_ALIAS("platform:wm831x-epe");
diff --git a/drivers/regulator/wm831x-ldo.c b/drivers/regulator/wm831x-ldo.c
index 2220cf8..6709710 100644
--- a/drivers/regulator/wm831x-ldo.c
+++ b/drivers/regulator/wm831x-ldo.c
@@ -310,11 +310,17 @@
{
struct wm831x *wm831x = dev_get_drvdata(pdev->dev.parent);
struct wm831x_pdata *pdata = wm831x->dev->platform_data;
- int id = pdev->id % ARRAY_SIZE(pdata->ldo);
+ int id;
struct wm831x_ldo *ldo;
struct resource *res;
int ret, irq;
+ if (pdata && pdata->wm831x_num)
+ id = (pdata->wm831x_num * 10) + 1;
+ else
+ id = 0;
+ id = pdev->id - id;
+
dev_dbg(&pdev->dev, "Probing LDO%d\n", id + 1);
if (pdata == NULL || pdata->ldo[id] == NULL)
@@ -574,11 +580,17 @@
{
struct wm831x *wm831x = dev_get_drvdata(pdev->dev.parent);
struct wm831x_pdata *pdata = wm831x->dev->platform_data;
- int id = pdev->id % ARRAY_SIZE(pdata->ldo);
+ int id;
struct wm831x_ldo *ldo;
struct resource *res;
int ret, irq;
+ if (pdata && pdata->wm831x_num)
+ id = (pdata->wm831x_num * 10) + 1;
+ else
+ id = 0;
+ id = pdev->id - id;
+
dev_dbg(&pdev->dev, "Probing LDO%d\n", id + 1);
if (pdata == NULL || pdata->ldo[id] == NULL)
@@ -764,11 +776,18 @@
{
struct wm831x *wm831x = dev_get_drvdata(pdev->dev.parent);
struct wm831x_pdata *pdata = wm831x->dev->platform_data;
- int id = pdev->id % ARRAY_SIZE(pdata->ldo);
+ int id;
struct wm831x_ldo *ldo;
struct resource *res;
int ret;
+ if (pdata && pdata->wm831x_num)
+ id = (pdata->wm831x_num * 10) + 1;
+ else
+ id = 0;
+ id = pdev->id - id;
+
+
dev_dbg(&pdev->dev, "Probing LDO%d\n", id + 1);
if (pdata == NULL || pdata->ldo[id] == NULL)
diff --git a/drivers/regulator/wm8994-regulator.c b/drivers/regulator/wm8994-regulator.c
index 35b2958..1a6a690 100644
--- a/drivers/regulator/wm8994-regulator.c
+++ b/drivers/regulator/wm8994-regulator.c
@@ -43,7 +43,7 @@
if (!ldo->enable)
return 0;
- gpio_set_value(ldo->enable, 1);
+ gpio_set_value_cansleep(ldo->enable, 1);
ldo->is_enabled = true;
return 0;
@@ -57,7 +57,7 @@
if (!ldo->enable)
return -EINVAL;
- gpio_set_value(ldo->enable, 0);
+ gpio_set_value_cansleep(ldo->enable, 0);
ldo->is_enabled = false;
return 0;
diff --git a/drivers/rtc/interface.c b/drivers/rtc/interface.c
index 3195dbd..44e91e5 100644
--- a/drivers/rtc/interface.c
+++ b/drivers/rtc/interface.c
@@ -639,7 +639,7 @@
static int rtc_update_hrtimer(struct rtc_device *rtc, int enabled)
{
/*
- * We unconditionally cancel the timer here, because otherwise
+ * We always cancel the timer here first, because otherwise
* we could run into BUG_ON(timer->state != HRTIMER_STATE_CALLBACK);
* when we manage to start the timer before the callback
* returns HRTIMER_RESTART.
@@ -708,7 +708,7 @@
int err = 0;
unsigned long flags;
- if (freq <= 0 || freq > 5000)
+ if (freq <= 0 || freq > RTC_MAX_FREQ)
return -EINVAL;
retry:
spin_lock_irqsave(&rtc->irq_task_lock, flags);
diff --git a/drivers/rtc/rtc-omap.c b/drivers/rtc/rtc-omap.c
index bcae8dd..7789002 100644
--- a/drivers/rtc/rtc-omap.c
+++ b/drivers/rtc/rtc-omap.c
@@ -368,7 +368,7 @@
pr_info("%s: already running\n", pdev->name);
/* force to 24 hour mode */
- new_ctrl = reg & ~(OMAP_RTC_CTRL_SPLIT|OMAP_RTC_CTRL_AUTO_COMP);
+ new_ctrl = reg & (OMAP_RTC_CTRL_SPLIT|OMAP_RTC_CTRL_AUTO_COMP);
new_ctrl |= OMAP_RTC_CTRL_STOP;
/* BOARD-SPECIFIC CUSTOMIZATION CAN GO HERE:
diff --git a/drivers/s390/block/dasd.c b/drivers/s390/block/dasd.c
index 432444a..a1d3ddb 100644
--- a/drivers/s390/block/dasd.c
+++ b/drivers/s390/block/dasd.c
@@ -24,6 +24,7 @@
#include <linux/mutex.h>
#include <linux/debugfs.h>
#include <linux/seq_file.h>
+#include <linux/vmalloc.h>
#include <asm/ccwdev.h>
#include <asm/ebcdic.h>
@@ -888,11 +889,11 @@
{
char *buffer;
- buffer = kmalloc(user_len + 1, GFP_KERNEL);
+ buffer = vmalloc(user_len + 1);
if (buffer == NULL)
return ERR_PTR(-ENOMEM);
if (copy_from_user(buffer, user_buf, user_len) != 0) {
- kfree(buffer);
+ vfree(buffer);
return ERR_PTR(-EFAULT);
}
/* got the string, now strip linefeed. */
@@ -930,7 +931,7 @@
dasd_profile_off(prof);
} else
rc = -EINVAL;
- kfree(buffer);
+ vfree(buffer);
return rc;
}
@@ -1042,7 +1043,7 @@
dasd_global_profile_level = DASD_PROFILE_OFF;
} else
rc = -EINVAL;
- kfree(buffer);
+ vfree(buffer);
return rc;
}
diff --git a/drivers/s390/block/dasd_eckd.c b/drivers/s390/block/dasd_eckd.c
index 30fb979..6e835c9 100644
--- a/drivers/s390/block/dasd_eckd.c
+++ b/drivers/s390/block/dasd_eckd.c
@@ -1461,6 +1461,15 @@
"Read device characteristic failed, rc=%d", rc);
goto out_err3;
}
+
+ if ((device->features & DASD_FEATURE_USERAW) &&
+ !(private->rdc_data.facilities.RT_in_LR)) {
+ dev_err(&device->cdev->dev, "The storage server does not "
+ "support raw-track access\n");
+ rc = -EINVAL;
+ goto out_err3;
+ }
+
/* find the valid cylinder size */
if (private->rdc_data.no_cyl == LV_COMPAT_CYL &&
private->rdc_data.long_no_cyl)
diff --git a/drivers/s390/block/dasd_proc.c b/drivers/s390/block/dasd_proc.c
index 6c3c536..e12989f 100644
--- a/drivers/s390/block/dasd_proc.c
+++ b/drivers/s390/block/dasd_proc.c
@@ -312,14 +312,14 @@
pr_info("The statistics have been reset\n");
} else
goto out_parse_error;
- kfree(buffer);
+ vfree(buffer);
return user_len;
out_parse_error:
rc = -EINVAL;
pr_warning("%s is not a supported value for /proc/dasd/statistics\n",
str);
out_error:
- kfree(buffer);
+ vfree(buffer);
return rc;
#else
pr_warning("/proc/dasd/statistics: is not activated in this kernel\n");
diff --git a/drivers/s390/char/sclp_async.c b/drivers/s390/char/sclp_async.c
index 7ad30e7..5f9f929 100644
--- a/drivers/s390/char/sclp_async.c
+++ b/drivers/s390/char/sclp_async.c
@@ -82,12 +82,9 @@
return -EFAULT;
} else {
len = *count;
- rc = copy_from_user(buf, buffer, sizeof(buf));
- if (rc != 0)
- return -EFAULT;
- buf[sizeof(buf) - 1] = '\0';
- if (strict_strtoul(buf, 0, &val) != 0)
- return -EINVAL;
+ rc = kstrtoul_from_user(buffer, len, 0, &val);
+ if (rc)
+ return rc;
if (val != 0 && val != 1)
return -EINVAL;
callhome_enabled = val;
diff --git a/drivers/s390/cio/qdio.h b/drivers/s390/cio/qdio.h
index 7bc643f..e5c9664 100644
--- a/drivers/s390/cio/qdio.h
+++ b/drivers/s390/cio/qdio.h
@@ -14,6 +14,8 @@
#include "chsc.h"
#define QDIO_BUSY_BIT_PATIENCE (100 << 12) /* 100 microseconds */
+#define QDIO_BUSY_BIT_RETRY_DELAY 10 /* 10 milliseconds */
+#define QDIO_BUSY_BIT_RETRIES 1000 /* = 10s retry time */
#define QDIO_INPUT_THRESHOLD (500 << 12) /* 500 microseconds */
/*
diff --git a/drivers/s390/cio/qdio_debug.c b/drivers/s390/cio/qdio_debug.c
index f8b03a6..0e615cb 100644
--- a/drivers/s390/cio/qdio_debug.c
+++ b/drivers/s390/cio/qdio_debug.c
@@ -188,19 +188,13 @@
struct qdio_irq *irq_ptr = seq->private;
struct qdio_q *q;
unsigned long val;
- char buf[8];
int ret, i;
if (!irq_ptr)
return 0;
- if (count >= sizeof(buf))
- return -EINVAL;
- if (copy_from_user(&buf, ubuf, count))
- return -EFAULT;
- buf[count] = 0;
- ret = strict_strtoul(buf, 10, &val);
- if (ret < 0)
+ ret = kstrtoul_from_user(ubuf, count, 10, &val);
+ if (ret)
return ret;
switch (val) {
diff --git a/drivers/s390/cio/qdio_main.c b/drivers/s390/cio/qdio_main.c
index e58169c..288c914 100644
--- a/drivers/s390/cio/qdio_main.c
+++ b/drivers/s390/cio/qdio_main.c
@@ -313,7 +313,7 @@
unsigned long schid = *((u32 *) &q->irq_ptr->schid);
unsigned int fc = QDIO_SIGA_WRITE;
u64 start_time = 0;
- int cc;
+ int retries = 0, cc;
if (is_qebsm(q)) {
schid = q->irq_ptr->sch_token;
@@ -325,6 +325,7 @@
/* hipersocket busy condition */
if (unlikely(*busy_bit)) {
WARN_ON(queue_type(q) != QDIO_IQDIO_QFMT || cc != 2);
+ retries++;
if (!start_time) {
start_time = get_clock();
@@ -333,6 +334,11 @@
if ((get_clock() - start_time) < QDIO_BUSY_BIT_PATIENCE)
goto again;
}
+ if (retries) {
+ DBF_DEV_EVENT(DBF_WARN, q->irq_ptr,
+ "%4x cc2 BB1:%1d", SCH_NO(q), q->nr);
+ DBF_DEV_EVENT(DBF_WARN, q->irq_ptr, "count:%u", retries);
+ }
return cc;
}
@@ -728,13 +734,14 @@
static int qdio_kick_outbound_q(struct qdio_q *q)
{
+ int retries = 0, cc;
unsigned int busy_bit;
- int cc;
if (!need_siga_out(q))
return 0;
DBF_DEV_EVENT(DBF_INFO, q->irq_ptr, "siga-w:%1d", q->nr);
+retry:
qperf_inc(q, siga_write);
cc = qdio_siga_output(q, &busy_bit);
@@ -743,7 +750,11 @@
break;
case 2:
if (busy_bit) {
- DBF_ERROR("%4x cc2 REP:%1d", SCH_NO(q), q->nr);
+ while (++retries < QDIO_BUSY_BIT_RETRIES) {
+ mdelay(QDIO_BUSY_BIT_RETRY_DELAY);
+ goto retry;
+ }
+ DBF_ERROR("%4x cc2 BBC:%1d", SCH_NO(q), q->nr);
cc |= QDIO_ERROR_SIGA_BUSY;
} else
DBF_DEV_EVENT(DBF_INFO, q->irq_ptr, "siga-w cc2:%1d", q->nr);
@@ -753,6 +764,10 @@
DBF_ERROR("%4x SIGA-W:%1d", SCH_NO(q), cc);
break;
}
+ if (retries) {
+ DBF_ERROR("%4x cc2 BB2:%1d", SCH_NO(q), q->nr);
+ DBF_ERROR("count:%u", retries);
+ }
return cc;
}
diff --git a/drivers/sh/clk/core.c b/drivers/sh/clk/core.c
index d6702e5..dc8d022 100644
--- a/drivers/sh/clk/core.c
+++ b/drivers/sh/clk/core.c
@@ -34,6 +34,9 @@
static DEFINE_SPINLOCK(clock_lock);
static DEFINE_MUTEX(clock_list_sem);
+/* clock disable operations are not passed on to hardware during boot */
+static int allow_disable;
+
void clk_rate_table_build(struct clk *clk,
struct cpufreq_frequency_table *freq_table,
int nr_freqs,
@@ -228,7 +231,7 @@
return;
if (!(--clk->usecount)) {
- if (likely(clk->ops && clk->ops->disable))
+ if (likely(allow_disable && clk->ops && clk->ops->disable))
clk->ops->disable(clk);
if (likely(clk->parent))
__clk_disable(clk->parent);
@@ -393,7 +396,7 @@
{
int ret;
- if (clk == NULL || IS_ERR(clk))
+ if (IS_ERR_OR_NULL(clk))
return -EINVAL;
/*
@@ -744,3 +747,25 @@
return err;
}
late_initcall(clk_debugfs_init);
+
+static int __init clk_late_init(void)
+{
+ unsigned long flags;
+ struct clk *clk;
+
+ /* disable all clocks with zero use count */
+ mutex_lock(&clock_list_sem);
+ spin_lock_irqsave(&clock_lock, flags);
+
+ list_for_each_entry(clk, &clock_list, node)
+ if (!clk->usecount && clk->ops && clk->ops->disable)
+ clk->ops->disable(clk);
+
+ /* from now on allow clock disable operations */
+ allow_disable = 1;
+
+ spin_unlock_irqrestore(&clock_lock, flags);
+ mutex_unlock(&clock_list_sem);
+ return 0;
+}
+late_initcall(clk_late_init);
diff --git a/drivers/spi/spi-pl022.c b/drivers/spi/spi-pl022.c
index eba88c7..730b4a3 100644
--- a/drivers/spi/spi-pl022.c
+++ b/drivers/spi/spi-pl022.c
@@ -2267,17 +2267,13 @@
pl022_remove(struct amba_device *adev)
{
struct pl022 *pl022 = amba_get_drvdata(adev);
- int status = 0;
+
if (!pl022)
return 0;
/* Remove the queue */
- status = destroy_queue(pl022);
- if (status != 0) {
- dev_err(&adev->dev,
- "queue remove failed (%d)\n", status);
- return status;
- }
+ if (destroy_queue(pl022) != 0)
+ dev_err(&adev->dev, "queue remove failed\n");
load_ssp_default_config(pl022);
pl022_dma_remove(pl022);
free_irq(adev->irq[0], pl022);
@@ -2289,7 +2285,6 @@
spi_unregister_master(pl022->master);
spi_master_put(pl022->master);
amba_set_drvdata(adev, NULL);
- dev_dbg(&adev->dev, "remove succeeded\n");
return 0;
}
diff --git a/drivers/staging/ath6kl/miscdrv/ar3kps/ar3kpsparser.c b/drivers/staging/ath6kl/miscdrv/ar3kps/ar3kpsparser.c
index c01c0cb..b99a11a 100644
--- a/drivers/staging/ath6kl/miscdrv/ar3kps/ar3kpsparser.c
+++ b/drivers/staging/ath6kl/miscdrv/ar3kps/ar3kpsparser.c
@@ -812,7 +812,7 @@
for(count = 0; count < Patch_Count; count++) {
AR_DEBUG_PRINTF(ATH_DEBUG_ERR,("Freeing Patch Buffer %d \r\n",count));
- kfree(RamPatch[Patch_Count].Data);
+ kfree(RamPatch[count].Data);
}
for(count = 0; count < Tag_Count; count++) {
diff --git a/drivers/staging/dt3155v4l/dt3155v4l.c b/drivers/staging/dt3155v4l/dt3155v4l.c
index fe02d22..05aa41c 100644
--- a/drivers/staging/dt3155v4l/dt3155v4l.c
+++ b/drivers/staging/dt3155v4l/dt3155v4l.c
@@ -22,6 +22,7 @@
#include <linux/stringify.h>
#include <linux/delay.h>
#include <linux/kthread.h>
+#include <linux/slab.h>
#include <media/v4l2-dev.h>
#include <media/v4l2-ioctl.h>
#include <media/videobuf2-dma-contig.h>
diff --git a/drivers/staging/ft1000/ft1000-pcmcia/ft1000_proc.c b/drivers/staging/ft1000/ft1000-pcmcia/ft1000_proc.c
index 627a98b..9e728b3 100644
--- a/drivers/staging/ft1000/ft1000-pcmcia/ft1000_proc.c
+++ b/drivers/staging/ft1000/ft1000-pcmcia/ft1000_proc.c
@@ -22,6 +22,7 @@
#include <linux/string.h>
#include <linux/vmalloc.h>
#include <linux/netdevice.h>
+#include <asm/io.h>
#include <asm/uaccess.h>
#include "ft1000.h"
diff --git a/drivers/staging/gma500/gem_glue.c b/drivers/staging/gma500/gem_glue.c
index 779ac1a..daac121 100644
--- a/drivers/staging/gma500/gem_glue.c
+++ b/drivers/staging/gma500/gem_glue.c
@@ -20,26 +20,6 @@
#include <drm/drmP.h>
#include <drm/drm.h>
-/**
- * Initialize an already allocated GEM object of the specified size with
- * no GEM provided backing store. Instead the caller is responsible for
- * backing the object and handling it.
- */
-int drm_gem_private_object_init(struct drm_device *dev,
- struct drm_gem_object *obj, size_t size)
-{
- BUG_ON((size & (PAGE_SIZE - 1)) != 0);
-
- obj->dev = dev;
- obj->filp = NULL;
-
- kref_init(&obj->refcount);
- atomic_set(&obj->handle_count, 0);
- obj->size = size;
-
- return 0;
-}
-
void drm_gem_object_release_wrap(struct drm_gem_object *obj)
{
/* Remove the list map if one is present */
@@ -51,8 +31,7 @@
kfree(list->map);
list->map = NULL;
}
- if (obj->filp)
- drm_gem_object_release(obj);
+ drm_gem_object_release(obj);
}
/**
diff --git a/drivers/staging/gma500/gem_glue.h b/drivers/staging/gma500/gem_glue.h
index a0f2bc4..ce5ce30 100644
--- a/drivers/staging/gma500/gem_glue.h
+++ b/drivers/staging/gma500/gem_glue.h
@@ -1,4 +1,2 @@
extern void drm_gem_object_release_wrap(struct drm_gem_object *obj);
-extern int drm_gem_private_object_init(struct drm_device *dev,
- struct drm_gem_object *obj, size_t size);
extern int gem_create_mmap_offset(struct drm_gem_object *obj);
diff --git a/drivers/staging/gma500/mdfld_dsi_dbi.c b/drivers/staging/gma500/mdfld_dsi_dbi.c
index 02e17c9..fd211f3 100644
--- a/drivers/staging/gma500/mdfld_dsi_dbi.c
+++ b/drivers/staging/gma500/mdfld_dsi_dbi.c
@@ -711,10 +711,11 @@
/* Create drm encoder object */
connector = &dsi_connector->base.base;
encoder = &dbi_output->base.base;
+ /* Review this if we ever get MIPI-HDMI bridges or similar */
drm_encoder_init(dev,
encoder,
p_funcs->encoder_funcs,
- DRM_MODE_ENCODER_MIPI);
+ DRM_MODE_ENCODER_LVDS);
drm_encoder_helper_add(encoder, p_funcs->encoder_helper_funcs);
/* Attach to given connector */
diff --git a/drivers/staging/gma500/mdfld_dsi_dbi.h b/drivers/staging/gma500/mdfld_dsi_dbi.h
index dc6242c..f0fa986 100644
--- a/drivers/staging/gma500/mdfld_dsi_dbi.h
+++ b/drivers/staging/gma500/mdfld_dsi_dbi.h
@@ -42,9 +42,6 @@
#include "mdfld_dsi_output.h"
#include "mdfld_output.h"
-#define DRM_MODE_ENCODER_MIPI 5
-
-
/*
* DBI encoder which inherits from mdfld_dsi_encoder
*/
diff --git a/drivers/staging/gma500/mdfld_dsi_dpi.c b/drivers/staging/gma500/mdfld_dsi_dpi.c
index 6e03a91..e685f12 100644
--- a/drivers/staging/gma500/mdfld_dsi_dpi.c
+++ b/drivers/staging/gma500/mdfld_dsi_dpi.c
@@ -777,10 +777,15 @@
/* Create drm encoder object */
connector = &dsi_connector->base.base;
encoder = &dpi_output->base.base;
+ /*
+ * On existing hardware this will be a panel of some form,
+ * if future devices also have HDMI bridges this will need
+ * revisiting
+ */
drm_encoder_init(dev,
encoder,
p_funcs->encoder_funcs,
- DRM_MODE_ENCODER_MIPI);
+ DRM_MODE_ENCODER_LVDS);
drm_encoder_helper_add(encoder,
p_funcs->encoder_helper_funcs);
diff --git a/drivers/staging/gma500/mdfld_dsi_output.c b/drivers/staging/gma500/mdfld_dsi_output.c
index 7536095..9050c0f 100644
--- a/drivers/staging/gma500/mdfld_dsi_output.c
+++ b/drivers/staging/gma500/mdfld_dsi_output.c
@@ -955,7 +955,9 @@
psb_output->type = (pipe == 0) ? INTEL_OUTPUT_MIPI : INTEL_OUTPUT_MIPI2;
connector = &psb_output->base;
- drm_connector_init(dev, connector, &mdfld_dsi_connector_funcs, DRM_MODE_CONNECTOR_MIPI);
+ /* Revisit type if MIPI/HDMI bridges ever appear on Medfield */
+ drm_connector_init(dev, connector, &mdfld_dsi_connector_funcs,
+ DRM_MODE_CONNECTOR_LVDS);
drm_connector_helper_add(connector, &mdfld_dsi_connector_helper_funcs);
connector->display_info.subpixel_order = SubPixelHorizontalRGB;
diff --git a/drivers/staging/gma500/medfield.h b/drivers/staging/gma500/medfield.h
index 38165e8..09e9687 100644
--- a/drivers/staging/gma500/medfield.h
+++ b/drivers/staging/gma500/medfield.h
@@ -21,8 +21,6 @@
* DEALINGS IN THE SOFTWARE.
*/
-#define DRM_MODE_ENCODER_MIPI 5
-
/* Medfield DSI controller registers */
#define MIPIA_DEVICE_READY_REG 0xb000
diff --git a/drivers/staging/gma500/psb_drv.h b/drivers/staging/gma500/psb_drv.h
index 72f487a..fd4732d 100644
--- a/drivers/staging/gma500/psb_drv.h
+++ b/drivers/staging/gma500/psb_drv.h
@@ -35,7 +35,6 @@
/* Append new drm mode definition here, align with libdrm definition */
#define DRM_MODE_SCALE_NO_SCALE 2
-#define DRM_MODE_CONNECTOR_MIPI 15
enum {
CHIP_PSB_8108 = 0, /* Poulsbo */
diff --git a/drivers/staging/hv/blkvsc_drv.c b/drivers/staging/hv/blkvsc_drv.c
index 3612574..d286b22 100644
--- a/drivers/staging/hv/blkvsc_drv.c
+++ b/drivers/staging/hv/blkvsc_drv.c
@@ -325,7 +325,7 @@
page_buf = alloc_page(GFP_KERNEL);
if (!page_buf) {
- kmem_cache_free(blkvsc_req->dev->request_pool, blkvsc_req);
+ kmem_cache_free(blkdev->request_pool, blkvsc_req);
return -ENOMEM;
}
@@ -422,7 +422,7 @@
__free_page(page_buf);
- kmem_cache_free(blkvsc_req->dev->request_pool, blkvsc_req);
+ kmem_cache_free(blkdev->request_pool, blkvsc_req);
return ret;
}
diff --git a/drivers/staging/iio/accel/adis16203_core.c b/drivers/staging/iio/accel/adis16203_core.c
index bf19888..cf5d15d 100644
--- a/drivers/staging/iio/accel/adis16203_core.c
+++ b/drivers/staging/iio/accel/adis16203_core.c
@@ -311,13 +311,17 @@
mutex_lock(&indio_dev->mlock);
addr = adis16203_addresses[chan->address][0];
ret = adis16203_spi_read_reg_16(indio_dev, addr, &val16);
- if (ret)
+ if (ret) {
+ mutex_unlock(&indio_dev->mlock);
return ret;
+ }
if (val16 & ADIS16203_ERROR_ACTIVE) {
ret = adis16203_check_status(indio_dev);
- if (ret)
+ if (ret) {
+ mutex_unlock(&indio_dev->mlock);
return ret;
+ }
}
val16 = val16 & ((1 << chan->scan_type.realbits) - 1);
if (chan->scan_type.sign == 's')
diff --git a/drivers/staging/iio/accel/adis16204_core.c b/drivers/staging/iio/accel/adis16204_core.c
index cfd09b3..3e2b626 100644
--- a/drivers/staging/iio/accel/adis16204_core.c
+++ b/drivers/staging/iio/accel/adis16204_core.c
@@ -341,13 +341,17 @@
mutex_lock(&indio_dev->mlock);
addr = adis16204_addresses[chan->address][0];
ret = adis16204_spi_read_reg_16(indio_dev, addr, &val16);
- if (ret)
+ if (ret) {
+ mutex_unlock(&indio_dev->mlock);
return ret;
+ }
if (val16 & ADIS16204_ERROR_ACTIVE) {
ret = adis16204_check_status(indio_dev);
- if (ret)
+ if (ret) {
+ mutex_unlock(&indio_dev->mlock);
return ret;
+ }
}
val16 = val16 & ((1 << chan->scan_type.realbits) - 1);
if (chan->scan_type.sign == 's')
diff --git a/drivers/staging/iio/accel/adis16209_core.c b/drivers/staging/iio/accel/adis16209_core.c
index 55f3a7b..bec1fa8 100644
--- a/drivers/staging/iio/accel/adis16209_core.c
+++ b/drivers/staging/iio/accel/adis16209_core.c
@@ -337,13 +337,17 @@
mutex_lock(&indio_dev->mlock);
addr = adis16209_addresses[chan->address][0];
ret = adis16209_spi_read_reg_16(indio_dev, addr, &val16);
- if (ret)
+ if (ret) {
+ mutex_unlock(&indio_dev->mlock);
return ret;
+ }
if (val16 & ADIS16209_ERROR_ACTIVE) {
ret = adis16209_check_status(indio_dev);
- if (ret)
+ if (ret) {
+ mutex_unlock(&indio_dev->mlock);
return ret;
+ }
}
val16 = val16 & ((1 << chan->scan_type.realbits) - 1);
if (chan->scan_type.sign == 's')
diff --git a/drivers/staging/iio/accel/adis16240_core.c b/drivers/staging/iio/accel/adis16240_core.c
index 4a4eafc..aee8b69 100644
--- a/drivers/staging/iio/accel/adis16240_core.c
+++ b/drivers/staging/iio/accel/adis16240_core.c
@@ -370,13 +370,17 @@
mutex_lock(&indio_dev->mlock);
addr = adis16240_addresses[chan->address][0];
ret = adis16240_spi_read_reg_16(indio_dev, addr, &val16);
- if (ret)
+ if (ret) {
+ mutex_unlock(&indio_dev->mlock);
return ret;
+ }
if (val16 & ADIS16240_ERROR_ACTIVE) {
ret = adis16240_check_status(indio_dev);
- if (ret)
+ if (ret) {
+ mutex_unlock(&indio_dev->mlock);
return ret;
+ }
}
val16 = val16 & ((1 << chan->scan_type.realbits) - 1);
if (chan->scan_type.sign == 's')
diff --git a/drivers/staging/iio/gyro/adis16260_core.c b/drivers/staging/iio/gyro/adis16260_core.c
index 05797f4..f2d43cf 100644
--- a/drivers/staging/iio/gyro/adis16260_core.c
+++ b/drivers/staging/iio/gyro/adis16260_core.c
@@ -446,13 +446,17 @@
mutex_lock(&indio_dev->mlock);
addr = adis16260_addresses[chan->address][0];
ret = adis16260_spi_read_reg_16(indio_dev, addr, &val16);
- if (ret)
+ if (ret) {
+ mutex_unlock(&indio_dev->mlock);
return ret;
+ }
if (val16 & ADIS16260_ERROR_ACTIVE) {
ret = adis16260_check_status(indio_dev);
- if (ret)
+ if (ret) {
+ mutex_unlock(&indio_dev->mlock);
return ret;
+ }
}
val16 = val16 & ((1 << chan->scan_type.realbits) - 1);
if (chan->scan_type.sign == 's')
diff --git a/drivers/staging/nvec/TODO b/drivers/staging/nvec/TODO
index 77b47f7..649d6b7 100644
--- a/drivers/staging/nvec/TODO
+++ b/drivers/staging/nvec/TODO
@@ -4,5 +4,7 @@
- add compile as module support
- move nvec devices to mfd cells?
- adjust to kernel style
-
-
+ - fix clk usage
+ should not be using clk_get_sys(), but clk_get(&pdev->dev, conn)
+ where conn is either NULL if the device only has one clock, or
+ the device specific name if it has multiple clocks.
diff --git a/drivers/staging/rtl8192u/r819xU_firmware.c b/drivers/staging/rtl8192u/r819xU_firmware.c
index 6766f46..4bb5fff 100644
--- a/drivers/staging/rtl8192u/r819xU_firmware.c
+++ b/drivers/staging/rtl8192u/r819xU_firmware.c
@@ -399,10 +399,7 @@
}
-
-
-
-
-
-
+MODULE_FIRMWARE("RTL8192U/boot.img");
+MODULE_FIRMWARE("RTL8192U/main.img");
+MODULE_FIRMWARE("RTL8192U/data.img");
diff --git a/drivers/staging/rts_pstor/rtsx.c b/drivers/staging/rts_pstor/rtsx.c
index 5ff59f2..16c73fb 100644
--- a/drivers/staging/rts_pstor/rtsx.c
+++ b/drivers/staging/rts_pstor/rtsx.c
@@ -66,12 +66,6 @@
module_param(msi_en, int, S_IRUGO | S_IWUSR);
MODULE_PARM_DESC(msi_en, "enable msi");
-/* These are used to make sure the module doesn't unload before all the
- * threads have exited.
- */
-static atomic_t total_threads = ATOMIC_INIT(0);
-static DECLARE_COMPLETION(threads_gone);
-
static irqreturn_t rtsx_interrupt(int irq, void *dev_id);
/***********************************************************************
@@ -192,7 +186,7 @@
/* enqueue the command and wake up the control thread */
srb->scsi_done = done;
chip->srb = srb;
- up(&(dev->sema));
+ complete(&dev->cmnd_ready);
return 0;
}
@@ -475,7 +469,7 @@
current->flags |= PF_NOFREEZE;
for (;;) {
- if (down_interruptible(&dev->sema))
+ if (wait_for_completion_interruptible(&dev->cmnd_ready))
break;
/* lock the device pointers */
@@ -557,8 +551,6 @@
mutex_unlock(&dev->dev_mutex);
} /* for (;;) */
- scsi_host_put(host);
-
/* notify the exit routine that we're actually exiting now
*
* complete()/wait_for_completion() is similar to up()/down(),
@@ -573,7 +565,7 @@
* This is important in preemption kernels, which transfer the flow
* of execution immediately upon a complete().
*/
- complete_and_exit(&threads_gone, 0);
+ complete_and_exit(&dev->control_exit, 0);
}
@@ -581,7 +573,6 @@
{
struct rtsx_dev *dev = (struct rtsx_dev *)__dev;
struct rtsx_chip *chip = dev->chip;
- struct Scsi_Host *host = rtsx_to_host(dev);
struct sd_info *sd_card = &(chip->sd_card);
struct xd_info *xd_card = &(chip->xd_card);
struct ms_info *ms_card = &(chip->ms_card);
@@ -621,8 +612,7 @@
mutex_unlock(&dev->dev_mutex);
}
- scsi_host_put(host);
- complete_and_exit(&threads_gone, 0);
+ complete_and_exit(&dev->polling_exit, 0);
}
/*
@@ -699,29 +689,38 @@
{
printk(KERN_INFO "-- %s\n", __func__);
- if (dev->rtsx_resv_buf) {
- dma_free_coherent(&(dev->pci->dev), HOST_CMDS_BUF_LEN,
- dev->rtsx_resv_buf, dev->rtsx_resv_buf_addr);
- dev->chip->host_cmds_ptr = NULL;
- dev->chip->host_sg_tbl_ptr = NULL;
- }
-
- pci_disable_device(dev->pci);
- pci_release_regions(dev->pci);
-
- if (dev->irq > 0) {
- free_irq(dev->irq, (void *)dev);
- }
- if (dev->chip->msi_en) {
- pci_disable_msi(dev->pci);
- }
-
/* Tell the control thread to exit. The SCSI host must
* already have been removed so it won't try to queue
* any more commands.
*/
printk(KERN_INFO "-- sending exit command to thread\n");
- up(&dev->sema);
+ complete(&dev->cmnd_ready);
+ if (dev->ctl_thread)
+ wait_for_completion(&dev->control_exit);
+ if (dev->polling_thread)
+ wait_for_completion(&dev->polling_exit);
+
+ wait_timeout(200);
+
+ if (dev->rtsx_resv_buf) {
+ dma_free_coherent(&(dev->pci->dev), RTSX_RESV_BUF_LEN,
+ dev->rtsx_resv_buf, dev->rtsx_resv_buf_addr);
+ dev->chip->host_cmds_ptr = NULL;
+ dev->chip->host_sg_tbl_ptr = NULL;
+ }
+
+ if (dev->irq > 0)
+ free_irq(dev->irq, (void *)dev);
+ if (dev->chip->msi_en)
+ pci_disable_msi(dev->pci);
+ if (dev->remap_addr)
+ iounmap(dev->remap_addr);
+
+ pci_disable_device(dev->pci);
+ pci_release_regions(dev->pci);
+
+ rtsx_release_chip(dev->chip);
+ kfree(dev->chip);
}
/* First stage of disconnect processing: stop all commands and remove
@@ -739,6 +738,7 @@
scsi_unlock(host);
mutex_unlock(&dev->dev_mutex);
wake_up(&dev->delay_wait);
+ wait_for_completion(&dev->scanning_done);
/* Wait some time to let other threads exist */
wait_timeout(100);
@@ -793,8 +793,7 @@
/* Should we unbind if no devices were detected? */
}
- scsi_host_put(rtsx_to_host(dev));
- complete_and_exit(&threads_gone, 0);
+ complete_and_exit(&dev->scanning_done, 0);
}
static void rtsx_init_options(struct rtsx_chip *chip)
@@ -941,8 +940,11 @@
spin_lock_init(&dev->reg_lock);
mutex_init(&(dev->dev_mutex));
- sema_init(&(dev->sema), 0);
+ init_completion(&dev->cmnd_ready);
+ init_completion(&dev->control_exit);
+ init_completion(&dev->polling_exit);
init_completion(&(dev->notify));
+ init_completion(&dev->scanning_done);
init_waitqueue_head(&dev->delay_wait);
dev->pci = pci;
@@ -992,28 +994,22 @@
pci_set_master(pci);
synchronize_irq(dev->irq);
- err = scsi_add_host(host, &pci->dev);
- if (err) {
- printk(KERN_ERR "Unable to add the scsi host\n");
- goto errout;
- }
-
rtsx_init_chip(dev->chip);
/* Start up our control thread */
- th = kthread_create(rtsx_control_thread, dev, CR_DRIVER_NAME);
+ th = kthread_run(rtsx_control_thread, dev, CR_DRIVER_NAME);
if (IS_ERR(th)) {
printk(KERN_ERR "Unable to start control thread\n");
err = PTR_ERR(th);
goto errout;
}
+ dev->ctl_thread = th;
- /* Take a reference to the host for the control thread and
- * count it among all the threads we have launched. Then
- * start it up. */
- scsi_host_get(rtsx_to_host(dev));
- atomic_inc(&total_threads);
- wake_up_process(th);
+ err = scsi_add_host(host, &pci->dev);
+ if (err) {
+ printk(KERN_ERR "Unable to add the scsi host\n");
+ goto errout;
+ }
/* Start up the thread for delayed SCSI-device scanning */
th = kthread_create(rtsx_scan_thread, dev, "rtsx-scan");
@@ -1024,28 +1020,17 @@
goto errout;
}
- /* Take a reference to the host for the scanning thread and
- * count it among all the threads we have launched. Then
- * start it up. */
- scsi_host_get(rtsx_to_host(dev));
- atomic_inc(&total_threads);
wake_up_process(th);
/* Start up the thread for polling thread */
- th = kthread_create(rtsx_polling_thread, dev, "rtsx-polling");
+ th = kthread_run(rtsx_polling_thread, dev, "rtsx-polling");
if (IS_ERR(th)) {
printk(KERN_ERR "Unable to start the device-polling thread\n");
quiesce_and_remove_host(dev);
err = PTR_ERR(th);
goto errout;
}
-
- /* Take a reference to the host for the polling thread and
- * count it among all the threads we have launched. Then
- * start it up. */
- scsi_host_get(rtsx_to_host(dev));
- atomic_inc(&total_threads);
- wake_up_process(th);
+ dev->polling_thread = th;
pci_set_drvdata(pci, dev);
@@ -1108,16 +1093,6 @@
pci_unregister_driver(&driver);
- /* Don't return until all of our control and scanning threads
- * have exited. Since each thread signals threads_gone as its
- * last act, we have to call wait_for_completion the right number
- * of times.
- */
- while (atomic_read(&total_threads) > 0) {
- wait_for_completion(&threads_gone);
- atomic_dec(&total_threads);
- }
-
printk(KERN_INFO "%s module exit\n", CR_DRIVER_NAME);
}
diff --git a/drivers/staging/rts_pstor/rtsx.h b/drivers/staging/rts_pstor/rtsx.h
index 247615b..86e47c2 100644
--- a/drivers/staging/rts_pstor/rtsx.h
+++ b/drivers/staging/rts_pstor/rtsx.h
@@ -112,9 +112,16 @@
/* locks */
spinlock_t reg_lock;
+ struct task_struct *ctl_thread; /* the control thread */
+ struct task_struct *polling_thread; /* the polling thread */
+
/* mutual exclusion and synchronization structures */
- struct semaphore sema; /* to sleep thread on */
+ struct completion cmnd_ready; /* to sleep thread on */
+ struct completion control_exit; /* control thread exit */
+ struct completion polling_exit; /* polling thread exit */
struct completion notify; /* thread begin/end */
+ struct completion scanning_done; /* wait for scan thread */
+
wait_queue_head_t delay_wait; /* wait during scan, reset */
struct mutex dev_mutex;
diff --git a/drivers/staging/solo6x10/core.c b/drivers/staging/solo6x10/core.c
index 7677994..f974f64 100644
--- a/drivers/staging/solo6x10/core.c
+++ b/drivers/staging/solo6x10/core.c
@@ -21,6 +21,7 @@
#include <linux/module.h>
#include <linux/pci.h>
#include <linux/interrupt.h>
+#include <linux/slab.h>
#include <linux/videodev2.h>
#include "solo6x10.h"
#include "tw28.h"
diff --git a/drivers/staging/solo6x10/enc.c b/drivers/staging/solo6x10/enc.c
index 285f7f3..de50259 100644
--- a/drivers/staging/solo6x10/enc.c
+++ b/drivers/staging/solo6x10/enc.c
@@ -18,6 +18,7 @@
*/
#include <linux/kernel.h>
+#include <linux/slab.h>
#include "solo6x10.h"
#include "osd-font.h"
diff --git a/drivers/staging/solo6x10/g723.c b/drivers/staging/solo6x10/g723.c
index bd8eb92..59274bf 100644
--- a/drivers/staging/solo6x10/g723.c
+++ b/drivers/staging/solo6x10/g723.c
@@ -21,6 +21,7 @@
#include <linux/mempool.h>
#include <linux/poll.h>
#include <linux/kthread.h>
+#include <linux/slab.h>
#include <linux/freezer.h>
#include <sound/core.h>
#include <sound/initval.h>
diff --git a/drivers/staging/solo6x10/p2m.c b/drivers/staging/solo6x10/p2m.c
index 5717eab..56210f0 100644
--- a/drivers/staging/solo6x10/p2m.c
+++ b/drivers/staging/solo6x10/p2m.c
@@ -18,6 +18,7 @@
*/
#include <linux/kernel.h>
+#include <linux/slab.h>
#include <linux/scatterlist.h>
#include "solo6x10.h"
diff --git a/drivers/staging/solo6x10/solo6x10.h b/drivers/staging/solo6x10/solo6x10.h
index 17c06bd..abee721 100644
--- a/drivers/staging/solo6x10/solo6x10.h
+++ b/drivers/staging/solo6x10/solo6x10.h
@@ -28,6 +28,7 @@
#include <linux/list.h>
#include <linux/wait.h>
#include <linux/delay.h>
+#include <linux/slab.h>
#include <asm/io.h>
#include <linux/atomic.h>
#include <linux/videodev2.h>
diff --git a/drivers/staging/speakup/devsynth.c b/drivers/staging/speakup/devsynth.c
index 39dc586..940769e 100644
--- a/drivers/staging/speakup/devsynth.c
+++ b/drivers/staging/speakup/devsynth.c
@@ -18,13 +18,14 @@
{
size_t count = nbytes;
const char *ptr = buffer;
- int bytes;
+ size_t bytes;
unsigned long flags;
u_char buf[256];
+
if (synth == NULL)
return -ENODEV;
while (count > 0) {
- bytes = min_t(size_t, count, sizeof(buf));
+ bytes = min(count, sizeof(buf));
if (copy_from_user(buf, ptr, bytes))
return -EFAULT;
count -= bytes;
diff --git a/drivers/staging/zcache/Makefile b/drivers/staging/zcache/Makefile
index f5ec64f..60daa27 100644
--- a/drivers/staging/zcache/Makefile
+++ b/drivers/staging/zcache/Makefile
@@ -1,3 +1,3 @@
-zcache-y := tmem.o
+zcache-y := zcache-main.o tmem.o
obj-$(CONFIG_ZCACHE) += zcache.o
diff --git a/drivers/staging/zcache/zcache.c b/drivers/staging/zcache/zcache-main.c
similarity index 99%
rename from drivers/staging/zcache/zcache.c
rename to drivers/staging/zcache/zcache-main.c
index 65a81a0..855a5bb 100644
--- a/drivers/staging/zcache/zcache.c
+++ b/drivers/staging/zcache/zcache-main.c
@@ -19,6 +19,7 @@
* http://marc.info/?l=linux-mm&m=127811271605009
*/
+#include <linux/module.h>
#include <linux/cpu.h>
#include <linux/highmem.h>
#include <linux/list.h>
@@ -27,6 +28,7 @@
#include <linux/spinlock.h>
#include <linux/types.h>
#include <linux/atomic.h>
+#include <linux/math64.h>
#include "tmem.h"
#include "../zram/xvmalloc.h" /* if built in drivers/staging */
@@ -53,6 +55,9 @@
#define MAX_CLIENTS 16
#define LOCAL_CLIENT ((uint16_t)-1)
+
+MODULE_LICENSE("GPL");
+
struct zcache_client {
struct tmem_pool *tmem_pools[MAX_POOLS_PER_CLIENT];
struct xv_pool *xvpool;
@@ -1158,6 +1163,7 @@
uint16_t client_id = get_client_id_from_client(cli);
unsigned long zv_mean_zsize;
unsigned long curr_pers_pampd_count;
+ u64 total_zsize;
if (eph) {
ret = zcache_compress(page, &cdata, &clen);
@@ -1190,8 +1196,9 @@
}
/* reject if mean compression is too poor */
if ((clen > zv_max_mean_zsize) && (curr_pers_pampd_count > 0)) {
- zv_mean_zsize = xv_get_total_size_bytes(cli->xvpool) /
- curr_pers_pampd_count;
+ total_zsize = xv_get_total_size_bytes(cli->xvpool);
+ zv_mean_zsize = div_u64(total_zsize,
+ curr_pers_pampd_count);
if (zv_mean_zsize > zv_max_mean_zsize) {
zcache_mean_compress_poor++;
goto out;
@@ -1929,9 +1936,9 @@
static int __init zcache_init(void)
{
-#ifdef CONFIG_SYSFS
int ret = 0;
+#ifdef CONFIG_SYSFS
ret = sysfs_create_group(mm_kobj, &zcache_attr_group);
if (ret) {
pr_err("zcache: can't create sysfs\n");
diff --git a/drivers/target/iscsi/Kconfig b/drivers/target/iscsi/Kconfig
index 564ff4e..8345fb4 100644
--- a/drivers/target/iscsi/Kconfig
+++ b/drivers/target/iscsi/Kconfig
@@ -1,5 +1,6 @@
config ISCSI_TARGET
tristate "Linux-iSCSI.org iSCSI Target Mode Stack"
+ depends on NET
select CRYPTO
select CRYPTO_CRC32C
select CRYPTO_CRC32C_INTEL if X86
diff --git a/drivers/target/iscsi/iscsi_target.c b/drivers/target/iscsi/iscsi_target.c
index 14c81c4..c24fb10 100644
--- a/drivers/target/iscsi/iscsi_target.c
+++ b/drivers/target/iscsi/iscsi_target.c
@@ -120,7 +120,7 @@
struct iscsi_tiqn *tiqn = NULL;
int ret;
- if (strlen(buf) > ISCSI_IQN_LEN) {
+ if (strlen(buf) >= ISCSI_IQN_LEN) {
pr_err("Target IQN exceeds %d bytes\n",
ISCSI_IQN_LEN);
return ERR_PTR(-EINVAL);
@@ -1857,7 +1857,7 @@
char *text_ptr, *text_in;
int cmdsn_ret, niov = 0, rx_got, rx_size;
u32 checksum = 0, data_crc = 0, payload_length;
- u32 padding = 0, text_length = 0;
+ u32 padding = 0, pad_bytes = 0, text_length = 0;
struct iscsi_cmd *cmd;
struct kvec iov[3];
struct iscsi_text *hdr;
@@ -1896,7 +1896,7 @@
padding = ((-payload_length) & 3);
if (padding != 0) {
- iov[niov].iov_base = cmd->pad_bytes;
+ iov[niov].iov_base = &pad_bytes;
iov[niov++].iov_len = padding;
rx_size += padding;
pr_debug("Receiving %u additional bytes"
@@ -1917,7 +1917,7 @@
if (conn->conn_ops->DataDigest) {
iscsit_do_crypto_hash_buf(&conn->conn_rx_hash,
text_in, text_length,
- padding, cmd->pad_bytes,
+ padding, (u8 *)&pad_bytes,
(u8 *)&data_crc);
if (checksum != data_crc) {
@@ -3468,7 +3468,12 @@
}
#else
-#define iscsit_thread_get_cpumask(X) ({})
+
+void iscsit_thread_get_cpumask(struct iscsi_conn *conn)
+{
+ return;
+}
+
#define iscsit_thread_check_cpumask(X, Y, Z) ({})
#endif /* CONFIG_SMP */
diff --git a/drivers/target/iscsi/iscsi_target_configfs.c b/drivers/target/iscsi/iscsi_target_configfs.c
index 32bb92c..f095e65 100644
--- a/drivers/target/iscsi/iscsi_target_configfs.c
+++ b/drivers/target/iscsi/iscsi_target_configfs.c
@@ -181,7 +181,7 @@
return ERR_PTR(-EOVERFLOW);
}
memset(buf, 0, MAX_PORTAL_LEN + 1);
- snprintf(buf, MAX_PORTAL_LEN, "%s", name);
+ snprintf(buf, MAX_PORTAL_LEN + 1, "%s", name);
memset(&sockaddr, 0, sizeof(struct __kernel_sockaddr_storage));
diff --git a/drivers/target/iscsi/iscsi_target_nego.c b/drivers/target/iscsi/iscsi_target_nego.c
index 713a4d2..4d087ac 100644
--- a/drivers/target/iscsi/iscsi_target_nego.c
+++ b/drivers/target/iscsi/iscsi_target_nego.c
@@ -978,7 +978,7 @@
pr_err("Unable to allocate memory for struct iscsi_login.\n");
iscsit_tx_login_rsp(conn, ISCSI_STATUS_CLS_TARGET_ERR,
ISCSI_LOGIN_STATUS_NO_RESOURCES);
- goto out;
+ return NULL;
}
login->req = kzalloc(ISCSI_HDR_LEN, GFP_KERNEL);
diff --git a/drivers/target/target_core_transport.c b/drivers/target/target_core_transport.c
index c75a01a..8976032 100644
--- a/drivers/target/target_core_transport.c
+++ b/drivers/target/target_core_transport.c
@@ -1747,6 +1747,8 @@
}
EXPORT_SYMBOL(transport_generic_handle_cdb);
+static void transport_generic_request_failure(struct se_cmd *,
+ struct se_device *, int, int);
/*
* Used by fabric module frontends to queue tasks directly.
* Many only be used from process context only
@@ -1754,6 +1756,8 @@
int transport_handle_cdb_direct(
struct se_cmd *cmd)
{
+ int ret;
+
if (!cmd->se_lun) {
dump_stack();
pr_err("cmd->se_lun is NULL\n");
@@ -1765,8 +1769,31 @@
" from interrupt context\n");
return -EINVAL;
}
-
- return transport_generic_new_cmd(cmd);
+ /*
+ * Set TRANSPORT_NEW_CMD state and cmd->t_transport_active=1 following
+ * transport_generic_handle_cdb*() -> transport_add_cmd_to_queue()
+ * in existing usage to ensure that outstanding descriptors are handled
+ * correctly during shutdown via transport_generic_wait_for_tasks()
+ *
+ * Also, we don't take cmd->t_state_lock here as we only expect
+ * this to be called for initial descriptor submission.
+ */
+ cmd->t_state = TRANSPORT_NEW_CMD;
+ atomic_set(&cmd->t_transport_active, 1);
+ /*
+ * transport_generic_new_cmd() is already handling QUEUE_FULL,
+ * so follow TRANSPORT_NEW_CMD processing thread context usage
+ * and call transport_generic_request_failure() if necessary..
+ */
+ ret = transport_generic_new_cmd(cmd);
+ if (ret == -EAGAIN)
+ return 0;
+ else if (ret < 0) {
+ cmd->transport_error_status = ret;
+ transport_generic_request_failure(cmd, NULL, 0,
+ (cmd->data_direction != DMA_TO_DEVICE));
+ }
+ return 0;
}
EXPORT_SYMBOL(transport_handle_cdb_direct);
@@ -3324,7 +3351,7 @@
goto out_invalid_cdb_field;
}
- cmd->t_task_lba = get_unaligned_be16(&cdb[2]);
+ cmd->t_task_lba = get_unaligned_be64(&cdb[2]);
passthrough = (dev->transport->transport_type ==
TRANSPORT_PLUGIN_PHBA_PDEV);
/*
diff --git a/drivers/target/tcm_fc/tcm_fc.h b/drivers/target/tcm_fc/tcm_fc.h
index f7fff7e..bd4fe21 100644
--- a/drivers/target/tcm_fc/tcm_fc.h
+++ b/drivers/target/tcm_fc/tcm_fc.h
@@ -187,4 +187,9 @@
ssize_t ft_format_wwn(char *, size_t, u64);
+/*
+ * Underlying HW specific helper function
+ */
+void ft_invl_hw_context(struct ft_cmd *);
+
#endif /* __TCM_FC_H__ */
diff --git a/drivers/target/tcm_fc/tfc_cmd.c b/drivers/target/tcm_fc/tfc_cmd.c
index 09df38b..5654dc2 100644
--- a/drivers/target/tcm_fc/tfc_cmd.c
+++ b/drivers/target/tcm_fc/tfc_cmd.c
@@ -320,6 +320,7 @@
default:
pr_debug("%s: unhandled frame r_ctl %x\n",
__func__, fh->fh_r_ctl);
+ ft_invl_hw_context(cmd);
fc_frame_free(fp);
transport_generic_free_cmd(&cmd->se_cmd, 0, 0);
break;
diff --git a/drivers/target/tcm_fc/tfc_io.c b/drivers/target/tcm_fc/tfc_io.c
index 8e2a46d..c37f4cd 100644
--- a/drivers/target/tcm_fc/tfc_io.c
+++ b/drivers/target/tcm_fc/tfc_io.c
@@ -213,62 +213,49 @@
if (!(ntoh24(fh->fh_f_ctl) & FC_FC_REL_OFF))
goto drop;
- /*
- * Doesn't expect even single byte of payload. Payload
- * is expected to be copied directly to user buffers
- * due to DDP (Large Rx offload) feature, hence
- * BUG_ON if BUF is non-NULL
- */
- buf = fc_frame_payload_get(fp, 1);
- if (cmd->was_ddp_setup && buf) {
- pr_debug("%s: When DDP was setup, not expected to"
- "receive frame with payload, Payload shall be"
- "copied directly to buffer instead of coming "
- "via. legacy receive queues\n", __func__);
- BUG_ON(buf);
+ f_ctl = ntoh24(fh->fh_f_ctl);
+ ep = fc_seq_exch(seq);
+ lport = ep->lp;
+ if (cmd->was_ddp_setup) {
+ BUG_ON(!ep);
+ BUG_ON(!lport);
}
/*
- * If ft_cmd indicated 'ddp_setup', in that case only the last frame
- * should come with 'TSI bit being set'. If 'TSI bit is not set and if
- * data frame appears here, means error condition. In both the cases
- * release the DDP context (ddp_put) and in error case, as well
- * initiate error recovery mechanism.
+ * Doesn't expect payload if DDP is setup. Payload
+ * is expected to be copied directly to user buffers
+ * due to DDP (Large Rx offload),
*/
- ep = fc_seq_exch(seq);
- if (cmd->was_ddp_setup) {
- BUG_ON(!ep);
- lport = ep->lp;
- BUG_ON(!lport);
- }
- if (cmd->was_ddp_setup && ep->xid != FC_XID_UNKNOWN) {
- f_ctl = ntoh24(fh->fh_f_ctl);
- /*
- * If TSI bit set in f_ctl, means last write data frame is
- * received successfully where payload is posted directly
- * to user buffer and only the last frame's header is posted
- * in legacy receive queue
- */
- if (f_ctl & FC_FC_SEQ_INIT) { /* TSI bit set in FC frame */
- cmd->write_data_len = lport->tt.ddp_done(lport,
- ep->xid);
- goto last_frame;
- } else {
- /*
- * Updating the write_data_len may be meaningless at
- * this point, but just in case if required in future
- * for debugging or any other purpose
- */
- pr_err("%s: Received frame with TSI bit not"
- " being SET, dropping the frame, "
- "cmd->sg <%p>, cmd->sg_cnt <0x%x>\n",
- __func__, cmd->sg, cmd->sg_cnt);
- cmd->write_data_len = lport->tt.ddp_done(lport,
- ep->xid);
- lport->tt.seq_exch_abort(cmd->seq, 0);
- goto drop;
- }
- }
+ buf = fc_frame_payload_get(fp, 1);
+ if (buf)
+ pr_err("%s: xid 0x%x, f_ctl 0x%x, cmd->sg %p, "
+ "cmd->sg_cnt 0x%x. DDP was setup"
+ " hence not expected to receive frame with "
+ "payload, Frame will be dropped if "
+ "'Sequence Initiative' bit in f_ctl is "
+ "not set\n", __func__, ep->xid, f_ctl,
+ cmd->sg, cmd->sg_cnt);
+ /*
+ * Invalidate HW DDP context if it was setup for respective
+ * command. Invalidation of HW DDP context is requited in both
+ * situation (success and error).
+ */
+ ft_invl_hw_context(cmd);
+
+ /*
+ * If "Sequence Initiative (TSI)" bit set in f_ctl, means last
+ * write data frame is received successfully where payload is
+ * posted directly to user buffer and only the last frame's
+ * header is posted in receive queue.
+ *
+ * If "Sequence Initiative (TSI)" bit is not set, means error
+ * condition w.r.t. DDP, hence drop the packet and let explict
+ * ABORTS from other end of exchange timer trigger the recovery.
+ */
+ if (f_ctl & FC_FC_SEQ_INIT)
+ goto last_frame;
+ else
+ goto drop;
rel_off = ntohl(fh->fh_parm_offset);
frame_len = fr_len(fp);
@@ -331,3 +318,39 @@
drop:
fc_frame_free(fp);
}
+
+/*
+ * Handle and cleanup any HW specific resources if
+ * received ABORTS, errors, timeouts.
+ */
+void ft_invl_hw_context(struct ft_cmd *cmd)
+{
+ struct fc_seq *seq = cmd->seq;
+ struct fc_exch *ep = NULL;
+ struct fc_lport *lport = NULL;
+
+ BUG_ON(!cmd);
+
+ /* Cleanup the DDP context in HW if DDP was setup */
+ if (cmd->was_ddp_setup && seq) {
+ ep = fc_seq_exch(seq);
+ if (ep) {
+ lport = ep->lp;
+ if (lport && (ep->xid <= lport->lro_xid))
+ /*
+ * "ddp_done" trigger invalidation of HW
+ * specific DDP context
+ */
+ cmd->write_data_len = lport->tt.ddp_done(lport,
+ ep->xid);
+
+ /*
+ * Resetting same variable to indicate HW's
+ * DDP context has been invalidated to avoid
+ * re_invalidation of same context (context is
+ * identified using ep->xid)
+ */
+ cmd->was_ddp_setup = 0;
+ }
+ }
+}
diff --git a/drivers/thermal/Kconfig b/drivers/thermal/Kconfig
index bf7c687..f7f71b2 100644
--- a/drivers/thermal/Kconfig
+++ b/drivers/thermal/Kconfig
@@ -14,11 +14,7 @@
If you want this support, you should say Y or M here.
config THERMAL_HWMON
- bool "Hardware monitoring support"
+ bool
depends on THERMAL
depends on HWMON=y || HWMON=THERMAL
- help
- The generic thermal sysfs driver's hardware monitoring support
- requires a 2.10.7/3.0.2 or later lm-sensors userspace.
-
- Say Y if your user-space is new enough.
+ default y
diff --git a/drivers/thermal/thermal_sys.c b/drivers/thermal/thermal_sys.c
index 0b1c82a..708f8e9 100644
--- a/drivers/thermal/thermal_sys.c
+++ b/drivers/thermal/thermal_sys.c
@@ -420,6 +420,29 @@
/* hwmon sys I/F */
#include <linux/hwmon.h>
+
+/* thermal zone devices with the same type share one hwmon device */
+struct thermal_hwmon_device {
+ char type[THERMAL_NAME_LENGTH];
+ struct device *device;
+ int count;
+ struct list_head tz_list;
+ struct list_head node;
+};
+
+struct thermal_hwmon_attr {
+ struct device_attribute attr;
+ char name[16];
+};
+
+/* one temperature input for each thermal zone */
+struct thermal_hwmon_temp {
+ struct list_head hwmon_node;
+ struct thermal_zone_device *tz;
+ struct thermal_hwmon_attr temp_input; /* hwmon sys attr */
+ struct thermal_hwmon_attr temp_crit; /* hwmon sys attr */
+};
+
static LIST_HEAD(thermal_hwmon_list);
static ssize_t
@@ -437,9 +460,10 @@
int ret;
struct thermal_hwmon_attr *hwmon_attr
= container_of(attr, struct thermal_hwmon_attr, attr);
- struct thermal_zone_device *tz
- = container_of(hwmon_attr, struct thermal_zone_device,
+ struct thermal_hwmon_temp *temp
+ = container_of(hwmon_attr, struct thermal_hwmon_temp,
temp_input);
+ struct thermal_zone_device *tz = temp->tz;
ret = tz->ops->get_temp(tz, &temperature);
@@ -455,9 +479,10 @@
{
struct thermal_hwmon_attr *hwmon_attr
= container_of(attr, struct thermal_hwmon_attr, attr);
- struct thermal_zone_device *tz
- = container_of(hwmon_attr, struct thermal_zone_device,
+ struct thermal_hwmon_temp *temp
+ = container_of(hwmon_attr, struct thermal_hwmon_temp,
temp_crit);
+ struct thermal_zone_device *tz = temp->tz;
long temperature;
int ret;
@@ -469,22 +494,54 @@
}
-static int
-thermal_add_hwmon_sysfs(struct thermal_zone_device *tz)
+static struct thermal_hwmon_device *
+thermal_hwmon_lookup_by_type(const struct thermal_zone_device *tz)
{
struct thermal_hwmon_device *hwmon;
- int new_hwmon_device = 1;
- int result;
mutex_lock(&thermal_list_lock);
list_for_each_entry(hwmon, &thermal_hwmon_list, node)
if (!strcmp(hwmon->type, tz->type)) {
- new_hwmon_device = 0;
mutex_unlock(&thermal_list_lock);
- goto register_sys_interface;
+ return hwmon;
}
mutex_unlock(&thermal_list_lock);
+ return NULL;
+}
+
+/* Find the temperature input matching a given thermal zone */
+static struct thermal_hwmon_temp *
+thermal_hwmon_lookup_temp(const struct thermal_hwmon_device *hwmon,
+ const struct thermal_zone_device *tz)
+{
+ struct thermal_hwmon_temp *temp;
+
+ mutex_lock(&thermal_list_lock);
+ list_for_each_entry(temp, &hwmon->tz_list, hwmon_node)
+ if (temp->tz == tz) {
+ mutex_unlock(&thermal_list_lock);
+ return temp;
+ }
+ mutex_unlock(&thermal_list_lock);
+
+ return NULL;
+}
+
+static int
+thermal_add_hwmon_sysfs(struct thermal_zone_device *tz)
+{
+ struct thermal_hwmon_device *hwmon;
+ struct thermal_hwmon_temp *temp;
+ int new_hwmon_device = 1;
+ int result;
+
+ hwmon = thermal_hwmon_lookup_by_type(tz);
+ if (hwmon) {
+ new_hwmon_device = 0;
+ goto register_sys_interface;
+ }
+
hwmon = kzalloc(sizeof(struct thermal_hwmon_device), GFP_KERNEL);
if (!hwmon)
return -ENOMEM;
@@ -502,30 +559,36 @@
goto free_mem;
register_sys_interface:
- tz->hwmon = hwmon;
+ temp = kzalloc(sizeof(struct thermal_hwmon_temp), GFP_KERNEL);
+ if (!temp) {
+ result = -ENOMEM;
+ goto unregister_name;
+ }
+
+ temp->tz = tz;
hwmon->count++;
- snprintf(tz->temp_input.name, THERMAL_NAME_LENGTH,
+ snprintf(temp->temp_input.name, THERMAL_NAME_LENGTH,
"temp%d_input", hwmon->count);
- tz->temp_input.attr.attr.name = tz->temp_input.name;
- tz->temp_input.attr.attr.mode = 0444;
- tz->temp_input.attr.show = temp_input_show;
- sysfs_attr_init(&tz->temp_input.attr.attr);
- result = device_create_file(hwmon->device, &tz->temp_input.attr);
+ temp->temp_input.attr.attr.name = temp->temp_input.name;
+ temp->temp_input.attr.attr.mode = 0444;
+ temp->temp_input.attr.show = temp_input_show;
+ sysfs_attr_init(&temp->temp_input.attr.attr);
+ result = device_create_file(hwmon->device, &temp->temp_input.attr);
if (result)
- goto unregister_name;
+ goto free_temp_mem;
if (tz->ops->get_crit_temp) {
unsigned long temperature;
if (!tz->ops->get_crit_temp(tz, &temperature)) {
- snprintf(tz->temp_crit.name, THERMAL_NAME_LENGTH,
+ snprintf(temp->temp_crit.name, THERMAL_NAME_LENGTH,
"temp%d_crit", hwmon->count);
- tz->temp_crit.attr.attr.name = tz->temp_crit.name;
- tz->temp_crit.attr.attr.mode = 0444;
- tz->temp_crit.attr.show = temp_crit_show;
- sysfs_attr_init(&tz->temp_crit.attr.attr);
+ temp->temp_crit.attr.attr.name = temp->temp_crit.name;
+ temp->temp_crit.attr.attr.mode = 0444;
+ temp->temp_crit.attr.show = temp_crit_show;
+ sysfs_attr_init(&temp->temp_crit.attr.attr);
result = device_create_file(hwmon->device,
- &tz->temp_crit.attr);
+ &temp->temp_crit.attr);
if (result)
goto unregister_input;
}
@@ -534,13 +597,15 @@
mutex_lock(&thermal_list_lock);
if (new_hwmon_device)
list_add_tail(&hwmon->node, &thermal_hwmon_list);
- list_add_tail(&tz->hwmon_node, &hwmon->tz_list);
+ list_add_tail(&temp->hwmon_node, &hwmon->tz_list);
mutex_unlock(&thermal_list_lock);
return 0;
unregister_input:
- device_remove_file(hwmon->device, &tz->temp_input.attr);
+ device_remove_file(hwmon->device, &temp->temp_input.attr);
+ free_temp_mem:
+ kfree(temp);
unregister_name:
if (new_hwmon_device) {
device_remove_file(hwmon->device, &dev_attr_name);
@@ -556,15 +621,30 @@
static void
thermal_remove_hwmon_sysfs(struct thermal_zone_device *tz)
{
- struct thermal_hwmon_device *hwmon = tz->hwmon;
+ struct thermal_hwmon_device *hwmon;
+ struct thermal_hwmon_temp *temp;
- tz->hwmon = NULL;
- device_remove_file(hwmon->device, &tz->temp_input.attr);
+ hwmon = thermal_hwmon_lookup_by_type(tz);
+ if (unlikely(!hwmon)) {
+ /* Should never happen... */
+ dev_dbg(&tz->device, "hwmon device lookup failed!\n");
+ return;
+ }
+
+ temp = thermal_hwmon_lookup_temp(hwmon, tz);
+ if (unlikely(!temp)) {
+ /* Should never happen... */
+ dev_dbg(&tz->device, "temperature input lookup failed!\n");
+ return;
+ }
+
+ device_remove_file(hwmon->device, &temp->temp_input.attr);
if (tz->ops->get_crit_temp)
- device_remove_file(hwmon->device, &tz->temp_crit.attr);
+ device_remove_file(hwmon->device, &temp->temp_crit.attr);
mutex_lock(&thermal_list_lock);
- list_del(&tz->hwmon_node);
+ list_del(&temp->hwmon_node);
+ kfree(temp);
if (!list_empty(&hwmon->tz_list)) {
mutex_unlock(&thermal_list_lock);
return;
diff --git a/drivers/tty/serial/Kconfig b/drivers/tty/serial/Kconfig
index cb40b82..4dcb37b 100644
--- a/drivers/tty/serial/Kconfig
+++ b/drivers/tty/serial/Kconfig
@@ -959,7 +959,7 @@
config SERIAL_SH_SCI
tristate "SuperH SCI(F) serial port support"
- depends on HAVE_CLK && (SUPERH || H8300 || ARCH_SHMOBILE)
+ depends on HAVE_CLK && (SUPERH || ARCH_SHMOBILE)
select SERIAL_CORE
config SERIAL_SH_SCI_NR_UARTS
diff --git a/drivers/tty/serial/imx.c b/drivers/tty/serial/imx.c
index 827db76..7e91b3d 100644
--- a/drivers/tty/serial/imx.c
+++ b/drivers/tty/serial/imx.c
@@ -1286,22 +1286,17 @@
static int serial_imx_probe_dt(struct imx_port *sport,
struct platform_device *pdev)
{
+ static int portnum = 0;
struct device_node *np = pdev->dev.of_node;
const struct of_device_id *of_id =
of_match_device(imx_uart_dt_ids, &pdev->dev);
- int ret;
if (!np)
return -ENODEV;
- ret = of_alias_get_id(np, "serial");
- if (ret < 0) {
- pr_err("%s: failed to get alias id, errno %d\n",
- __func__, ret);
- return -ENODEV;
- } else {
- sport->port.line = ret;
- }
+ sport->port.line = portnum++;
+ if (sport->port.line >= UART_NR)
+ return -EINVAL;
if (of_get_property(np, "fsl,uart-has-rtscts", NULL))
sport->have_rtscts = 1;
diff --git a/drivers/tty/serial/sh-sci.c b/drivers/tty/serial/sh-sci.c
index ebd8629..2ec57b2 100644
--- a/drivers/tty/serial/sh-sci.c
+++ b/drivers/tty/serial/sh-sci.c
@@ -54,10 +54,6 @@
#include <asm/sh_bios.h>
#endif
-#ifdef CONFIG_H8300
-#include <asm/gpio.h>
-#endif
-
#include "sh-sci.h"
struct sci_port {
@@ -66,12 +62,6 @@
/* Platform configuration */
struct plat_sci_port *cfg;
- /* Port enable callback */
- void (*enable)(struct uart_port *port);
-
- /* Port disable callback */
- void (*disable)(struct uart_port *port);
-
/* Break timer */
struct timer_list break_timer;
int break_flag;
@@ -81,6 +71,8 @@
/* Function clock */
struct clk *fclk;
+ char *irqstr[SCIx_NR_IRQS];
+
struct dma_chan *chan_tx;
struct dma_chan *chan_rx;
@@ -121,6 +113,278 @@
return container_of(uart, struct sci_port, port);
}
+struct plat_sci_reg {
+ u8 offset, size;
+};
+
+/* Helper for invalidating specific entries of an inherited map. */
+#define sci_reg_invalid { .offset = 0, .size = 0 }
+
+static struct plat_sci_reg sci_regmap[SCIx_NR_REGTYPES][SCIx_NR_REGS] = {
+ [SCIx_PROBE_REGTYPE] = {
+ [0 ... SCIx_NR_REGS - 1] = sci_reg_invalid,
+ },
+
+ /*
+ * Common SCI definitions, dependent on the port's regshift
+ * value.
+ */
+ [SCIx_SCI_REGTYPE] = {
+ [SCSMR] = { 0x00, 8 },
+ [SCBRR] = { 0x01, 8 },
+ [SCSCR] = { 0x02, 8 },
+ [SCxTDR] = { 0x03, 8 },
+ [SCxSR] = { 0x04, 8 },
+ [SCxRDR] = { 0x05, 8 },
+ [SCFCR] = sci_reg_invalid,
+ [SCFDR] = sci_reg_invalid,
+ [SCTFDR] = sci_reg_invalid,
+ [SCRFDR] = sci_reg_invalid,
+ [SCSPTR] = sci_reg_invalid,
+ [SCLSR] = sci_reg_invalid,
+ },
+
+ /*
+ * Common definitions for legacy IrDA ports, dependent on
+ * regshift value.
+ */
+ [SCIx_IRDA_REGTYPE] = {
+ [SCSMR] = { 0x00, 8 },
+ [SCBRR] = { 0x01, 8 },
+ [SCSCR] = { 0x02, 8 },
+ [SCxTDR] = { 0x03, 8 },
+ [SCxSR] = { 0x04, 8 },
+ [SCxRDR] = { 0x05, 8 },
+ [SCFCR] = { 0x06, 8 },
+ [SCFDR] = { 0x07, 16 },
+ [SCTFDR] = sci_reg_invalid,
+ [SCRFDR] = sci_reg_invalid,
+ [SCSPTR] = sci_reg_invalid,
+ [SCLSR] = sci_reg_invalid,
+ },
+
+ /*
+ * Common SCIFA definitions.
+ */
+ [SCIx_SCIFA_REGTYPE] = {
+ [SCSMR] = { 0x00, 16 },
+ [SCBRR] = { 0x04, 8 },
+ [SCSCR] = { 0x08, 16 },
+ [SCxTDR] = { 0x20, 8 },
+ [SCxSR] = { 0x14, 16 },
+ [SCxRDR] = { 0x24, 8 },
+ [SCFCR] = { 0x18, 16 },
+ [SCFDR] = { 0x1c, 16 },
+ [SCTFDR] = sci_reg_invalid,
+ [SCRFDR] = sci_reg_invalid,
+ [SCSPTR] = sci_reg_invalid,
+ [SCLSR] = sci_reg_invalid,
+ },
+
+ /*
+ * Common SCIFB definitions.
+ */
+ [SCIx_SCIFB_REGTYPE] = {
+ [SCSMR] = { 0x00, 16 },
+ [SCBRR] = { 0x04, 8 },
+ [SCSCR] = { 0x08, 16 },
+ [SCxTDR] = { 0x40, 8 },
+ [SCxSR] = { 0x14, 16 },
+ [SCxRDR] = { 0x60, 8 },
+ [SCFCR] = { 0x18, 16 },
+ [SCFDR] = { 0x1c, 16 },
+ [SCTFDR] = sci_reg_invalid,
+ [SCRFDR] = sci_reg_invalid,
+ [SCSPTR] = sci_reg_invalid,
+ [SCLSR] = sci_reg_invalid,
+ },
+
+ /*
+ * Common SH-3 SCIF definitions.
+ */
+ [SCIx_SH3_SCIF_REGTYPE] = {
+ [SCSMR] = { 0x00, 8 },
+ [SCBRR] = { 0x02, 8 },
+ [SCSCR] = { 0x04, 8 },
+ [SCxTDR] = { 0x06, 8 },
+ [SCxSR] = { 0x08, 16 },
+ [SCxRDR] = { 0x0a, 8 },
+ [SCFCR] = { 0x0c, 8 },
+ [SCFDR] = { 0x0e, 16 },
+ [SCTFDR] = sci_reg_invalid,
+ [SCRFDR] = sci_reg_invalid,
+ [SCSPTR] = sci_reg_invalid,
+ [SCLSR] = sci_reg_invalid,
+ },
+
+ /*
+ * Common SH-4(A) SCIF(B) definitions.
+ */
+ [SCIx_SH4_SCIF_REGTYPE] = {
+ [SCSMR] = { 0x00, 16 },
+ [SCBRR] = { 0x04, 8 },
+ [SCSCR] = { 0x08, 16 },
+ [SCxTDR] = { 0x0c, 8 },
+ [SCxSR] = { 0x10, 16 },
+ [SCxRDR] = { 0x14, 8 },
+ [SCFCR] = { 0x18, 16 },
+ [SCFDR] = { 0x1c, 16 },
+ [SCTFDR] = sci_reg_invalid,
+ [SCRFDR] = sci_reg_invalid,
+ [SCSPTR] = { 0x20, 16 },
+ [SCLSR] = { 0x24, 16 },
+ },
+
+ /*
+ * Common SH-4(A) SCIF(B) definitions for ports without an SCSPTR
+ * register.
+ */
+ [SCIx_SH4_SCIF_NO_SCSPTR_REGTYPE] = {
+ [SCSMR] = { 0x00, 16 },
+ [SCBRR] = { 0x04, 8 },
+ [SCSCR] = { 0x08, 16 },
+ [SCxTDR] = { 0x0c, 8 },
+ [SCxSR] = { 0x10, 16 },
+ [SCxRDR] = { 0x14, 8 },
+ [SCFCR] = { 0x18, 16 },
+ [SCFDR] = { 0x1c, 16 },
+ [SCTFDR] = sci_reg_invalid,
+ [SCRFDR] = sci_reg_invalid,
+ [SCSPTR] = sci_reg_invalid,
+ [SCLSR] = { 0x24, 16 },
+ },
+
+ /*
+ * Common SH-4(A) SCIF(B) definitions for ports with FIFO data
+ * count registers.
+ */
+ [SCIx_SH4_SCIF_FIFODATA_REGTYPE] = {
+ [SCSMR] = { 0x00, 16 },
+ [SCBRR] = { 0x04, 8 },
+ [SCSCR] = { 0x08, 16 },
+ [SCxTDR] = { 0x0c, 8 },
+ [SCxSR] = { 0x10, 16 },
+ [SCxRDR] = { 0x14, 8 },
+ [SCFCR] = { 0x18, 16 },
+ [SCFDR] = { 0x1c, 16 },
+ [SCTFDR] = { 0x1c, 16 }, /* aliased to SCFDR */
+ [SCRFDR] = { 0x20, 16 },
+ [SCSPTR] = { 0x24, 16 },
+ [SCLSR] = { 0x28, 16 },
+ },
+
+ /*
+ * SH7705-style SCIF(B) ports, lacking both SCSPTR and SCLSR
+ * registers.
+ */
+ [SCIx_SH7705_SCIF_REGTYPE] = {
+ [SCSMR] = { 0x00, 16 },
+ [SCBRR] = { 0x04, 8 },
+ [SCSCR] = { 0x08, 16 },
+ [SCxTDR] = { 0x20, 8 },
+ [SCxSR] = { 0x14, 16 },
+ [SCxRDR] = { 0x24, 8 },
+ [SCFCR] = { 0x18, 16 },
+ [SCFDR] = { 0x1c, 16 },
+ [SCTFDR] = sci_reg_invalid,
+ [SCRFDR] = sci_reg_invalid,
+ [SCSPTR] = sci_reg_invalid,
+ [SCLSR] = sci_reg_invalid,
+ },
+};
+
+#define sci_getreg(up, offset) (sci_regmap[to_sci_port(up)->cfg->regtype] + offset)
+
+/*
+ * The "offset" here is rather misleading, in that it refers to an enum
+ * value relative to the port mapping rather than the fixed offset
+ * itself, which needs to be manually retrieved from the platform's
+ * register map for the given port.
+ */
+static unsigned int sci_serial_in(struct uart_port *p, int offset)
+{
+ struct plat_sci_reg *reg = sci_getreg(p, offset);
+
+ if (reg->size == 8)
+ return ioread8(p->membase + (reg->offset << p->regshift));
+ else if (reg->size == 16)
+ return ioread16(p->membase + (reg->offset << p->regshift));
+ else
+ WARN(1, "Invalid register access\n");
+
+ return 0;
+}
+
+static void sci_serial_out(struct uart_port *p, int offset, int value)
+{
+ struct plat_sci_reg *reg = sci_getreg(p, offset);
+
+ if (reg->size == 8)
+ iowrite8(value, p->membase + (reg->offset << p->regshift));
+ else if (reg->size == 16)
+ iowrite16(value, p->membase + (reg->offset << p->regshift));
+ else
+ WARN(1, "Invalid register access\n");
+}
+
+#define sci_in(up, offset) (up->serial_in(up, offset))
+#define sci_out(up, offset, value) (up->serial_out(up, offset, value))
+
+static int sci_probe_regmap(struct plat_sci_port *cfg)
+{
+ switch (cfg->type) {
+ case PORT_SCI:
+ cfg->regtype = SCIx_SCI_REGTYPE;
+ break;
+ case PORT_IRDA:
+ cfg->regtype = SCIx_IRDA_REGTYPE;
+ break;
+ case PORT_SCIFA:
+ cfg->regtype = SCIx_SCIFA_REGTYPE;
+ break;
+ case PORT_SCIFB:
+ cfg->regtype = SCIx_SCIFB_REGTYPE;
+ break;
+ case PORT_SCIF:
+ /*
+ * The SH-4 is a bit of a misnomer here, although that's
+ * where this particular port layout originated. This
+ * configuration (or some slight variation thereof)
+ * remains the dominant model for all SCIFs.
+ */
+ cfg->regtype = SCIx_SH4_SCIF_REGTYPE;
+ break;
+ default:
+ printk(KERN_ERR "Can't probe register map for given port\n");
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static void sci_port_enable(struct sci_port *sci_port)
+{
+ if (!sci_port->port.dev)
+ return;
+
+ pm_runtime_get_sync(sci_port->port.dev);
+
+ clk_enable(sci_port->iclk);
+ sci_port->port.uartclk = clk_get_rate(sci_port->iclk);
+ clk_enable(sci_port->fclk);
+}
+
+static void sci_port_disable(struct sci_port *sci_port)
+{
+ if (!sci_port->port.dev)
+ return;
+
+ clk_disable(sci_port->fclk);
+ clk_disable(sci_port->iclk);
+
+ pm_runtime_put_sync(sci_port->port.dev);
+}
+
#if defined(CONFIG_CONSOLE_POLL) || defined(CONFIG_SERIAL_SH_SCI_CONSOLE)
#ifdef CONFIG_CONSOLE_POLL
@@ -164,225 +428,78 @@
}
#endif /* CONFIG_CONSOLE_POLL || CONFIG_SERIAL_SH_SCI_CONSOLE */
-#if defined(__H8300H__) || defined(__H8300S__)
static void sci_init_pins(struct uart_port *port, unsigned int cflag)
{
- int ch = (port->mapbase - SMR0) >> 3;
+ struct sci_port *s = to_sci_port(port);
+ struct plat_sci_reg *reg = sci_regmap[s->cfg->regtype] + SCSPTR;
- /* set DDR regs */
- H8300_GPIO_DDR(h8300_sci_pins[ch].port,
- h8300_sci_pins[ch].rx,
- H8300_GPIO_INPUT);
- H8300_GPIO_DDR(h8300_sci_pins[ch].port,
- h8300_sci_pins[ch].tx,
- H8300_GPIO_OUTPUT);
-
- /* tx mark output*/
- H8300_SCI_DR(ch) |= h8300_sci_pins[ch].tx;
-}
-#elif defined(CONFIG_CPU_SUBTYPE_SH7710) || defined(CONFIG_CPU_SUBTYPE_SH7712)
-static inline void sci_init_pins(struct uart_port *port, unsigned int cflag)
-{
- if (port->mapbase == 0xA4400000) {
- __raw_writew(__raw_readw(PACR) & 0xffc0, PACR);
- __raw_writew(__raw_readw(PBCR) & 0x0fff, PBCR);
- } else if (port->mapbase == 0xA4410000)
- __raw_writew(__raw_readw(PBCR) & 0xf003, PBCR);
-}
-#elif defined(CONFIG_CPU_SUBTYPE_SH7720) || defined(CONFIG_CPU_SUBTYPE_SH7721)
-static inline void sci_init_pins(struct uart_port *port, unsigned int cflag)
-{
- unsigned short data;
-
- if (cflag & CRTSCTS) {
- /* enable RTS/CTS */
- if (port->mapbase == 0xa4430000) { /* SCIF0 */
- /* Clear PTCR bit 9-2; enable all scif pins but sck */
- data = __raw_readw(PORT_PTCR);
- __raw_writew((data & 0xfc03), PORT_PTCR);
- } else if (port->mapbase == 0xa4438000) { /* SCIF1 */
- /* Clear PVCR bit 9-2 */
- data = __raw_readw(PORT_PVCR);
- __raw_writew((data & 0xfc03), PORT_PVCR);
- }
- } else {
- if (port->mapbase == 0xa4430000) { /* SCIF0 */
- /* Clear PTCR bit 5-2; enable only tx and rx */
- data = __raw_readw(PORT_PTCR);
- __raw_writew((data & 0xffc3), PORT_PTCR);
- } else if (port->mapbase == 0xa4438000) { /* SCIF1 */
- /* Clear PVCR bit 5-2 */
- data = __raw_readw(PORT_PVCR);
- __raw_writew((data & 0xffc3), PORT_PVCR);
- }
+ /*
+ * Use port-specific handler if provided.
+ */
+ if (s->cfg->ops && s->cfg->ops->init_pins) {
+ s->cfg->ops->init_pins(port, cflag);
+ return;
}
-}
-#elif defined(CONFIG_CPU_SH3)
-/* For SH7705, SH7706, SH7707, SH7709, SH7709A, SH7729 */
-static inline void sci_init_pins(struct uart_port *port, unsigned int cflag)
-{
- unsigned short data;
- /* We need to set SCPCR to enable RTS/CTS */
- data = __raw_readw(SCPCR);
- /* Clear out SCP7MD1,0, SCP6MD1,0, SCP4MD1,0*/
- __raw_writew(data & 0x0fcf, SCPCR);
+ /*
+ * For the generic path SCSPTR is necessary. Bail out if that's
+ * unavailable, too.
+ */
+ if (!reg->size)
+ return;
- if (!(cflag & CRTSCTS)) {
- /* We need to set SCPCR to enable RTS/CTS */
- data = __raw_readw(SCPCR);
- /* Clear out SCP7MD1,0, SCP4MD1,0,
- Set SCP6MD1,0 = {01} (output) */
- __raw_writew((data & 0x0fcf) | 0x1000, SCPCR);
-
- data = __raw_readb(SCPDR);
- /* Set /RTS2 (bit6) = 0 */
- __raw_writeb(data & 0xbf, SCPDR);
- }
-}
-#elif defined(CONFIG_CPU_SUBTYPE_SH7722)
-static inline void sci_init_pins(struct uart_port *port, unsigned int cflag)
-{
- unsigned short data;
-
- if (port->mapbase == 0xffe00000) {
- data = __raw_readw(PSCR);
- data &= ~0x03cf;
- if (!(cflag & CRTSCTS))
- data |= 0x0340;
-
- __raw_writew(data, PSCR);
- }
-}
-#elif defined(CONFIG_CPU_SUBTYPE_SH7757) || \
- defined(CONFIG_CPU_SUBTYPE_SH7763) || \
- defined(CONFIG_CPU_SUBTYPE_SH7780) || \
- defined(CONFIG_CPU_SUBTYPE_SH7785) || \
- defined(CONFIG_CPU_SUBTYPE_SH7786) || \
- defined(CONFIG_CPU_SUBTYPE_SHX3)
-static inline void sci_init_pins(struct uart_port *port, unsigned int cflag)
-{
if (!(cflag & CRTSCTS))
- __raw_writew(0x0080, SCSPTR0); /* Set RTS = 1 */
+ sci_out(port, SCSPTR, 0x0080); /* Set RTS = 1 */
}
-#elif defined(CONFIG_CPU_SH4) && !defined(CONFIG_CPU_SH4A)
-static inline void sci_init_pins(struct uart_port *port, unsigned int cflag)
-{
- if (!(cflag & CRTSCTS))
- __raw_writew(0x0080, SCSPTR2); /* Set RTS = 1 */
-}
-#else
-static inline void sci_init_pins(struct uart_port *port, unsigned int cflag)
-{
- /* Nothing to do */
-}
-#endif
-
-#if defined(CONFIG_CPU_SUBTYPE_SH7760) || \
- defined(CONFIG_CPU_SUBTYPE_SH7780) || \
- defined(CONFIG_CPU_SUBTYPE_SH7785) || \
- defined(CONFIG_CPU_SUBTYPE_SH7786)
-static int scif_txfill(struct uart_port *port)
-{
- return sci_in(port, SCTFDR) & 0xff;
-}
-
-static int scif_txroom(struct uart_port *port)
-{
- return SCIF_TXROOM_MAX - scif_txfill(port);
-}
-
-static int scif_rxfill(struct uart_port *port)
-{
- return sci_in(port, SCRFDR) & 0xff;
-}
-#elif defined(CONFIG_CPU_SUBTYPE_SH7763)
-static int scif_txfill(struct uart_port *port)
-{
- if (port->mapbase == 0xffe00000 ||
- port->mapbase == 0xffe08000)
- /* SCIF0/1*/
- return sci_in(port, SCTFDR) & 0xff;
- else
- /* SCIF2 */
- return sci_in(port, SCFDR) >> 8;
-}
-
-static int scif_txroom(struct uart_port *port)
-{
- if (port->mapbase == 0xffe00000 ||
- port->mapbase == 0xffe08000)
- /* SCIF0/1*/
- return SCIF_TXROOM_MAX - scif_txfill(port);
- else
- /* SCIF2 */
- return SCIF2_TXROOM_MAX - scif_txfill(port);
-}
-
-static int scif_rxfill(struct uart_port *port)
-{
- if ((port->mapbase == 0xffe00000) ||
- (port->mapbase == 0xffe08000)) {
- /* SCIF0/1*/
- return sci_in(port, SCRFDR) & 0xff;
- } else {
- /* SCIF2 */
- return sci_in(port, SCFDR) & SCIF2_RFDC_MASK;
- }
-}
-#elif defined(CONFIG_ARCH_SH7372)
-static int scif_txfill(struct uart_port *port)
-{
- if (port->type == PORT_SCIFA)
- return sci_in(port, SCFDR) >> 8;
- else
- return sci_in(port, SCTFDR);
-}
-
-static int scif_txroom(struct uart_port *port)
-{
- return port->fifosize - scif_txfill(port);
-}
-
-static int scif_rxfill(struct uart_port *port)
-{
- if (port->type == PORT_SCIFA)
- return sci_in(port, SCFDR) & SCIF_RFDC_MASK;
- else
- return sci_in(port, SCRFDR);
-}
-#else
-static int scif_txfill(struct uart_port *port)
-{
- return sci_in(port, SCFDR) >> 8;
-}
-
-static int scif_txroom(struct uart_port *port)
-{
- return SCIF_TXROOM_MAX - scif_txfill(port);
-}
-
-static int scif_rxfill(struct uart_port *port)
-{
- return sci_in(port, SCFDR) & SCIF_RFDC_MASK;
-}
-#endif
static int sci_txfill(struct uart_port *port)
{
+ struct plat_sci_reg *reg;
+
+ reg = sci_getreg(port, SCTFDR);
+ if (reg->size)
+ return sci_in(port, SCTFDR) & 0xff;
+
+ reg = sci_getreg(port, SCFDR);
+ if (reg->size)
+ return sci_in(port, SCFDR) >> 8;
+
return !(sci_in(port, SCxSR) & SCI_TDRE);
}
static int sci_txroom(struct uart_port *port)
{
- return !sci_txfill(port);
+ return port->fifosize - sci_txfill(port);
}
static int sci_rxfill(struct uart_port *port)
{
+ struct plat_sci_reg *reg;
+
+ reg = sci_getreg(port, SCRFDR);
+ if (reg->size)
+ return sci_in(port, SCRFDR) & 0xff;
+
+ reg = sci_getreg(port, SCFDR);
+ if (reg->size)
+ return sci_in(port, SCFDR) & ((port->fifosize << 1) - 1);
+
return (sci_in(port, SCxSR) & SCxSR_RDxF(port)) != 0;
}
+/*
+ * SCI helper for checking the state of the muxed port/RXD pins.
+ */
+static inline int sci_rxd_in(struct uart_port *port)
+{
+ struct sci_port *s = to_sci_port(port);
+
+ if (s->cfg->port_reg <= 0)
+ return 1;
+
+ return !!__raw_readb(s->cfg->port_reg);
+}
+
/* ********************************************************************** *
* the interrupt related routines *
* ********************************************************************** */
@@ -406,10 +523,7 @@
return;
}
- if (port->type == PORT_SCI)
- count = sci_txroom(port);
- else
- count = scif_txroom(port);
+ count = sci_txroom(port);
do {
unsigned char c;
@@ -464,13 +578,8 @@
return;
while (1) {
- if (port->type == PORT_SCI)
- count = sci_rxfill(port);
- else
- count = scif_rxfill(port);
-
/* Don't copy more bytes than there is room for in the buffer */
- count = tty_buffer_request_room(tty, count);
+ count = tty_buffer_request_room(tty, sci_rxfill(port));
/* If for any reason we can't copy more data, we're done! */
if (count == 0)
@@ -561,8 +670,7 @@
{
struct sci_port *port = (struct sci_port *)data;
- if (port->enable)
- port->enable(&port->port);
+ sci_port_enable(port);
if (sci_rxd_in(&port->port) == 0) {
port->break_flag = 1;
@@ -574,8 +682,7 @@
} else
port->break_flag = 0;
- if (port->disable)
- port->disable(&port->port);
+ sci_port_disable(port);
}
static int sci_handle_errors(struct uart_port *port)
@@ -583,13 +690,19 @@
int copied = 0;
unsigned short status = sci_in(port, SCxSR);
struct tty_struct *tty = port->state->port.tty;
+ struct sci_port *s = to_sci_port(port);
- if (status & SCxSR_ORER(port)) {
- /* overrun error */
- if (tty_insert_flip_char(tty, 0, TTY_OVERRUN))
- copied++;
+ /*
+ * Handle overruns, if supported.
+ */
+ if (s->cfg->overrun_bit != SCIx_NOT_SUPPORTED) {
+ if (status & (1 << s->cfg->overrun_bit)) {
+ /* overrun error */
+ if (tty_insert_flip_char(tty, 0, TTY_OVERRUN))
+ copied++;
- dev_notice(port->dev, "overrun error");
+ dev_notice(port->dev, "overrun error");
+ }
}
if (status & SCxSR_FER(port)) {
@@ -637,12 +750,15 @@
static int sci_handle_fifo_overrun(struct uart_port *port)
{
struct tty_struct *tty = port->state->port.tty;
+ struct sci_port *s = to_sci_port(port);
+ struct plat_sci_reg *reg;
int copied = 0;
- if (port->type != PORT_SCIF)
+ reg = sci_getreg(port, SCLSR);
+ if (!reg->size)
return 0;
- if ((sci_in(port, SCLSR) & SCIF_ORER) != 0) {
+ if ((sci_in(port, SCLSR) & (1 << s->cfg->overrun_bit))) {
sci_out(port, SCLSR, 0);
tty_insert_flip_char(tty, 0, TTY_OVERRUN);
@@ -840,74 +956,102 @@
return NOTIFY_OK;
}
-static void sci_clk_enable(struct uart_port *port)
-{
- struct sci_port *sci_port = to_sci_port(port);
+static struct sci_irq_desc {
+ const char *desc;
+ irq_handler_t handler;
+} sci_irq_desc[] = {
+ /*
+ * Split out handlers, the default case.
+ */
+ [SCIx_ERI_IRQ] = {
+ .desc = "rx err",
+ .handler = sci_er_interrupt,
+ },
- pm_runtime_get_sync(port->dev);
+ [SCIx_RXI_IRQ] = {
+ .desc = "rx full",
+ .handler = sci_rx_interrupt,
+ },
- clk_enable(sci_port->iclk);
- sci_port->port.uartclk = clk_get_rate(sci_port->iclk);
- clk_enable(sci_port->fclk);
-}
+ [SCIx_TXI_IRQ] = {
+ .desc = "tx empty",
+ .handler = sci_tx_interrupt,
+ },
-static void sci_clk_disable(struct uart_port *port)
-{
- struct sci_port *sci_port = to_sci_port(port);
+ [SCIx_BRI_IRQ] = {
+ .desc = "break",
+ .handler = sci_br_interrupt,
+ },
- clk_disable(sci_port->fclk);
- clk_disable(sci_port->iclk);
-
- pm_runtime_put_sync(port->dev);
-}
+ /*
+ * Special muxed handler.
+ */
+ [SCIx_MUX_IRQ] = {
+ .desc = "mux",
+ .handler = sci_mpxed_interrupt,
+ },
+};
static int sci_request_irq(struct sci_port *port)
{
- int i;
- irqreturn_t (*handlers[4])(int irq, void *ptr) = {
- sci_er_interrupt, sci_rx_interrupt, sci_tx_interrupt,
- sci_br_interrupt,
- };
- const char *desc[] = { "SCI Receive Error", "SCI Receive Data Full",
- "SCI Transmit Data Empty", "SCI Break" };
+ struct uart_port *up = &port->port;
+ int i, j, ret = 0;
- if (port->cfg->irqs[0] == port->cfg->irqs[1]) {
- if (unlikely(!port->cfg->irqs[0]))
- return -ENODEV;
+ for (i = j = 0; i < SCIx_NR_IRQS; i++, j++) {
+ struct sci_irq_desc *desc;
+ unsigned int irq;
- if (request_irq(port->cfg->irqs[0], sci_mpxed_interrupt,
- IRQF_DISABLED, "sci", port)) {
- dev_err(port->port.dev, "Can't allocate IRQ\n");
- return -ENODEV;
+ if (SCIx_IRQ_IS_MUXED(port)) {
+ i = SCIx_MUX_IRQ;
+ irq = up->irq;
+ } else
+ irq = port->cfg->irqs[i];
+
+ desc = sci_irq_desc + i;
+ port->irqstr[j] = kasprintf(GFP_KERNEL, "%s:%s",
+ dev_name(up->dev), desc->desc);
+ if (!port->irqstr[j]) {
+ dev_err(up->dev, "Failed to allocate %s IRQ string\n",
+ desc->desc);
+ goto out_nomem;
}
- } else {
- for (i = 0; i < ARRAY_SIZE(handlers); i++) {
- if (unlikely(!port->cfg->irqs[i]))
- continue;
- if (request_irq(port->cfg->irqs[i], handlers[i],
- IRQF_DISABLED, desc[i], port)) {
- dev_err(port->port.dev, "Can't allocate IRQ\n");
- return -ENODEV;
- }
+ ret = request_irq(irq, desc->handler, up->irqflags,
+ port->irqstr[j], port);
+ if (unlikely(ret)) {
+ dev_err(up->dev, "Can't allocate %s IRQ\n", desc->desc);
+ goto out_noirq;
}
}
return 0;
+
+out_noirq:
+ while (--i >= 0)
+ free_irq(port->cfg->irqs[i], port);
+
+out_nomem:
+ while (--j >= 0)
+ kfree(port->irqstr[j]);
+
+ return ret;
}
static void sci_free_irq(struct sci_port *port)
{
int i;
- if (port->cfg->irqs[0] == port->cfg->irqs[1])
- free_irq(port->cfg->irqs[0], port);
- else {
- for (i = 0; i < ARRAY_SIZE(port->cfg->irqs); i++) {
- if (!port->cfg->irqs[i])
- continue;
+ /*
+ * Intentionally in reverse order so we iterate over the muxed
+ * IRQ first.
+ */
+ for (i = 0; i < SCIx_NR_IRQS; i++) {
+ free_irq(port->cfg->irqs[i], port);
+ kfree(port->irqstr[i]);
- free_irq(port->cfg->irqs[i], port);
+ if (SCIx_IRQ_IS_MUXED(port)) {
+ /* If there's only one IRQ, we're done. */
+ return;
}
}
}
@@ -915,7 +1059,7 @@
static unsigned int sci_tx_empty(struct uart_port *port)
{
unsigned short status = sci_in(port, SCxSR);
- unsigned short in_tx_fifo = scif_txfill(port);
+ unsigned short in_tx_fifo = sci_txfill(port);
return (status & SCxSR_TEND(port)) && !in_tx_fifo ? TIOCSER_TEMT : 0;
}
@@ -1438,8 +1582,7 @@
dev_dbg(port->dev, "%s(%d)\n", __func__, port->line);
- if (s->enable)
- s->enable(port);
+ sci_port_enable(s);
ret = sci_request_irq(s);
if (unlikely(ret < 0))
@@ -1465,8 +1608,7 @@
sci_free_dma(port);
sci_free_irq(s);
- if (s->disable)
- s->disable(port);
+ sci_port_disable(s);
}
static unsigned int sci_scbrr_calc(unsigned int algo_id, unsigned int bps,
@@ -1513,8 +1655,7 @@
if (likely(baud && port->uartclk))
t = sci_scbrr_calc(s->cfg->scbrr_algo_id, baud, port->uartclk);
- if (s->enable)
- s->enable(port);
+ sci_port_enable(s);
do {
status = sci_in(port, SCxSR);
@@ -1584,8 +1725,7 @@
if ((termios->c_cflag & CREAD) != 0)
sci_start_rx(port);
- if (s->disable)
- s->disable(port);
+ sci_port_disable(s);
}
static const char *sci_type(struct uart_port *port)
@@ -1726,6 +1866,7 @@
struct plat_sci_port *p)
{
struct uart_port *port = &sci_port->port;
+ int ret;
port->ops = &sci_uart_ops;
port->iotype = UPIO_MEM;
@@ -1746,6 +1887,12 @@
break;
}
+ if (p->regtype == SCIx_PROBE_REGTYPE) {
+ ret = sci_probe_regmap(p);
+ if (unlikely(ret))
+ return ret;
+ }
+
if (dev) {
sci_port->iclk = clk_get(&dev->dev, "sci_ick");
if (IS_ERR(sci_port->iclk)) {
@@ -1764,8 +1911,6 @@
if (IS_ERR(sci_port->fclk))
sci_port->fclk = NULL;
- sci_port->enable = sci_clk_enable;
- sci_port->disable = sci_clk_disable;
port->dev = &dev->dev;
pm_runtime_enable(&dev->dev);
@@ -1775,20 +1920,51 @@
sci_port->break_timer.function = sci_break_timer;
init_timer(&sci_port->break_timer);
+ /*
+ * Establish some sensible defaults for the error detection.
+ */
+ if (!p->error_mask)
+ p->error_mask = (p->type == PORT_SCI) ?
+ SCI_DEFAULT_ERROR_MASK : SCIF_DEFAULT_ERROR_MASK;
+
+ /*
+ * Establish sensible defaults for the overrun detection, unless
+ * the part has explicitly disabled support for it.
+ */
+ if (p->overrun_bit != SCIx_NOT_SUPPORTED) {
+ if (p->type == PORT_SCI)
+ p->overrun_bit = 5;
+ else if (p->scbrr_algo_id == SCBRR_ALGO_4)
+ p->overrun_bit = 9;
+ else
+ p->overrun_bit = 0;
+
+ /*
+ * Make the error mask inclusive of overrun detection, if
+ * supported.
+ */
+ p->error_mask |= (1 << p->overrun_bit);
+ }
+
sci_port->cfg = p;
port->mapbase = p->mapbase;
port->type = p->type;
port->flags = p->flags;
+ port->regshift = p->regshift;
/*
- * The UART port needs an IRQ value, so we peg this to the TX IRQ
+ * The UART port needs an IRQ value, so we peg this to the RX IRQ
* for the multi-IRQ ports, which is where we are primarily
* concerned with the shutdown path synchronization.
*
* For the muxed case there's nothing more to do.
*/
port->irq = p->irqs[SCIx_RXI_IRQ];
+ port->irqflags = IRQF_DISABLED;
+
+ port->serial_in = sci_serial_in;
+ port->serial_out = sci_serial_out;
if (p->dma_dev)
dev_dbg(port->dev, "DMA device %p, tx %d, rx %d\n",
@@ -1814,8 +1990,7 @@
struct uart_port *port = &sci_port->port;
unsigned short bits;
- if (sci_port->enable)
- sci_port->enable(port);
+ sci_port_enable(sci_port);
uart_console_write(port, s, count, serial_console_putchar);
@@ -1824,8 +1999,7 @@
while ((sci_in(port, SCxSR) & bits) != bits)
cpu_relax();
- if (sci_port->disable)
- sci_port->disable(port);
+ sci_port_disable(sci_port);
}
static int __devinit serial_console_setup(struct console *co, char *options)
@@ -1857,20 +2031,13 @@
if (unlikely(ret != 0))
return ret;
- if (sci_port->enable)
- sci_port->enable(port);
+ sci_port_enable(sci_port);
if (options)
uart_parse_options(options, &baud, &parity, &bits, &flow);
- ret = uart_set_options(port, co, baud, parity, bits, flow);
-#if defined(__H8300H__) || defined(__H8300S__)
- /* disable rx interrupt */
- if (ret == 0)
- sci_stop_rx(port);
-#endif
/* TODO: disable clock */
- return ret;
+ return uart_set_options(port, co, baud, parity, bits, flow);
}
static struct console serial_console = {
@@ -2081,3 +2248,5 @@
MODULE_LICENSE("GPL");
MODULE_ALIAS("platform:sh-sci");
+MODULE_AUTHOR("Paul Mundt");
+MODULE_DESCRIPTION("SuperH SCI(F) serial driver");
diff --git a/drivers/tty/serial/sh-sci.h b/drivers/tty/serial/sh-sci.h
index b04d937..e9bed03 100644
--- a/drivers/tty/serial/sh-sci.h
+++ b/drivers/tty/serial/sh-sci.h
@@ -2,169 +2,14 @@
#include <linux/io.h>
#include <linux/gpio.h>
-#if defined(CONFIG_H83007) || defined(CONFIG_H83068)
-#include <asm/regs306x.h>
-#endif
-#if defined(CONFIG_H8S2678)
-#include <asm/regs267x.h>
-#endif
-
-#if defined(CONFIG_CPU_SUBTYPE_SH7706) || \
- defined(CONFIG_CPU_SUBTYPE_SH7707) || \
- defined(CONFIG_CPU_SUBTYPE_SH7708) || \
- defined(CONFIG_CPU_SUBTYPE_SH7709)
-# define SCPCR 0xA4000116 /* 16 bit SCI and SCIF */
-# define SCPDR 0xA4000136 /* 8 bit SCI and SCIF */
-#elif defined(CONFIG_CPU_SUBTYPE_SH7705)
-# define SCIF0 0xA4400000
-# define SCIF2 0xA4410000
-# define SCPCR 0xA4000116
-# define SCPDR 0xA4000136
-#elif defined(CONFIG_CPU_SUBTYPE_SH7720) || \
- defined(CONFIG_CPU_SUBTYPE_SH7721) || \
- defined(CONFIG_ARCH_SH73A0) || \
- defined(CONFIG_ARCH_SH7367) || \
- defined(CONFIG_ARCH_SH7377) || \
- defined(CONFIG_ARCH_SH7372)
-# define PORT_PTCR 0xA405011EUL
-# define PORT_PVCR 0xA4050122UL
-# define SCIF_ORER 0x0200 /* overrun error bit */
-#elif defined(CONFIG_SH_RTS7751R2D)
-# define SCSPTR1 0xFFE0001C /* 8 bit SCIF */
-# define SCSPTR2 0xFFE80020 /* 16 bit SCIF */
-# define SCIF_ORER 0x0001 /* overrun error bit */
-#elif defined(CONFIG_CPU_SUBTYPE_SH7750) || \
- defined(CONFIG_CPU_SUBTYPE_SH7750R) || \
- defined(CONFIG_CPU_SUBTYPE_SH7750S) || \
- defined(CONFIG_CPU_SUBTYPE_SH7091) || \
- defined(CONFIG_CPU_SUBTYPE_SH7751) || \
- defined(CONFIG_CPU_SUBTYPE_SH7751R)
-# define SCSPTR1 0xffe0001c /* 8 bit SCI */
-# define SCSPTR2 0xFFE80020 /* 16 bit SCIF */
-# define SCIF_ORER 0x0001 /* overrun error bit */
-#elif defined(CONFIG_CPU_SUBTYPE_SH7760)
-# define SCSPTR0 0xfe600024 /* 16 bit SCIF */
-# define SCSPTR1 0xfe610024 /* 16 bit SCIF */
-# define SCSPTR2 0xfe620024 /* 16 bit SCIF */
-# define SCIF_ORER 0x0001 /* overrun error bit */
-#elif defined(CONFIG_CPU_SUBTYPE_SH7710) || defined(CONFIG_CPU_SUBTYPE_SH7712)
-# define SCSPTR0 0xA4400000 /* 16 bit SCIF */
-# define SCIF_ORER 0x0001 /* overrun error bit */
-# define PACR 0xa4050100
-# define PBCR 0xa4050102
-#elif defined(CONFIG_CPU_SUBTYPE_SH7343)
-# define SCSPTR0 0xffe00010 /* 16 bit SCIF */
-#elif defined(CONFIG_CPU_SUBTYPE_SH7722)
-# define PADR 0xA4050120
-# define PSDR 0xA405013e
-# define PWDR 0xA4050166
-# define PSCR 0xA405011E
-# define SCIF_ORER 0x0001 /* overrun error bit */
-#elif defined(CONFIG_CPU_SUBTYPE_SH7366)
-# define SCPDR0 0xA405013E /* 16 bit SCIF0 PSDR */
-# define SCSPTR0 SCPDR0
-# define SCIF_ORER 0x0001 /* overrun error bit */
-#elif defined(CONFIG_CPU_SUBTYPE_SH7723)
-# define SCSPTR0 0xa4050160
-# define SCIF_ORER 0x0001 /* overrun error bit */
-#elif defined(CONFIG_CPU_SUBTYPE_SH7724)
-# define SCIF_ORER 0x0001 /* overrun error bit */
-#elif defined(CONFIG_CPU_SUBTYPE_SH4_202)
-# define SCSPTR2 0xffe80020 /* 16 bit SCIF */
-# define SCIF_ORER 0x0001 /* overrun error bit */
-#elif defined(CONFIG_H83007) || defined(CONFIG_H83068)
-# define H8300_SCI_DR(ch) *(volatile char *)(P1DR + h8300_sci_pins[ch].port)
-#elif defined(CONFIG_H8S2678)
-# define H8300_SCI_DR(ch) *(volatile char *)(P1DR + h8300_sci_pins[ch].port)
-#elif defined(CONFIG_CPU_SUBTYPE_SH7757)
-# define SCSPTR0 0xfe4b0020
-# define SCIF_ORER 0x0001
-#elif defined(CONFIG_CPU_SUBTYPE_SH7763)
-# define SCSPTR0 0xffe00024 /* 16 bit SCIF */
-# define SCIF_ORER 0x0001 /* overrun error bit */
-#elif defined(CONFIG_CPU_SUBTYPE_SH7770)
-# define SCSPTR0 0xff923020 /* 16 bit SCIF */
-# define SCIF_ORER 0x0001 /* overrun error bit */
-#elif defined(CONFIG_CPU_SUBTYPE_SH7780)
-# define SCSPTR0 0xffe00024 /* 16 bit SCIF */
-# define SCIF_ORER 0x0001 /* Overrun error bit */
-#elif defined(CONFIG_CPU_SUBTYPE_SH7785) || \
- defined(CONFIG_CPU_SUBTYPE_SH7786)
-# define SCSPTR0 0xffea0024 /* 16 bit SCIF */
-# define SCIF_ORER 0x0001 /* Overrun error bit */
-#elif defined(CONFIG_CPU_SUBTYPE_SH7201) || \
- defined(CONFIG_CPU_SUBTYPE_SH7203) || \
- defined(CONFIG_CPU_SUBTYPE_SH7206) || \
- defined(CONFIG_CPU_SUBTYPE_SH7263)
-# define SCSPTR0 0xfffe8020 /* 16 bit SCIF */
-#elif defined(CONFIG_CPU_SUBTYPE_SH7619)
-# define SCSPTR0 0xf8400020 /* 16 bit SCIF */
-# define SCIF_ORER 0x0001 /* overrun error bit */
-#elif defined(CONFIG_CPU_SUBTYPE_SHX3)
-# define SCSPTR0 0xffc30020 /* 16 bit SCIF */
-# define SCIF_ORER 0x0001 /* Overrun error bit */
-#else
-# error CPU subtype not defined
-#endif
-
-/* SCxSR SCI */
-#define SCI_TDRE 0x80 /* 7707 SCI, 7708 SCI, 7709 SCI, 7750 SCI */
-#define SCI_RDRF 0x40 /* 7707 SCI, 7708 SCI, 7709 SCI, 7750 SCI */
-#define SCI_ORER 0x20 /* 7707 SCI, 7708 SCI, 7709 SCI, 7750 SCI */
-#define SCI_FER 0x10 /* 7707 SCI, 7708 SCI, 7709 SCI, 7750 SCI */
-#define SCI_PER 0x08 /* 7707 SCI, 7708 SCI, 7709 SCI, 7750 SCI */
-#define SCI_TEND 0x04 /* 7707 SCI, 7708 SCI, 7709 SCI, 7750 SCI */
-/* SCI_MPB 0x02 * 7707 SCI, 7708 SCI, 7709 SCI, 7750 SCI */
-/* SCI_MPBT 0x01 * 7707 SCI, 7708 SCI, 7709 SCI, 7750 SCI */
-
-#define SCI_ERRORS ( SCI_PER | SCI_FER | SCI_ORER)
-
-/* SCxSR SCIF */
-#define SCIF_ER 0x0080 /* 7705 SCIF, 7707 SCIF, 7709 SCIF, 7750 SCIF */
-#define SCIF_TEND 0x0040 /* 7705 SCIF, 7707 SCIF, 7709 SCIF, 7750 SCIF */
-#define SCIF_TDFE 0x0020 /* 7705 SCIF, 7707 SCIF, 7709 SCIF, 7750 SCIF */
-#define SCIF_BRK 0x0010 /* 7705 SCIF, 7707 SCIF, 7709 SCIF, 7750 SCIF */
-#define SCIF_FER 0x0008 /* 7705 SCIF, 7707 SCIF, 7709 SCIF, 7750 SCIF */
-#define SCIF_PER 0x0004 /* 7705 SCIF, 7707 SCIF, 7709 SCIF, 7750 SCIF */
-#define SCIF_RDF 0x0002 /* 7705 SCIF, 7707 SCIF, 7709 SCIF, 7750 SCIF */
-#define SCIF_DR 0x0001 /* 7705 SCIF, 7707 SCIF, 7709 SCIF, 7750 SCIF */
-
-#if defined(CONFIG_CPU_SUBTYPE_SH7705) || \
- defined(CONFIG_CPU_SUBTYPE_SH7720) || \
- defined(CONFIG_CPU_SUBTYPE_SH7721) || \
- defined(CONFIG_ARCH_SH73A0) || \
- defined(CONFIG_ARCH_SH7367) || \
- defined(CONFIG_ARCH_SH7377) || \
- defined(CONFIG_ARCH_SH7372)
-# define SCIF_ORER 0x0200
-# define SCIF_ERRORS ( SCIF_PER | SCIF_FER | SCIF_ER | SCIF_BRK | SCIF_ORER)
-# define SCIF_RFDC_MASK 0x007f
-# define SCIF_TXROOM_MAX 64
-#elif defined(CONFIG_CPU_SUBTYPE_SH7763)
-# define SCIF_ERRORS ( SCIF_PER | SCIF_FER | SCIF_ER | SCIF_BRK )
-# define SCIF_RFDC_MASK 0x007f
-# define SCIF_TXROOM_MAX 64
-/* SH7763 SCIF2 support */
-# define SCIF2_RFDC_MASK 0x001f
-# define SCIF2_TXROOM_MAX 16
-#else
-# define SCIF_ERRORS ( SCIF_PER | SCIF_FER | SCIF_ER | SCIF_BRK)
-# define SCIF_RFDC_MASK 0x001f
-# define SCIF_TXROOM_MAX 16
-#endif
-
-#ifndef SCIF_ORER
-#define SCIF_ORER 0x0000
-#endif
-
#define SCxSR_TEND(port) (((port)->type == PORT_SCI) ? SCI_TEND : SCIF_TEND)
-#define SCxSR_ERRORS(port) (((port)->type == PORT_SCI) ? SCI_ERRORS : SCIF_ERRORS)
#define SCxSR_RDxF(port) (((port)->type == PORT_SCI) ? SCI_RDRF : SCIF_RDF)
#define SCxSR_TDxE(port) (((port)->type == PORT_SCI) ? SCI_TDRE : SCIF_TDFE)
#define SCxSR_FER(port) (((port)->type == PORT_SCI) ? SCI_FER : SCIF_FER)
#define SCxSR_PER(port) (((port)->type == PORT_SCI) ? SCI_PER : SCIF_PER)
#define SCxSR_BRK(port) (((port)->type == PORT_SCI) ? 0x00 : SCIF_BRK)
-#define SCxSR_ORER(port) (((port)->type == PORT_SCI) ? SCI_ORER : SCIF_ORER)
+
+#define SCxSR_ERRORS(port) (to_sci_port(port)->cfg->error_mask)
#if defined(CONFIG_CPU_SUBTYPE_SH7705) || \
defined(CONFIG_CPU_SUBTYPE_SH7720) || \
@@ -191,278 +36,3 @@
#define SCI_MAJOR 204
#define SCI_MINOR_START 8
-
-#define SCI_IN(size, offset) \
- if ((size) == 8) { \
- return ioread8(port->membase + (offset)); \
- } else { \
- return ioread16(port->membase + (offset)); \
- }
-#define SCI_OUT(size, offset, value) \
- if ((size) == 8) { \
- iowrite8(value, port->membase + (offset)); \
- } else if ((size) == 16) { \
- iowrite16(value, port->membase + (offset)); \
- }
-
-#define CPU_SCIx_FNS(name, sci_offset, sci_size, scif_offset, scif_size)\
- static inline unsigned int sci_##name##_in(struct uart_port *port) \
- { \
- if (port->type == PORT_SCIF || port->type == PORT_SCIFB) { \
- SCI_IN(scif_size, scif_offset) \
- } else { /* PORT_SCI or PORT_SCIFA */ \
- SCI_IN(sci_size, sci_offset); \
- } \
- } \
- static inline void sci_##name##_out(struct uart_port *port, unsigned int value) \
- { \
- if (port->type == PORT_SCIF || port->type == PORT_SCIFB) { \
- SCI_OUT(scif_size, scif_offset, value) \
- } else { /* PORT_SCI or PORT_SCIFA */ \
- SCI_OUT(sci_size, sci_offset, value); \
- } \
- }
-
-#ifdef CONFIG_H8300
-/* h8300 don't have SCIF */
-#define CPU_SCIF_FNS(name) \
- static inline unsigned int sci_##name##_in(struct uart_port *port) \
- { \
- return 0; \
- } \
- static inline void sci_##name##_out(struct uart_port *port, unsigned int value) \
- { \
- }
-#else
-#define CPU_SCIF_FNS(name, scif_offset, scif_size) \
- static inline unsigned int sci_##name##_in(struct uart_port *port) \
- { \
- SCI_IN(scif_size, scif_offset); \
- } \
- static inline void sci_##name##_out(struct uart_port *port, unsigned int value) \
- { \
- SCI_OUT(scif_size, scif_offset, value); \
- }
-#endif
-
-#define CPU_SCI_FNS(name, sci_offset, sci_size) \
- static inline unsigned int sci_##name##_in(struct uart_port* port) \
- { \
- SCI_IN(sci_size, sci_offset); \
- } \
- static inline void sci_##name##_out(struct uart_port* port, unsigned int value) \
- { \
- SCI_OUT(sci_size, sci_offset, value); \
- }
-
-#if defined(CONFIG_CPU_SH3) || \
- defined(CONFIG_ARCH_SH73A0) || \
- defined(CONFIG_ARCH_SH7367) || \
- defined(CONFIG_ARCH_SH7377) || \
- defined(CONFIG_ARCH_SH7372)
-#if defined(CONFIG_CPU_SUBTYPE_SH7710) || defined(CONFIG_CPU_SUBTYPE_SH7712)
-#define SCIx_FNS(name, sh3_sci_offset, sh3_sci_size, sh4_sci_offset, sh4_sci_size, \
- sh3_scif_offset, sh3_scif_size, sh4_scif_offset, sh4_scif_size, \
- h8_sci_offset, h8_sci_size) \
- CPU_SCIx_FNS(name, sh4_sci_offset, sh4_sci_size, sh4_scif_offset, sh4_scif_size)
-#define SCIF_FNS(name, sh3_scif_offset, sh3_scif_size, sh4_scif_offset, sh4_scif_size) \
- CPU_SCIF_FNS(name, sh4_scif_offset, sh4_scif_size)
-#elif defined(CONFIG_CPU_SUBTYPE_SH7705) || \
- defined(CONFIG_CPU_SUBTYPE_SH7720) || \
- defined(CONFIG_CPU_SUBTYPE_SH7721) || \
- defined(CONFIG_ARCH_SH7367)
-#define SCIF_FNS(name, scif_offset, scif_size) \
- CPU_SCIF_FNS(name, scif_offset, scif_size)
-#elif defined(CONFIG_ARCH_SH7377) || \
- defined(CONFIG_ARCH_SH7372) || \
- defined(CONFIG_ARCH_SH73A0)
-#define SCIx_FNS(name, sh4_scifa_offset, sh4_scifa_size, sh4_scifb_offset, sh4_scifb_size) \
- CPU_SCIx_FNS(name, sh4_scifa_offset, sh4_scifa_size, sh4_scifb_offset, sh4_scifb_size)
-#define SCIF_FNS(name, scif_offset, scif_size) \
- CPU_SCIF_FNS(name, scif_offset, scif_size)
-#else
-#define SCIx_FNS(name, sh3_sci_offset, sh3_sci_size, sh4_sci_offset, sh4_sci_size, \
- sh3_scif_offset, sh3_scif_size, sh4_scif_offset, sh4_scif_size, \
- h8_sci_offset, h8_sci_size) \
- CPU_SCIx_FNS(name, sh3_sci_offset, sh3_sci_size, sh3_scif_offset, sh3_scif_size)
-#define SCIF_FNS(name, sh3_scif_offset, sh3_scif_size, sh4_scif_offset, sh4_scif_size) \
- CPU_SCIF_FNS(name, sh3_scif_offset, sh3_scif_size)
-#endif
-#elif defined(__H8300H__) || defined(__H8300S__)
-#define SCIx_FNS(name, sh3_sci_offset, sh3_sci_size, sh4_sci_offset, sh4_sci_size, \
- sh3_scif_offset, sh3_scif_size, sh4_scif_offset, sh4_scif_size, \
- h8_sci_offset, h8_sci_size) \
- CPU_SCI_FNS(name, h8_sci_offset, h8_sci_size)
-#define SCIF_FNS(name, sh3_scif_offset, sh3_scif_size, sh4_scif_offset, sh4_scif_size) \
- CPU_SCIF_FNS(name)
-#elif defined(CONFIG_CPU_SUBTYPE_SH7723) ||\
- defined(CONFIG_CPU_SUBTYPE_SH7724)
- #define SCIx_FNS(name, sh4_scifa_offset, sh4_scifa_size, sh4_scif_offset, sh4_scif_size) \
- CPU_SCIx_FNS(name, sh4_scifa_offset, sh4_scifa_size, sh4_scif_offset, sh4_scif_size)
- #define SCIF_FNS(name, sh4_scif_offset, sh4_scif_size) \
- CPU_SCIF_FNS(name, sh4_scif_offset, sh4_scif_size)
-#else
-#define SCIx_FNS(name, sh3_sci_offset, sh3_sci_size, sh4_sci_offset, sh4_sci_size, \
- sh3_scif_offset, sh3_scif_size, sh4_scif_offset, sh4_scif_size, \
- h8_sci_offset, h8_sci_size) \
- CPU_SCIx_FNS(name, sh4_sci_offset, sh4_sci_size, sh4_scif_offset, sh4_scif_size)
-#define SCIF_FNS(name, sh3_scif_offset, sh3_scif_size, sh4_scif_offset, sh4_scif_size) \
- CPU_SCIF_FNS(name, sh4_scif_offset, sh4_scif_size)
-#endif
-
-#if defined(CONFIG_CPU_SUBTYPE_SH7705) || \
- defined(CONFIG_CPU_SUBTYPE_SH7720) || \
- defined(CONFIG_CPU_SUBTYPE_SH7721) || \
- defined(CONFIG_ARCH_SH7367)
-
-SCIF_FNS(SCSMR, 0x00, 16)
-SCIF_FNS(SCBRR, 0x04, 8)
-SCIF_FNS(SCSCR, 0x08, 16)
-SCIF_FNS(SCxSR, 0x14, 16)
-SCIF_FNS(SCFCR, 0x18, 16)
-SCIF_FNS(SCFDR, 0x1c, 16)
-SCIF_FNS(SCxTDR, 0x20, 8)
-SCIF_FNS(SCxRDR, 0x24, 8)
-SCIF_FNS(SCLSR, 0x00, 0)
-#elif defined(CONFIG_ARCH_SH7377) || \
- defined(CONFIG_ARCH_SH7372) || \
- defined(CONFIG_ARCH_SH73A0)
-SCIF_FNS(SCSMR, 0x00, 16)
-SCIF_FNS(SCBRR, 0x04, 8)
-SCIF_FNS(SCSCR, 0x08, 16)
-SCIF_FNS(SCTDSR, 0x0c, 16)
-SCIF_FNS(SCFER, 0x10, 16)
-SCIF_FNS(SCxSR, 0x14, 16)
-SCIF_FNS(SCFCR, 0x18, 16)
-SCIF_FNS(SCFDR, 0x1c, 16)
-SCIF_FNS(SCTFDR, 0x38, 16)
-SCIF_FNS(SCRFDR, 0x3c, 16)
-SCIx_FNS(SCxTDR, 0x20, 8, 0x40, 8)
-SCIx_FNS(SCxRDR, 0x24, 8, 0x60, 8)
-SCIF_FNS(SCLSR, 0x00, 0)
-#elif defined(CONFIG_CPU_SUBTYPE_SH7723) ||\
- defined(CONFIG_CPU_SUBTYPE_SH7724)
-SCIx_FNS(SCSMR, 0x00, 16, 0x00, 16)
-SCIx_FNS(SCBRR, 0x04, 8, 0x04, 8)
-SCIx_FNS(SCSCR, 0x08, 16, 0x08, 16)
-SCIx_FNS(SCxTDR, 0x20, 8, 0x0c, 8)
-SCIx_FNS(SCxSR, 0x14, 16, 0x10, 16)
-SCIx_FNS(SCxRDR, 0x24, 8, 0x14, 8)
-SCIx_FNS(SCSPTR, 0, 0, 0, 0)
-SCIF_FNS(SCFCR, 0x18, 16)
-SCIF_FNS(SCFDR, 0x1c, 16)
-SCIF_FNS(SCLSR, 0x24, 16)
-#else
-/* reg SCI/SH3 SCI/SH4 SCIF/SH3 SCIF/SH4 SCI/H8*/
-/* name off sz off sz off sz off sz off sz*/
-SCIx_FNS(SCSMR, 0x00, 8, 0x00, 8, 0x00, 8, 0x00, 16, 0x00, 8)
-SCIx_FNS(SCBRR, 0x02, 8, 0x04, 8, 0x02, 8, 0x04, 8, 0x01, 8)
-SCIx_FNS(SCSCR, 0x04, 8, 0x08, 8, 0x04, 8, 0x08, 16, 0x02, 8)
-SCIx_FNS(SCxTDR, 0x06, 8, 0x0c, 8, 0x06, 8, 0x0C, 8, 0x03, 8)
-SCIx_FNS(SCxSR, 0x08, 8, 0x10, 8, 0x08, 16, 0x10, 16, 0x04, 8)
-SCIx_FNS(SCxRDR, 0x0a, 8, 0x14, 8, 0x0A, 8, 0x14, 8, 0x05, 8)
-SCIF_FNS(SCFCR, 0x0c, 8, 0x18, 16)
-#if defined(CONFIG_CPU_SUBTYPE_SH7760) || \
- defined(CONFIG_CPU_SUBTYPE_SH7780) || \
- defined(CONFIG_CPU_SUBTYPE_SH7785) || \
- defined(CONFIG_CPU_SUBTYPE_SH7786)
-SCIF_FNS(SCFDR, 0x0e, 16, 0x1C, 16)
-SCIF_FNS(SCTFDR, 0x0e, 16, 0x1C, 16)
-SCIF_FNS(SCRFDR, 0x0e, 16, 0x20, 16)
-SCIF_FNS(SCSPTR, 0, 0, 0x24, 16)
-SCIF_FNS(SCLSR, 0, 0, 0x28, 16)
-#elif defined(CONFIG_CPU_SUBTYPE_SH7763)
-SCIF_FNS(SCFDR, 0, 0, 0x1C, 16)
-SCIF_FNS(SCTFDR, 0x0e, 16, 0x1C, 16)
-SCIF_FNS(SCRFDR, 0x0e, 16, 0x20, 16)
-SCIF_FNS(SCSPTR, 0, 0, 0x24, 16)
-SCIF_FNS(SCLSR, 0, 0, 0x28, 16)
-#else
-SCIF_FNS(SCFDR, 0x0e, 16, 0x1C, 16)
-#if defined(CONFIG_CPU_SUBTYPE_SH7722)
-SCIF_FNS(SCSPTR, 0, 0, 0, 0)
-#else
-SCIF_FNS(SCSPTR, 0, 0, 0x20, 16)
-#endif
-SCIF_FNS(SCLSR, 0, 0, 0x24, 16)
-#endif
-#endif
-#define sci_in(port, reg) sci_##reg##_in(port)
-#define sci_out(port, reg, value) sci_##reg##_out(port, value)
-
-/* H8/300 series SCI pins assignment */
-#if defined(__H8300H__) || defined(__H8300S__)
-static const struct __attribute__((packed)) {
- int port; /* GPIO port no */
- unsigned short rx,tx; /* GPIO bit no */
-} h8300_sci_pins[] = {
-#if defined(CONFIG_H83007) || defined(CONFIG_H83068)
- { /* SCI0 */
- .port = H8300_GPIO_P9,
- .rx = H8300_GPIO_B2,
- .tx = H8300_GPIO_B0,
- },
- { /* SCI1 */
- .port = H8300_GPIO_P9,
- .rx = H8300_GPIO_B3,
- .tx = H8300_GPIO_B1,
- },
- { /* SCI2 */
- .port = H8300_GPIO_PB,
- .rx = H8300_GPIO_B7,
- .tx = H8300_GPIO_B6,
- }
-#elif defined(CONFIG_H8S2678)
- { /* SCI0 */
- .port = H8300_GPIO_P3,
- .rx = H8300_GPIO_B2,
- .tx = H8300_GPIO_B0,
- },
- { /* SCI1 */
- .port = H8300_GPIO_P3,
- .rx = H8300_GPIO_B3,
- .tx = H8300_GPIO_B1,
- },
- { /* SCI2 */
- .port = H8300_GPIO_P5,
- .rx = H8300_GPIO_B1,
- .tx = H8300_GPIO_B0,
- }
-#endif
-};
-#endif
-
-#if defined(CONFIG_CPU_SUBTYPE_SH7706) || \
- defined(CONFIG_CPU_SUBTYPE_SH7707) || \
- defined(CONFIG_CPU_SUBTYPE_SH7708) || \
- defined(CONFIG_CPU_SUBTYPE_SH7709)
-static inline int sci_rxd_in(struct uart_port *port)
-{
- if (port->mapbase == 0xfffffe80)
- return __raw_readb(SCPDR)&0x01 ? 1 : 0; /* SCI */
- return 1;
-}
-#elif defined(CONFIG_CPU_SUBTYPE_SH7750) || \
- defined(CONFIG_CPU_SUBTYPE_SH7751) || \
- defined(CONFIG_CPU_SUBTYPE_SH7751R) || \
- defined(CONFIG_CPU_SUBTYPE_SH7750R) || \
- defined(CONFIG_CPU_SUBTYPE_SH7750S) || \
- defined(CONFIG_CPU_SUBTYPE_SH7091)
-static inline int sci_rxd_in(struct uart_port *port)
-{
- if (port->mapbase == 0xffe00000)
- return __raw_readb(SCSPTR1)&0x01 ? 1 : 0; /* SCI */
- return 1;
-}
-#elif defined(__H8300H__) || defined(__H8300S__)
-static inline int sci_rxd_in(struct uart_port *port)
-{
- int ch = (port->mapbase - SMR0) >> 3;
- return (H8300_SCI_DR(ch) & h8300_sci_pins[ch].rx) ? 1 : 0;
-}
-#else /* default case for non-SCI processors */
-static inline int sci_rxd_in(struct uart_port *port)
-{
- return 1;
-}
-#endif
diff --git a/drivers/usb/class/usbtmc.c b/drivers/usb/class/usbtmc.c
index 385acb8..3f94ac3 100644
--- a/drivers/usb/class/usbtmc.c
+++ b/drivers/usb/class/usbtmc.c
@@ -268,7 +268,7 @@
dev_err(dev, "usb_bulk_msg returned %d\n", rv);
goto exit;
}
- } while ((actual = max_size) &&
+ } while ((actual == max_size) &&
(n < USBTMC_MAX_READS_TO_CLEAR_BULK_IN));
if (actual == max_size) {
diff --git a/drivers/usb/core/config.c b/drivers/usb/core/config.c
index c962608..26678ca 100644
--- a/drivers/usb/core/config.c
+++ b/drivers/usb/core/config.c
@@ -123,10 +123,11 @@
}
if (usb_endpoint_xfer_isoc(&ep->desc))
- max_tx = ep->desc.wMaxPacketSize * (desc->bMaxBurst + 1) *
- (desc->bmAttributes + 1);
+ max_tx = (desc->bMaxBurst + 1) * (desc->bmAttributes + 1) *
+ le16_to_cpu(ep->desc.wMaxPacketSize);
else if (usb_endpoint_xfer_int(&ep->desc))
- max_tx = ep->desc.wMaxPacketSize * (desc->bMaxBurst + 1);
+ max_tx = le16_to_cpu(ep->desc.wMaxPacketSize) *
+ (desc->bMaxBurst + 1);
else
max_tx = 999999;
if (le16_to_cpu(desc->wBytesPerInterval) > max_tx) {
@@ -134,10 +135,10 @@
"config %d interface %d altsetting %d ep %d: "
"setting to %d\n",
usb_endpoint_xfer_isoc(&ep->desc) ? "Isoc" : "Int",
- desc->wBytesPerInterval,
+ le16_to_cpu(desc->wBytesPerInterval),
cfgno, inum, asnum, ep->desc.bEndpointAddress,
max_tx);
- ep->ss_ep_comp.wBytesPerInterval = max_tx;
+ ep->ss_ep_comp.wBytesPerInterval = cpu_to_le16(max_tx);
}
}
diff --git a/drivers/usb/gadget/Kconfig b/drivers/usb/gadget/Kconfig
index 44b6b40..5a084b9 100644
--- a/drivers/usb/gadget/Kconfig
+++ b/drivers/usb/gadget/Kconfig
@@ -310,7 +310,7 @@
# musb builds in ../musb along with host support
config USB_GADGET_MUSB_HDRC
tristate "Inventra HDRC USB Peripheral (TI, ADI, ...)"
- depends on USB_MUSB_HDRC && (USB_MUSB_PERIPHERAL || USB_MUSB_OTG)
+ depends on USB_MUSB_HDRC
select USB_GADGET_DUALSPEED
help
This OTG-capable silicon IP is used in dual designs including
diff --git a/drivers/usb/gadget/at91_udc.c b/drivers/usb/gadget/at91_udc.c
index 98cbc06..ddb118a 100644
--- a/drivers/usb/gadget/at91_udc.c
+++ b/drivers/usb/gadget/at91_udc.c
@@ -35,6 +35,7 @@
#include <linux/list.h>
#include <linux/interrupt.h>
#include <linux/proc_fs.h>
+#include <linux/prefetch.h>
#include <linux/clk.h>
#include <linux/usb/ch9.h>
#include <linux/usb/gadget.h>
diff --git a/drivers/usb/gadget/composite.c b/drivers/usb/gadget/composite.c
index 5ef8779..aef4741 100644
--- a/drivers/usb/gadget/composite.c
+++ b/drivers/usb/gadget/composite.c
@@ -1079,10 +1079,12 @@
cdev->desc.bMaxPacketSize0 =
cdev->gadget->ep0->maxpacket;
if (gadget_is_superspeed(gadget)) {
- if (gadget->speed >= USB_SPEED_SUPER)
+ if (gadget->speed >= USB_SPEED_SUPER) {
cdev->desc.bcdUSB = cpu_to_le16(0x0300);
- else
+ cdev->desc.bMaxPacketSize0 = 9;
+ } else {
cdev->desc.bcdUSB = cpu_to_le16(0x0210);
+ }
}
value = min(w_length, (u16) sizeof cdev->desc);
diff --git a/drivers/usb/gadget/f_hid.c b/drivers/usb/gadget/f_hid.c
index 403a48b..83a266b 100644
--- a/drivers/usb/gadget/f_hid.c
+++ b/drivers/usb/gadget/f_hid.c
@@ -367,6 +367,13 @@
case ((USB_DIR_IN | USB_TYPE_STANDARD | USB_RECIP_INTERFACE) << 8
| USB_REQ_GET_DESCRIPTOR):
switch (value >> 8) {
+ case HID_DT_HID:
+ VDBG(cdev, "USB_REQ_GET_DESCRIPTOR: HID\n");
+ length = min_t(unsigned short, length,
+ hidg_desc.bLength);
+ memcpy(req->buf, &hidg_desc, length);
+ goto respond;
+ break;
case HID_DT_REPORT:
VDBG(cdev, "USB_REQ_GET_DESCRIPTOR: REPORT\n");
length = min_t(unsigned short, length,
diff --git a/drivers/usb/gadget/fusb300_udc.c b/drivers/usb/gadget/fusb300_udc.c
index 24a9243..4ec888f 100644
--- a/drivers/usb/gadget/fusb300_udc.c
+++ b/drivers/usb/gadget/fusb300_udc.c
@@ -609,107 +609,6 @@
}
}
-#if 0
-static void fusb300_dbg_fifo(struct fusb300_ep *ep,
- u8 entry, u16 length)
-{
- u32 reg;
- u32 i = 0;
- u32 j = 0;
-
- reg = ioread32(ep->fusb300->reg + FUSB300_OFFSET_GTM);
- reg &= ~(FUSB300_GTM_TST_EP_ENTRY(0xF) |
- FUSB300_GTM_TST_EP_NUM(0xF) | FUSB300_GTM_TST_FIFO_DEG);
- reg |= (FUSB300_GTM_TST_EP_ENTRY(entry) |
- FUSB300_GTM_TST_EP_NUM(ep->epnum) | FUSB300_GTM_TST_FIFO_DEG);
- iowrite32(reg, ep->fusb300->reg + FUSB300_OFFSET_GTM);
-
- for (i = 0; i < (length >> 2); i++) {
- if (i * 4 == 1024)
- break;
- reg = ioread32(ep->fusb300->reg +
- FUSB300_OFFSET_BUFDBG_START + i * 4);
- printk(KERN_DEBUG" 0x%-8x", reg);
- j++;
- if ((j % 4) == 0)
- printk(KERN_DEBUG "\n");
- }
-
- if (length % 4) {
- reg = ioread32(ep->fusb300->reg +
- FUSB300_OFFSET_BUFDBG_START + i * 4);
- printk(KERN_DEBUG " 0x%x\n", reg);
- }
-
- if ((j % 4) != 0)
- printk(KERN_DEBUG "\n");
-
- fusb300_disable_bit(ep->fusb300, FUSB300_OFFSET_GTM,
- FUSB300_GTM_TST_FIFO_DEG);
-}
-
-static void fusb300_cmp_dbg_fifo(struct fusb300_ep *ep,
- u8 entry, u16 length, u8 *golden)
-{
- u32 reg;
- u32 i = 0;
- u32 golden_value;
- u8 *tmp;
-
- tmp = golden;
-
- printk(KERN_DEBUG "fusb300_cmp_dbg_fifo (entry %d) : start\n", entry);
-
- reg = ioread32(ep->fusb300->reg + FUSB300_OFFSET_GTM);
- reg &= ~(FUSB300_GTM_TST_EP_ENTRY(0xF) |
- FUSB300_GTM_TST_EP_NUM(0xF) | FUSB300_GTM_TST_FIFO_DEG);
- reg |= (FUSB300_GTM_TST_EP_ENTRY(entry) |
- FUSB300_GTM_TST_EP_NUM(ep->epnum) | FUSB300_GTM_TST_FIFO_DEG);
- iowrite32(reg, ep->fusb300->reg + FUSB300_OFFSET_GTM);
-
- for (i = 0; i < (length >> 2); i++) {
- if (i * 4 == 1024)
- break;
- golden_value = *tmp | *(tmp + 1) << 8 |
- *(tmp + 2) << 16 | *(tmp + 3) << 24;
-
- reg = ioread32(ep->fusb300->reg +
- FUSB300_OFFSET_BUFDBG_START + i*4);
-
- if (reg != golden_value) {
- printk(KERN_DEBUG "0x%x : ", (u32)(ep->fusb300->reg +
- FUSB300_OFFSET_BUFDBG_START + i*4));
- printk(KERN_DEBUG " golden = 0x%x, reg = 0x%x\n",
- golden_value, reg);
- }
- tmp += 4;
- }
-
- switch (length % 4) {
- case 1:
- golden_value = *tmp;
- case 2:
- golden_value = *tmp | *(tmp + 1) << 8;
- case 3:
- golden_value = *tmp | *(tmp + 1) << 8 | *(tmp + 2) << 16;
- default:
- break;
-
- reg = ioread32(ep->fusb300->reg + FUSB300_OFFSET_BUFDBG_START + i*4);
- if (reg != golden_value) {
- printk(KERN_DEBUG "0x%x:", (u32)(ep->fusb300->reg +
- FUSB300_OFFSET_BUFDBG_START + i*4));
- printk(KERN_DEBUG " golden = 0x%x, reg = 0x%x\n",
- golden_value, reg);
- }
- }
-
- printk(KERN_DEBUG "fusb300_cmp_dbg_fifo : end\n");
- fusb300_disable_bit(ep->fusb300, FUSB300_OFFSET_GTM,
- FUSB300_GTM_TST_FIFO_DEG);
-}
-#endif
-
static void fusb300_rdfifo(struct fusb300_ep *ep,
struct fusb300_request *req,
u32 length)
diff --git a/drivers/usb/gadget/net2272.c b/drivers/usb/gadget/net2272.c
index 7c7b0e1..ab98ea9 100644
--- a/drivers/usb/gadget/net2272.c
+++ b/drivers/usb/gadget/net2272.c
@@ -27,13 +27,13 @@
#include <linux/interrupt.h>
#include <linux/io.h>
#include <linux/ioport.h>
-#include <linux/irq.h>
#include <linux/kernel.h>
#include <linux/list.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/pci.h>
#include <linux/platform_device.h>
+#include <linux/prefetch.h>
#include <linux/sched.h>
#include <linux/slab.h>
#include <linux/timer.h>
diff --git a/drivers/usb/gadget/s3c2410_udc.c b/drivers/usb/gadget/s3c2410_udc.c
index 85c1b0d..8d31848 100644
--- a/drivers/usb/gadget/s3c2410_udc.c
+++ b/drivers/usb/gadget/s3c2410_udc.c
@@ -2060,6 +2060,7 @@
static const struct platform_device_id s3c_udc_ids[] = {
{ "s3c2410-usbgadget", },
{ "s3c2440-usbgadget", },
+ { }
};
MODULE_DEVICE_TABLE(platform, s3c_udc_ids);
diff --git a/drivers/usb/host/ehci-hub.c b/drivers/usb/host/ehci-hub.c
index bf2c8f6..e051b30 100644
--- a/drivers/usb/host/ehci-hub.c
+++ b/drivers/usb/host/ehci-hub.c
@@ -1046,7 +1046,19 @@
if (!selector || selector > 5)
goto error;
ehci_quiesce(ehci);
+
+ /* Put all enabled ports into suspend */
+ while (ports--) {
+ u32 __iomem *sreg =
+ &ehci->regs->port_status[ports];
+
+ temp = ehci_readl(ehci, sreg) & ~PORT_RWC_BITS;
+ if (temp & PORT_PE)
+ ehci_writel(ehci, temp | PORT_SUSPEND,
+ sreg);
+ }
ehci_halt(ehci);
+ temp = ehci_readl(ehci, status_reg);
temp |= selector << 16;
ehci_writel(ehci, temp, status_reg);
break;
diff --git a/drivers/usb/host/ehci-mxc.c b/drivers/usb/host/ehci-mxc.c
index 0c058be..555a73c 100644
--- a/drivers/usb/host/ehci-mxc.c
+++ b/drivers/usb/host/ehci-mxc.c
@@ -24,6 +24,7 @@
#include <linux/usb/ulpi.h>
#include <linux/slab.h>
+#include <mach/hardware.h>
#include <mach/mxc_ehci.h>
#include <asm/mach-types.h>
diff --git a/drivers/usb/host/ehci-omap.c b/drivers/usb/host/ehci-omap.c
index 55a57c2..4524032 100644
--- a/drivers/usb/host/ehci-omap.c
+++ b/drivers/usb/host/ehci-omap.c
@@ -98,6 +98,18 @@
}
}
+static void disable_put_regulator(
+ struct ehci_hcd_omap_platform_data *pdata)
+{
+ int i;
+
+ for (i = 0 ; i < OMAP3_HS_USB_PORTS ; i++) {
+ if (pdata->regulator[i]) {
+ regulator_disable(pdata->regulator[i]);
+ regulator_put(pdata->regulator[i]);
+ }
+ }
+}
/* configure so an HC device and id are always provided */
/* always called with process context; sleeping is OK */
@@ -231,9 +243,11 @@
omap_usbhs_disable(dev);
err_enable:
+ disable_put_regulator(pdata);
usb_put_hcd(hcd);
err_io:
+ iounmap(regs);
return ret;
}
@@ -253,6 +267,8 @@
usb_remove_hcd(hcd);
omap_usbhs_disable(dev);
+ disable_put_regulator(dev->platform_data);
+ iounmap(hcd->regs);
usb_put_hcd(hcd);
return 0;
}
diff --git a/drivers/usb/host/isp1760-hcd.c b/drivers/usb/host/isp1760-hcd.c
index 55d3d58..840beda 100644
--- a/drivers/usb/host/isp1760-hcd.c
+++ b/drivers/usb/host/isp1760-hcd.c
@@ -1583,6 +1583,9 @@
int retval = 0;
spin_lock_irqsave(&priv->lock, spinflags);
+ retval = usb_hcd_check_unlink_urb(hcd, urb, status);
+ if (retval)
+ goto out;
qh = urb->ep->hcpriv;
if (!qh) {
diff --git a/drivers/usb/host/pci-quirks.c b/drivers/usb/host/pci-quirks.c
index a9d3159..629a968 100644
--- a/drivers/usb/host/pci-quirks.c
+++ b/drivers/usb/host/pci-quirks.c
@@ -535,7 +535,7 @@
iounmap(base);
}
-static const struct dmi_system_id __initconst ehci_dmi_nohandoff_table[] = {
+static const struct dmi_system_id __devinitconst ehci_dmi_nohandoff_table[] = {
{
/* Pegatron Lucid (ExoPC) */
.matches = {
@@ -817,7 +817,7 @@
/* If the BIOS owns the HC, signal that the OS wants it, and wait */
if (val & XHCI_HC_BIOS_OWNED) {
- writel(val & XHCI_HC_OS_OWNED, base + ext_cap_offset);
+ writel(val | XHCI_HC_OS_OWNED, base + ext_cap_offset);
/* Wait for 5 seconds with 10 microsecond polling interval */
timeout = handshake(base + ext_cap_offset, XHCI_HC_BIOS_OWNED,
diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c
index 763f484..1c4432d 100644
--- a/drivers/usb/host/xhci.c
+++ b/drivers/usb/host/xhci.c
@@ -345,7 +345,8 @@
spin_lock_irqsave(&xhci->lock, flags);
temp = xhci_readl(xhci, &xhci->op_regs->status);
xhci_dbg(xhci, "op reg status = 0x%x\n", temp);
- if (temp == 0xffffffff || (xhci->xhc_state & XHCI_STATE_DYING)) {
+ if (temp == 0xffffffff || (xhci->xhc_state & XHCI_STATE_DYING) ||
+ (xhci->xhc_state & XHCI_STATE_HALTED)) {
xhci_dbg(xhci, "HW died, polling stopped.\n");
spin_unlock_irqrestore(&xhci->lock, flags);
return;
@@ -939,8 +940,11 @@
return 0;
}
+ xhci = hcd_to_xhci(hcd);
+ if (xhci->xhc_state & XHCI_STATE_HALTED)
+ return -ENODEV;
+
if (check_virt_dev) {
- xhci = hcd_to_xhci(hcd);
if (!udev->slot_id || !xhci->devs
|| !xhci->devs[udev->slot_id]) {
printk(KERN_DEBUG "xHCI %s called with unaddressed "
@@ -1242,7 +1246,8 @@
xhci_urb_free_priv(xhci, urb_priv);
return ret;
}
- if (xhci->xhc_state & XHCI_STATE_DYING) {
+ if ((xhci->xhc_state & XHCI_STATE_DYING) ||
+ (xhci->xhc_state & XHCI_STATE_HALTED)) {
xhci_dbg(xhci, "Ep 0x%x: URB %p to be canceled on "
"non-responsive xHCI host.\n",
urb->ep->desc.bEndpointAddress, urb);
@@ -2665,7 +2670,10 @@
int i, ret;
ret = xhci_check_args(hcd, udev, NULL, 0, true, __func__);
- if (ret <= 0)
+ /* If the host is halted due to driver unload, we still need to free the
+ * device.
+ */
+ if (ret <= 0 && ret != -ENODEV)
return;
virt_dev = xhci->devs[udev->slot_id];
@@ -2679,7 +2687,8 @@
spin_lock_irqsave(&xhci->lock, flags);
/* Don't disable the slot if the host controller is dead. */
state = xhci_readl(xhci, &xhci->op_regs->status);
- if (state == 0xffffffff || (xhci->xhc_state & XHCI_STATE_DYING)) {
+ if (state == 0xffffffff || (xhci->xhc_state & XHCI_STATE_DYING) ||
+ (xhci->xhc_state & XHCI_STATE_HALTED)) {
xhci_free_virt_device(xhci, udev->slot_id);
spin_unlock_irqrestore(&xhci->lock, flags);
return;
diff --git a/drivers/usb/musb/Kconfig b/drivers/usb/musb/Kconfig
index 6192b45..fc34b8b 100644
--- a/drivers/usb/musb/Kconfig
+++ b/drivers/usb/musb/Kconfig
@@ -3,9 +3,6 @@
# for silicon based on Mentor Graphics INVENTRA designs
#
-comment "Enable Host or Gadget support to see Inventra options"
- depends on !USB && USB_GADGET=n
-
# (M)HDRC = (Multipoint) Highspeed Dual-Role Controller
config USB_MUSB_HDRC
depends on USB && USB_GADGET
diff --git a/drivers/usb/musb/musb_gadget.c b/drivers/usb/musb/musb_gadget.c
index b67a062..8c41a2e 100644
--- a/drivers/usb/musb/musb_gadget.c
+++ b/drivers/usb/musb/musb_gadget.c
@@ -1698,6 +1698,8 @@
is_on = !!is_on;
+ pm_runtime_get_sync(musb->controller);
+
/* NOTE: this assumes we are sensing vbus; we'd rather
* not pullup unless the B-session is active.
*/
@@ -1707,6 +1709,9 @@
musb_pullup(musb, is_on);
}
spin_unlock_irqrestore(&musb->lock, flags);
+
+ pm_runtime_put(musb->controller);
+
return 0;
}
diff --git a/drivers/usb/musb/tusb6010_omap.c b/drivers/usb/musb/tusb6010_omap.c
index c784e6c..07c8a73 100644
--- a/drivers/usb/musb/tusb6010_omap.c
+++ b/drivers/usb/musb/tusb6010_omap.c
@@ -89,7 +89,7 @@
u32 reg = musb_readl(chdat->tbase, TUSB_DMA_EP_MAP);
if (reg != 0) {
- dev_dbg(musb->controller, "ep%i dmareq0 is busy for ep%i\n",
+ dev_dbg(chdat->musb->controller, "ep%i dmareq0 is busy for ep%i\n",
chdat->epnum, reg & 0xf);
return -EAGAIN;
}
diff --git a/drivers/usb/renesas_usbhs/mod_gadget.c b/drivers/usb/renesas_usbhs/mod_gadget.c
index ba79dbf..cb2d451 100644
--- a/drivers/usb/renesas_usbhs/mod_gadget.c
+++ b/drivers/usb/renesas_usbhs/mod_gadget.c
@@ -14,6 +14,7 @@
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*
*/
+#include <linux/dma-mapping.h>
#include <linux/io.h>
#include <linux/module.h>
#include <linux/platform_device.h>
@@ -76,7 +77,7 @@
struct usbhsg_gpriv, mod)
#define __usbhsg_for_each_uep(start, pos, g, i) \
- for (i = start, pos = (g)->uep; \
+ for (i = start, pos = (g)->uep + i; \
i < (g)->uep_size; \
i++, pos = (g)->uep + i)
diff --git a/drivers/usb/serial/ftdi_sio.c b/drivers/usb/serial/ftdi_sio.c
index 2e06b90..78a2cf9 100644
--- a/drivers/usb/serial/ftdi_sio.c
+++ b/drivers/usb/serial/ftdi_sio.c
@@ -151,6 +151,7 @@
* /sys/bus/usb/ftdi_sio/new_id, then send patch/report!
*/
static struct usb_device_id id_table_combined [] = {
+ { USB_DEVICE(FTDI_VID, FTDI_ZEITCONTROL_TAGTRACE_MIFARE_PID) },
{ USB_DEVICE(FTDI_VID, FTDI_CTI_MINI_PID) },
{ USB_DEVICE(FTDI_VID, FTDI_CTI_NANO_PID) },
{ USB_DEVICE(FTDI_VID, FTDI_AMC232_PID) },
@@ -1171,7 +1172,7 @@
case FT2232H: /* FT2232H chip */
case FT4232H: /* FT4232H chip */
case FT232H: /* FT232H chip */
- if ((baud <= 12000000) & (baud >= 1200)) {
+ if ((baud <= 12000000) && (baud >= 1200)) {
div_value = ftdi_2232h_baud_to_divisor(baud);
} else if (baud < 1200) {
div_value = ftdi_232bm_baud_to_divisor(baud);
@@ -1205,7 +1206,10 @@
urb_index_value = get_ftdi_divisor(tty, port);
urb_value = (__u16)urb_index_value;
urb_index = (__u16)(urb_index_value >> 16);
- if (priv->interface) { /* FT2232C */
+ if ((priv->chip_type == FT2232C) || (priv->chip_type == FT2232H) ||
+ (priv->chip_type == FT4232H) || (priv->chip_type == FT232H)) {
+ /* Probably the BM type needs the MSB of the encoded fractional
+ * divider also moved like for the chips above. Any infos? */
urb_index = (__u16)((urb_index << 8) | priv->interface);
}
diff --git a/drivers/usb/serial/ftdi_sio_ids.h b/drivers/usb/serial/ftdi_sio_ids.h
index 19156d1..bf5227a 100644
--- a/drivers/usb/serial/ftdi_sio_ids.h
+++ b/drivers/usb/serial/ftdi_sio_ids.h
@@ -1159,4 +1159,8 @@
/* USB-Nano-485*/
#define FTDI_CTI_NANO_PID 0xF60B
-
+/*
+ * ZeitControl cardsystems GmbH rfid-readers http://zeitconrol.de
+ */
+/* TagTracer MIFARE*/
+#define FTDI_ZEITCONTROL_TAGTRACE_MIFARE_PID 0xF7C0
diff --git a/drivers/usb/serial/option.c b/drivers/usb/serial/option.c
index 60b25d8..8156561 100644
--- a/drivers/usb/serial/option.c
+++ b/drivers/usb/serial/option.c
@@ -148,6 +148,10 @@
#define HUAWEI_PRODUCT_K4505 0x1464
#define HUAWEI_PRODUCT_K3765 0x1465
#define HUAWEI_PRODUCT_E14AC 0x14AC
+#define HUAWEI_PRODUCT_K3770 0x14C9
+#define HUAWEI_PRODUCT_K3771 0x14CA
+#define HUAWEI_PRODUCT_K4510 0x14CB
+#define HUAWEI_PRODUCT_K4511 0x14CC
#define HUAWEI_PRODUCT_ETS1220 0x1803
#define HUAWEI_PRODUCT_E353 0x1506
@@ -547,6 +551,14 @@
{ USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_K3765, 0xff, 0xff, 0xff) },
{ USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_ETS1220, 0xff, 0xff, 0xff) },
{ USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E14AC, 0xff, 0xff, 0xff) },
+ { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_K3770, 0xff, 0x02, 0x31) },
+ { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_K3770, 0xff, 0x02, 0x32) },
+ { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_K3771, 0xff, 0x02, 0x31) },
+ { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_K3771, 0xff, 0x02, 0x32) },
+ { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_K4510, 0xff, 0x01, 0x31) },
+ { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_K4510, 0xff, 0x01, 0x32) },
+ { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_K4511, 0xff, 0x01, 0x31) },
+ { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_K4511, 0xff, 0x01, 0x32) },
{ USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E353, 0xff, 0x01, 0x01) },
{ USB_DEVICE(NOVATELWIRELESS_VENDOR_ID, NOVATELWIRELESS_PRODUCT_V640) },
{ USB_DEVICE(NOVATELWIRELESS_VENDOR_ID, NOVATELWIRELESS_PRODUCT_V620) },
diff --git a/drivers/usb/serial/qcserial.c b/drivers/usb/serial/qcserial.c
index 54a9dab..aeccc7f 100644
--- a/drivers/usb/serial/qcserial.c
+++ b/drivers/usb/serial/qcserial.c
@@ -45,6 +45,7 @@
{USB_DEVICE(0x05c6, 0x9203)}, /* Generic Gobi Modem device */
{USB_DEVICE(0x05c6, 0x9222)}, /* Generic Gobi Modem device */
{USB_DEVICE(0x05c6, 0x9008)}, /* Generic Gobi QDL device */
+ {USB_DEVICE(0x05c6, 0x9009)}, /* Generic Gobi Modem device */
{USB_DEVICE(0x05c6, 0x9201)}, /* Generic Gobi QDL device */
{USB_DEVICE(0x05c6, 0x9221)}, /* Generic Gobi QDL device */
{USB_DEVICE(0x05c6, 0x9231)}, /* Generic Gobi QDL device */
@@ -78,6 +79,7 @@
{USB_DEVICE(0x1199, 0x9008)}, /* Sierra Wireless Gobi 2000 Modem device (VT773) */
{USB_DEVICE(0x1199, 0x9009)}, /* Sierra Wireless Gobi 2000 Modem device (VT773) */
{USB_DEVICE(0x1199, 0x900a)}, /* Sierra Wireless Gobi 2000 Modem device (VT773) */
+ {USB_DEVICE(0x1199, 0x9011)}, /* Sierra Wireless Gobi 2000 Modem device (MC8305) */
{USB_DEVICE(0x16d8, 0x8001)}, /* CMDTech Gobi 2000 QDL device (VU922) */
{USB_DEVICE(0x16d8, 0x8002)}, /* CMDTech Gobi 2000 Modem device (VU922) */
{USB_DEVICE(0x05c6, 0x9204)}, /* Gobi 2000 QDL device */
diff --git a/drivers/usb/storage/unusual_devs.h b/drivers/usb/storage/unusual_devs.h
index ccff348..3041a97 100644
--- a/drivers/usb/storage/unusual_devs.h
+++ b/drivers/usb/storage/unusual_devs.h
@@ -1988,6 +1988,16 @@
"Micro Mini 1GB",
USB_SC_DEVICE, USB_PR_DEVICE, NULL, US_FL_NOT_LOCKABLE ),
+/*
+ * Nick Bowler <nbowler@elliptictech.com>
+ * SCSI stack spams (otherwise harmless) error messages.
+ */
+UNUSUAL_DEV( 0xc251, 0x4003, 0x0100, 0x0100,
+ "Keil Software, Inc.",
+ "V2M MotherBoard",
+ USB_SC_DEVICE, USB_PR_DEVICE, NULL,
+ US_FL_NOT_LOCKABLE),
+
/* Reported by Andrew Simmons <andrew.simmons@gmail.com> */
UNUSUAL_DEV( 0xed06, 0x4500, 0x0001, 0x0001,
"DataStor",
diff --git a/drivers/video/backlight/Kconfig b/drivers/video/backlight/Kconfig
index 1e54b8b..278aeaa 100644
--- a/drivers/video/backlight/Kconfig
+++ b/drivers/video/backlight/Kconfig
@@ -335,6 +335,13 @@
If you have a backlight driven by a NXP PCF50633 MFD, say Y here to
enable its driver.
+config BACKLIGHT_AAT2870
+ tristate "AnalogicTech AAT2870 Backlight"
+ depends on BACKLIGHT_CLASS_DEVICE && MFD_AAT2870_CORE
+ help
+ If you have a AnalogicTech AAT2870 say Y to enable the
+ backlight driver.
+
endif # BACKLIGHT_CLASS_DEVICE
endif # BACKLIGHT_LCD_SUPPORT
diff --git a/drivers/video/backlight/Makefile b/drivers/video/backlight/Makefile
index bf1dd92..fdd1fc4 100644
--- a/drivers/video/backlight/Makefile
+++ b/drivers/video/backlight/Makefile
@@ -38,4 +38,5 @@
obj-$(CONFIG_BACKLIGHT_ADP8870) += adp8870_bl.o
obj-$(CONFIG_BACKLIGHT_88PM860X) += 88pm860x_bl.o
obj-$(CONFIG_BACKLIGHT_PCF50633) += pcf50633-backlight.o
+obj-$(CONFIG_BACKLIGHT_AAT2870) += aat2870_bl.o
diff --git a/drivers/video/backlight/aat2870_bl.c b/drivers/video/backlight/aat2870_bl.c
new file mode 100644
index 0000000..331f1ef
--- /dev/null
+++ b/drivers/video/backlight/aat2870_bl.c
@@ -0,0 +1,246 @@
+/*
+ * linux/drivers/video/backlight/aat2870_bl.c
+ *
+ * Copyright (c) 2011, NVIDIA Corporation.
+ * Author: Jin Park <jinyoungp@nvidia.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., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/platform_device.h>
+#include <linux/mutex.h>
+#include <linux/delay.h>
+#include <linux/fb.h>
+#include <linux/backlight.h>
+#include <linux/mfd/aat2870.h>
+
+struct aat2870_bl_driver_data {
+ struct platform_device *pdev;
+ struct backlight_device *bd;
+
+ int channels;
+ int max_current;
+ int brightness; /* current brightness */
+};
+
+static inline int aat2870_brightness(struct aat2870_bl_driver_data *aat2870_bl,
+ int brightness)
+{
+ struct backlight_device *bd = aat2870_bl->bd;
+ int val;
+
+ val = brightness * (aat2870_bl->max_current - 1);
+ val /= bd->props.max_brightness;
+
+ return val;
+}
+
+static inline int aat2870_bl_enable(struct aat2870_bl_driver_data *aat2870_bl)
+{
+ struct aat2870_data *aat2870
+ = dev_get_drvdata(aat2870_bl->pdev->dev.parent);
+
+ return aat2870->write(aat2870, AAT2870_BL_CH_EN,
+ (u8)aat2870_bl->channels);
+}
+
+static inline int aat2870_bl_disable(struct aat2870_bl_driver_data *aat2870_bl)
+{
+ struct aat2870_data *aat2870
+ = dev_get_drvdata(aat2870_bl->pdev->dev.parent);
+
+ return aat2870->write(aat2870, AAT2870_BL_CH_EN, 0x0);
+}
+
+static int aat2870_bl_get_brightness(struct backlight_device *bd)
+{
+ return bd->props.brightness;
+}
+
+static int aat2870_bl_update_status(struct backlight_device *bd)
+{
+ struct aat2870_bl_driver_data *aat2870_bl = dev_get_drvdata(&bd->dev);
+ struct aat2870_data *aat2870 =
+ dev_get_drvdata(aat2870_bl->pdev->dev.parent);
+ int brightness = bd->props.brightness;
+ int ret;
+
+ if ((brightness < 0) || (bd->props.max_brightness < brightness)) {
+ dev_err(&bd->dev, "invalid brightness, %d\n", brightness);
+ return -EINVAL;
+ }
+
+ dev_dbg(&bd->dev, "brightness=%d, power=%d, state=%d\n",
+ bd->props.brightness, bd->props.power, bd->props.state);
+
+ if ((bd->props.power != FB_BLANK_UNBLANK) ||
+ (bd->props.state & BL_CORE_FBBLANK) ||
+ (bd->props.state & BL_CORE_SUSPENDED))
+ brightness = 0;
+
+ ret = aat2870->write(aat2870, AAT2870_BLM,
+ (u8)aat2870_brightness(aat2870_bl, brightness));
+ if (ret < 0)
+ return ret;
+
+ if (brightness == 0) {
+ ret = aat2870_bl_disable(aat2870_bl);
+ if (ret < 0)
+ return ret;
+ } else if (aat2870_bl->brightness == 0) {
+ ret = aat2870_bl_enable(aat2870_bl);
+ if (ret < 0)
+ return ret;
+ }
+
+ aat2870_bl->brightness = brightness;
+
+ return 0;
+}
+
+static int aat2870_bl_check_fb(struct backlight_device *bd, struct fb_info *fi)
+{
+ return 1;
+}
+
+static const struct backlight_ops aat2870_bl_ops = {
+ .options = BL_CORE_SUSPENDRESUME,
+ .get_brightness = aat2870_bl_get_brightness,
+ .update_status = aat2870_bl_update_status,
+ .check_fb = aat2870_bl_check_fb,
+};
+
+static int aat2870_bl_probe(struct platform_device *pdev)
+{
+ struct aat2870_bl_platform_data *pdata = pdev->dev.platform_data;
+ struct aat2870_bl_driver_data *aat2870_bl;
+ struct backlight_device *bd;
+ struct backlight_properties props;
+ int ret = 0;
+
+ if (!pdata) {
+ dev_err(&pdev->dev, "No platform data\n");
+ ret = -ENXIO;
+ goto out;
+ }
+
+ if (pdev->id != AAT2870_ID_BL) {
+ dev_err(&pdev->dev, "Invalid device ID, %d\n", pdev->id);
+ ret = -EINVAL;
+ goto out;
+ }
+
+ aat2870_bl = kzalloc(sizeof(struct aat2870_bl_driver_data), GFP_KERNEL);
+ if (!aat2870_bl) {
+ dev_err(&pdev->dev,
+ "Failed to allocate memory for aat2870 backlight\n");
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ memset(&props, 0, sizeof(struct backlight_properties));
+
+ props.type = BACKLIGHT_RAW;
+ bd = backlight_device_register("aat2870-backlight", &pdev->dev,
+ aat2870_bl, &aat2870_bl_ops, &props);
+ if (IS_ERR(bd)) {
+ dev_err(&pdev->dev,
+ "Failed allocate memory for backlight device\n");
+ ret = PTR_ERR(bd);
+ goto out_kfree;
+ }
+
+ aat2870_bl->pdev = pdev;
+ platform_set_drvdata(pdev, aat2870_bl);
+
+ aat2870_bl->bd = bd;
+
+ if (pdata->channels > 0)
+ aat2870_bl->channels = pdata->channels;
+ else
+ aat2870_bl->channels = AAT2870_BL_CH_ALL;
+
+ if (pdata->max_current > 0)
+ aat2870_bl->max_current = pdata->max_current;
+ else
+ aat2870_bl->max_current = AAT2870_CURRENT_27_9;
+
+ if (pdata->max_brightness > 0)
+ bd->props.max_brightness = pdata->max_brightness;
+ else
+ bd->props.max_brightness = 255;
+
+ aat2870_bl->brightness = 0;
+ bd->props.power = FB_BLANK_UNBLANK;
+ bd->props.brightness = bd->props.max_brightness;
+
+ ret = aat2870_bl_update_status(bd);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "Failed to initialize\n");
+ goto out_bl_dev_unregister;
+ }
+
+ return 0;
+
+out_bl_dev_unregister:
+ backlight_device_unregister(bd);
+out_kfree:
+ kfree(aat2870_bl);
+out:
+ return ret;
+}
+
+static int aat2870_bl_remove(struct platform_device *pdev)
+{
+ struct aat2870_bl_driver_data *aat2870_bl = platform_get_drvdata(pdev);
+ struct backlight_device *bd = aat2870_bl->bd;
+
+ bd->props.power = FB_BLANK_POWERDOWN;
+ bd->props.brightness = 0;
+ backlight_update_status(bd);
+
+ backlight_device_unregister(bd);
+ kfree(aat2870_bl);
+
+ return 0;
+}
+
+static struct platform_driver aat2870_bl_driver = {
+ .driver = {
+ .name = "aat2870-backlight",
+ .owner = THIS_MODULE,
+ },
+ .probe = aat2870_bl_probe,
+ .remove = aat2870_bl_remove,
+};
+
+static int __init aat2870_bl_init(void)
+{
+ return platform_driver_register(&aat2870_bl_driver);
+}
+subsys_initcall(aat2870_bl_init);
+
+static void __exit aat2870_bl_exit(void)
+{
+ platform_driver_unregister(&aat2870_bl_driver);
+}
+module_exit(aat2870_bl_exit);
+
+MODULE_DESCRIPTION("AnalogicTech AAT2870 Backlight");
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Jin Park <jinyoungp@nvidia.com>");
diff --git a/drivers/video/omap2/displays/panel-taal.c b/drivers/video/omap2/displays/panel-taal.c
index fdd5d4ae..4e888ac 100644
--- a/drivers/video/omap2/displays/panel-taal.c
+++ b/drivers/video/omap2/displays/panel-taal.c
@@ -504,14 +504,18 @@
return 0;
r = omapdss_dsi_display_enable(dssdev);
- if (r)
- goto err;
+ if (r) {
+ dev_err(&dssdev->dev, "failed to enable DSI\n");
+ goto err1;
+ }
omapdss_dsi_vc_enable_hs(dssdev, td->channel, true);
r = _taal_enable_te(dssdev, true);
- if (r)
- goto err;
+ if (r) {
+ dev_err(&dssdev->dev, "failed to re-enable TE");
+ goto err2;
+ }
enable_irq(gpio_to_irq(panel_data->ext_te_gpio));
@@ -521,13 +525,15 @@
return 0;
-err:
- dev_err(&dssdev->dev, "exit ULPS failed");
+err2:
+ dev_err(&dssdev->dev, "failed to exit ULPS");
+
r = taal_panel_reset(dssdev);
-
- enable_irq(gpio_to_irq(panel_data->ext_te_gpio));
- td->ulps_enabled = false;
-
+ if (!r) {
+ enable_irq(gpio_to_irq(panel_data->ext_te_gpio));
+ td->ulps_enabled = false;
+ }
+err1:
taal_queue_ulps_work(dssdev);
return r;
@@ -1241,11 +1247,8 @@
int r;
r = taal_dcs_write_0(td, DCS_DISPLAY_OFF);
- if (!r) {
+ if (!r)
r = taal_sleep_in(td);
- /* HACK: wait a bit so that the message goes through */
- msleep(10);
- }
if (r) {
dev_err(&dssdev->dev,
@@ -1317,8 +1320,11 @@
dsi_bus_lock(dssdev);
if (dssdev->state == OMAP_DSS_DISPLAY_ACTIVE) {
- taal_wake_up(dssdev);
- taal_power_off(dssdev);
+ int r;
+
+ r = taal_wake_up(dssdev);
+ if (!r)
+ taal_power_off(dssdev);
}
dsi_bus_unlock(dssdev);
@@ -1897,20 +1903,6 @@
mutex_unlock(&td->lock);
}
-static int taal_set_update_mode(struct omap_dss_device *dssdev,
- enum omap_dss_update_mode mode)
-{
- if (mode != OMAP_DSS_UPDATE_MANUAL)
- return -EINVAL;
- return 0;
-}
-
-static enum omap_dss_update_mode taal_get_update_mode(
- struct omap_dss_device *dssdev)
-{
- return OMAP_DSS_UPDATE_MANUAL;
-}
-
static struct omap_dss_driver taal_driver = {
.probe = taal_probe,
.remove = __exit_p(taal_remove),
@@ -1920,9 +1912,6 @@
.suspend = taal_suspend,
.resume = taal_resume,
- .set_update_mode = taal_set_update_mode,
- .get_update_mode = taal_get_update_mode,
-
.update = taal_update,
.sync = taal_sync,
diff --git a/drivers/video/omap2/dss/Kconfig b/drivers/video/omap2/dss/Kconfig
index 6b3e2da..0d12524 100644
--- a/drivers/video/omap2/dss/Kconfig
+++ b/drivers/video/omap2/dss/Kconfig
@@ -117,18 +117,6 @@
Max FCK is 173MHz, so this doesn't work if your PCK
is very high.
-config OMAP2_DSS_SLEEP_BEFORE_RESET
- bool "Sleep 50ms before DSS reset"
- default y
- help
- For some unknown reason we may get SYNC_LOST errors from the display
- subsystem at initialization time if we don't sleep before resetting
- the DSS. See the source (dss.c) for more comments.
-
- However, 50ms is quite long time to sleep, and with some
- configurations the SYNC_LOST may never happen, so the sleep can
- be disabled here.
-
config OMAP2_DSS_SLEEP_AFTER_VENC_RESET
bool "Sleep 20ms after VENC reset"
default y
diff --git a/drivers/video/omap2/dss/core.c b/drivers/video/omap2/dss/core.c
index 3da4267..76821fe 100644
--- a/drivers/video/omap2/dss/core.c
+++ b/drivers/video/omap2/dss/core.c
@@ -183,8 +183,11 @@
goto err_dss;
}
- /* keep clocks enabled to prevent context saves/restores during init */
- dss_clk_enable(DSS_CLK_ICK | DSS_CLK_FCK);
+ r = dispc_init_platform_driver();
+ if (r) {
+ DSSERR("Failed to initialize dispc platform driver\n");
+ goto err_dispc;
+ }
r = rfbi_init_platform_driver();
if (r) {
@@ -192,12 +195,6 @@
goto err_rfbi;
}
- r = dispc_init_platform_driver();
- if (r) {
- DSSERR("Failed to initialize dispc platform driver\n");
- goto err_dispc;
- }
-
r = venc_init_platform_driver();
if (r) {
DSSERR("Failed to initialize venc platform driver\n");
@@ -238,8 +235,6 @@
pdata->default_device = dssdev;
}
- dss_clk_disable(DSS_CLK_ICK | DSS_CLK_FCK);
-
return 0;
err_register:
@@ -268,11 +263,11 @@
dss_uninitialize_debugfs();
- venc_uninit_platform_driver();
- dispc_uninit_platform_driver();
- rfbi_uninit_platform_driver();
- dsi_uninit_platform_driver();
hdmi_uninit_platform_driver();
+ dsi_uninit_platform_driver();
+ venc_uninit_platform_driver();
+ rfbi_uninit_platform_driver();
+ dispc_uninit_platform_driver();
dss_uninit_platform_driver();
dss_uninit_overlays(pdev);
diff --git a/drivers/video/omap2/dss/dispc.c b/drivers/video/omap2/dss/dispc.c
index 7a9a2e7..0f3961a 100644
--- a/drivers/video/omap2/dss/dispc.c
+++ b/drivers/video/omap2/dss/dispc.c
@@ -33,6 +33,8 @@
#include <linux/workqueue.h>
#include <linux/hardirq.h>
#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
#include <plat/sram.h>
#include <plat/clock.h>
@@ -77,6 +79,12 @@
s8 vc00;
};
+enum omap_burst_size {
+ BURST_SIZE_X2 = 0,
+ BURST_SIZE_X4 = 1,
+ BURST_SIZE_X8 = 2,
+};
+
#define REG_GET(idx, start, end) \
FLD_GET(dispc_read_reg(idx), start, end)
@@ -92,7 +100,11 @@
static struct {
struct platform_device *pdev;
void __iomem *base;
+
+ int ctx_loss_cnt;
+
int irq;
+ struct clk *dss_clk;
u32 fifo_size[3];
@@ -102,6 +114,7 @@
u32 error_irqs;
struct work_struct error_work;
+ bool ctx_valid;
u32 ctx[DISPC_SZ_REGS / sizeof(u32)];
#ifdef CONFIG_OMAP2_DSS_COLLECT_IRQ_STATS
@@ -134,18 +147,34 @@
return __raw_readl(dispc.base + idx);
}
+static int dispc_get_ctx_loss_count(void)
+{
+ struct device *dev = &dispc.pdev->dev;
+ struct omap_display_platform_data *pdata = dev->platform_data;
+ struct omap_dss_board_info *board_data = pdata->board_data;
+ int cnt;
+
+ if (!board_data->get_context_loss_count)
+ return -ENOENT;
+
+ cnt = board_data->get_context_loss_count(dev);
+
+ WARN_ONCE(cnt < 0, "get_context_loss_count failed: %d\n", cnt);
+
+ return cnt;
+}
+
#define SR(reg) \
dispc.ctx[DISPC_##reg / sizeof(u32)] = dispc_read_reg(DISPC_##reg)
#define RR(reg) \
dispc_write_reg(DISPC_##reg, dispc.ctx[DISPC_##reg / sizeof(u32)])
-void dispc_save_context(void)
+static void dispc_save_context(void)
{
int i;
- if (cpu_is_omap24xx())
- return;
- SR(SYSCONFIG);
+ DSSDBG("dispc_save_context\n");
+
SR(IRQENABLE);
SR(CONTROL);
SR(CONFIG);
@@ -158,7 +187,8 @@
SR(TIMING_V(OMAP_DSS_CHANNEL_LCD));
SR(POL_FREQ(OMAP_DSS_CHANNEL_LCD));
SR(DIVISORo(OMAP_DSS_CHANNEL_LCD));
- SR(GLOBAL_ALPHA);
+ if (dss_has_feature(FEAT_GLOBAL_ALPHA))
+ SR(GLOBAL_ALPHA);
SR(SIZE_MGR(OMAP_DSS_CHANNEL_DIGIT));
SR(SIZE_MGR(OMAP_DSS_CHANNEL_LCD));
if (dss_has_feature(FEAT_MGR_LCD2)) {
@@ -188,20 +218,25 @@
SR(DATA_CYCLE2(OMAP_DSS_CHANNEL_LCD));
SR(DATA_CYCLE3(OMAP_DSS_CHANNEL_LCD));
- SR(CPR_COEF_R(OMAP_DSS_CHANNEL_LCD));
- SR(CPR_COEF_G(OMAP_DSS_CHANNEL_LCD));
- SR(CPR_COEF_B(OMAP_DSS_CHANNEL_LCD));
+ if (dss_has_feature(FEAT_CPR)) {
+ SR(CPR_COEF_R(OMAP_DSS_CHANNEL_LCD));
+ SR(CPR_COEF_G(OMAP_DSS_CHANNEL_LCD));
+ SR(CPR_COEF_B(OMAP_DSS_CHANNEL_LCD));
+ }
if (dss_has_feature(FEAT_MGR_LCD2)) {
- SR(CPR_COEF_B(OMAP_DSS_CHANNEL_LCD2));
- SR(CPR_COEF_G(OMAP_DSS_CHANNEL_LCD2));
- SR(CPR_COEF_R(OMAP_DSS_CHANNEL_LCD2));
+ if (dss_has_feature(FEAT_CPR)) {
+ SR(CPR_COEF_B(OMAP_DSS_CHANNEL_LCD2));
+ SR(CPR_COEF_G(OMAP_DSS_CHANNEL_LCD2));
+ SR(CPR_COEF_R(OMAP_DSS_CHANNEL_LCD2));
+ }
SR(DATA_CYCLE1(OMAP_DSS_CHANNEL_LCD2));
SR(DATA_CYCLE2(OMAP_DSS_CHANNEL_LCD2));
SR(DATA_CYCLE3(OMAP_DSS_CHANNEL_LCD2));
}
- SR(OVL_PRELOAD(OMAP_DSS_GFX));
+ if (dss_has_feature(FEAT_PRELOAD))
+ SR(OVL_PRELOAD(OMAP_DSS_GFX));
/* VID1 */
SR(OVL_BA0(OMAP_DSS_VIDEO1));
@@ -226,8 +261,10 @@
for (i = 0; i < 5; i++)
SR(OVL_CONV_COEF(OMAP_DSS_VIDEO1, i));
- for (i = 0; i < 8; i++)
- SR(OVL_FIR_COEF_V(OMAP_DSS_VIDEO1, i));
+ if (dss_has_feature(FEAT_FIR_COEF_V)) {
+ for (i = 0; i < 8; i++)
+ SR(OVL_FIR_COEF_V(OMAP_DSS_VIDEO1, i));
+ }
if (dss_has_feature(FEAT_HANDLE_UV_SEPARATE)) {
SR(OVL_BA0_UV(OMAP_DSS_VIDEO1));
@@ -248,7 +285,8 @@
if (dss_has_feature(FEAT_ATTR2))
SR(OVL_ATTRIBUTES2(OMAP_DSS_VIDEO1));
- SR(OVL_PRELOAD(OMAP_DSS_VIDEO1));
+ if (dss_has_feature(FEAT_PRELOAD))
+ SR(OVL_PRELOAD(OMAP_DSS_VIDEO1));
/* VID2 */
SR(OVL_BA0(OMAP_DSS_VIDEO2));
@@ -273,8 +311,10 @@
for (i = 0; i < 5; i++)
SR(OVL_CONV_COEF(OMAP_DSS_VIDEO2, i));
- for (i = 0; i < 8; i++)
- SR(OVL_FIR_COEF_V(OMAP_DSS_VIDEO2, i));
+ if (dss_has_feature(FEAT_FIR_COEF_V)) {
+ for (i = 0; i < 8; i++)
+ SR(OVL_FIR_COEF_V(OMAP_DSS_VIDEO2, i));
+ }
if (dss_has_feature(FEAT_HANDLE_UV_SEPARATE)) {
SR(OVL_BA0_UV(OMAP_DSS_VIDEO2));
@@ -295,16 +335,35 @@
if (dss_has_feature(FEAT_ATTR2))
SR(OVL_ATTRIBUTES2(OMAP_DSS_VIDEO2));
- SR(OVL_PRELOAD(OMAP_DSS_VIDEO2));
+ if (dss_has_feature(FEAT_PRELOAD))
+ SR(OVL_PRELOAD(OMAP_DSS_VIDEO2));
if (dss_has_feature(FEAT_CORE_CLK_DIV))
SR(DIVISOR);
+
+ dispc.ctx_loss_cnt = dispc_get_ctx_loss_count();
+ dispc.ctx_valid = true;
+
+ DSSDBG("context saved, ctx_loss_count %d\n", dispc.ctx_loss_cnt);
}
-void dispc_restore_context(void)
+static void dispc_restore_context(void)
{
- int i;
- RR(SYSCONFIG);
+ int i, ctx;
+
+ DSSDBG("dispc_restore_context\n");
+
+ if (!dispc.ctx_valid)
+ return;
+
+ ctx = dispc_get_ctx_loss_count();
+
+ if (ctx >= 0 && ctx == dispc.ctx_loss_cnt)
+ return;
+
+ DSSDBG("ctx_loss_count: saved %d, current %d\n",
+ dispc.ctx_loss_cnt, ctx);
+
/*RR(IRQENABLE);*/
/*RR(CONTROL);*/
RR(CONFIG);
@@ -317,7 +376,8 @@
RR(TIMING_V(OMAP_DSS_CHANNEL_LCD));
RR(POL_FREQ(OMAP_DSS_CHANNEL_LCD));
RR(DIVISORo(OMAP_DSS_CHANNEL_LCD));
- RR(GLOBAL_ALPHA);
+ if (dss_has_feature(FEAT_GLOBAL_ALPHA))
+ RR(GLOBAL_ALPHA);
RR(SIZE_MGR(OMAP_DSS_CHANNEL_DIGIT));
RR(SIZE_MGR(OMAP_DSS_CHANNEL_LCD));
if (dss_has_feature(FEAT_MGR_LCD2)) {
@@ -347,20 +407,25 @@
RR(DATA_CYCLE2(OMAP_DSS_CHANNEL_LCD));
RR(DATA_CYCLE3(OMAP_DSS_CHANNEL_LCD));
- RR(CPR_COEF_R(OMAP_DSS_CHANNEL_LCD));
- RR(CPR_COEF_G(OMAP_DSS_CHANNEL_LCD));
- RR(CPR_COEF_B(OMAP_DSS_CHANNEL_LCD));
+ if (dss_has_feature(FEAT_CPR)) {
+ RR(CPR_COEF_R(OMAP_DSS_CHANNEL_LCD));
+ RR(CPR_COEF_G(OMAP_DSS_CHANNEL_LCD));
+ RR(CPR_COEF_B(OMAP_DSS_CHANNEL_LCD));
+ }
if (dss_has_feature(FEAT_MGR_LCD2)) {
RR(DATA_CYCLE1(OMAP_DSS_CHANNEL_LCD2));
RR(DATA_CYCLE2(OMAP_DSS_CHANNEL_LCD2));
RR(DATA_CYCLE3(OMAP_DSS_CHANNEL_LCD2));
- RR(CPR_COEF_B(OMAP_DSS_CHANNEL_LCD2));
- RR(CPR_COEF_G(OMAP_DSS_CHANNEL_LCD2));
- RR(CPR_COEF_R(OMAP_DSS_CHANNEL_LCD2));
+ if (dss_has_feature(FEAT_CPR)) {
+ RR(CPR_COEF_B(OMAP_DSS_CHANNEL_LCD2));
+ RR(CPR_COEF_G(OMAP_DSS_CHANNEL_LCD2));
+ RR(CPR_COEF_R(OMAP_DSS_CHANNEL_LCD2));
+ }
}
- RR(OVL_PRELOAD(OMAP_DSS_GFX));
+ if (dss_has_feature(FEAT_PRELOAD))
+ RR(OVL_PRELOAD(OMAP_DSS_GFX));
/* VID1 */
RR(OVL_BA0(OMAP_DSS_VIDEO1));
@@ -385,8 +450,10 @@
for (i = 0; i < 5; i++)
RR(OVL_CONV_COEF(OMAP_DSS_VIDEO1, i));
- for (i = 0; i < 8; i++)
- RR(OVL_FIR_COEF_V(OMAP_DSS_VIDEO1, i));
+ if (dss_has_feature(FEAT_FIR_COEF_V)) {
+ for (i = 0; i < 8; i++)
+ RR(OVL_FIR_COEF_V(OMAP_DSS_VIDEO1, i));
+ }
if (dss_has_feature(FEAT_HANDLE_UV_SEPARATE)) {
RR(OVL_BA0_UV(OMAP_DSS_VIDEO1));
@@ -407,7 +474,8 @@
if (dss_has_feature(FEAT_ATTR2))
RR(OVL_ATTRIBUTES2(OMAP_DSS_VIDEO1));
- RR(OVL_PRELOAD(OMAP_DSS_VIDEO1));
+ if (dss_has_feature(FEAT_PRELOAD))
+ RR(OVL_PRELOAD(OMAP_DSS_VIDEO1));
/* VID2 */
RR(OVL_BA0(OMAP_DSS_VIDEO2));
@@ -432,8 +500,10 @@
for (i = 0; i < 5; i++)
RR(OVL_CONV_COEF(OMAP_DSS_VIDEO2, i));
- for (i = 0; i < 8; i++)
- RR(OVL_FIR_COEF_V(OMAP_DSS_VIDEO2, i));
+ if (dss_has_feature(FEAT_FIR_COEF_V)) {
+ for (i = 0; i < 8; i++)
+ RR(OVL_FIR_COEF_V(OMAP_DSS_VIDEO2, i));
+ }
if (dss_has_feature(FEAT_HANDLE_UV_SEPARATE)) {
RR(OVL_BA0_UV(OMAP_DSS_VIDEO2));
@@ -454,7 +524,8 @@
if (dss_has_feature(FEAT_ATTR2))
RR(OVL_ATTRIBUTES2(OMAP_DSS_VIDEO2));
- RR(OVL_PRELOAD(OMAP_DSS_VIDEO2));
+ if (dss_has_feature(FEAT_PRELOAD))
+ RR(OVL_PRELOAD(OMAP_DSS_VIDEO2));
if (dss_has_feature(FEAT_CORE_CLK_DIV))
RR(DIVISOR);
@@ -471,19 +542,35 @@
* the context is fully restored
*/
RR(IRQENABLE);
+
+ DSSDBG("context restored\n");
}
#undef SR
#undef RR
-static inline void enable_clocks(bool enable)
+int dispc_runtime_get(void)
{
- if (enable)
- dss_clk_enable(DSS_CLK_ICK | DSS_CLK_FCK);
- else
- dss_clk_disable(DSS_CLK_ICK | DSS_CLK_FCK);
+ int r;
+
+ DSSDBG("dispc_runtime_get\n");
+
+ r = pm_runtime_get_sync(&dispc.pdev->dev);
+ WARN_ON(r < 0);
+ return r < 0 ? r : 0;
}
+void dispc_runtime_put(void)
+{
+ int r;
+
+ DSSDBG("dispc_runtime_put\n");
+
+ r = pm_runtime_put(&dispc.pdev->dev);
+ WARN_ON(r < 0);
+}
+
+
bool dispc_go_busy(enum omap_channel channel)
{
int bit;
@@ -505,8 +592,6 @@
int bit;
bool enable_bit, go_bit;
- enable_clocks(1);
-
if (channel == OMAP_DSS_CHANNEL_LCD ||
channel == OMAP_DSS_CHANNEL_LCD2)
bit = 0; /* LCDENABLE */
@@ -520,7 +605,7 @@
enable_bit = REG_GET(DISPC_CONTROL, bit, bit) == 1;
if (!enable_bit)
- goto end;
+ return;
if (channel == OMAP_DSS_CHANNEL_LCD ||
channel == OMAP_DSS_CHANNEL_LCD2)
@@ -535,7 +620,7 @@
if (go_bit) {
DSSERR("GO bit not down for channel %d\n", channel);
- goto end;
+ return;
}
DSSDBG("GO %s\n", channel == OMAP_DSS_CHANNEL_LCD ? "LCD" :
@@ -545,8 +630,6 @@
REG_FLD_MOD(DISPC_CONTROL2, 1, bit, bit);
else
REG_FLD_MOD(DISPC_CONTROL, 1, bit, bit);
-end:
- enable_clocks(0);
}
static void _dispc_write_firh_reg(enum omap_plane plane, int reg, u32 value)
@@ -920,7 +1003,7 @@
REG_FLD_MOD(DISPC_OVL_ATTRIBUTES(plane), m, 4, 1);
}
-static void _dispc_set_channel_out(enum omap_plane plane,
+void dispc_set_channel_out(enum omap_plane plane,
enum omap_channel channel)
{
int shift;
@@ -967,13 +1050,10 @@
dispc_write_reg(DISPC_OVL_ATTRIBUTES(plane), val);
}
-void dispc_set_burst_size(enum omap_plane plane,
+static void dispc_set_burst_size(enum omap_plane plane,
enum omap_burst_size burst_size)
{
int shift;
- u32 val;
-
- enable_clocks(1);
switch (plane) {
case OMAP_DSS_GFX:
@@ -988,11 +1068,24 @@
return;
}
- val = dispc_read_reg(DISPC_OVL_ATTRIBUTES(plane));
- val = FLD_MOD(val, burst_size, shift+1, shift);
- dispc_write_reg(DISPC_OVL_ATTRIBUTES(plane), val);
+ REG_FLD_MOD(DISPC_OVL_ATTRIBUTES(plane), burst_size, shift + 1, shift);
+}
- enable_clocks(0);
+static void dispc_configure_burst_sizes(void)
+{
+ int i;
+ const int burst_size = BURST_SIZE_X8;
+
+ /* Configure burst size always to maximum size */
+ for (i = 0; i < omap_dss_get_num_overlays(); ++i)
+ dispc_set_burst_size(i, burst_size);
+}
+
+u32 dispc_get_burst_size(enum omap_plane plane)
+{
+ unsigned unit = dss_feat_get_burst_size_unit();
+ /* burst multiplier is always x8 (see dispc_configure_burst_sizes()) */
+ return unit * 8;
}
void dispc_enable_gamma_table(bool enable)
@@ -1009,6 +1102,40 @@
REG_FLD_MOD(DISPC_CONFIG, enable, 9, 9);
}
+void dispc_enable_cpr(enum omap_channel channel, bool enable)
+{
+ u16 reg;
+
+ if (channel == OMAP_DSS_CHANNEL_LCD)
+ reg = DISPC_CONFIG;
+ else if (channel == OMAP_DSS_CHANNEL_LCD2)
+ reg = DISPC_CONFIG2;
+ else
+ return;
+
+ REG_FLD_MOD(reg, enable, 15, 15);
+}
+
+void dispc_set_cpr_coef(enum omap_channel channel,
+ struct omap_dss_cpr_coefs *coefs)
+{
+ u32 coef_r, coef_g, coef_b;
+
+ if (channel != OMAP_DSS_CHANNEL_LCD && channel != OMAP_DSS_CHANNEL_LCD2)
+ return;
+
+ coef_r = FLD_VAL(coefs->rr, 31, 22) | FLD_VAL(coefs->rg, 20, 11) |
+ FLD_VAL(coefs->rb, 9, 0);
+ coef_g = FLD_VAL(coefs->gr, 31, 22) | FLD_VAL(coefs->gg, 20, 11) |
+ FLD_VAL(coefs->gb, 9, 0);
+ coef_b = FLD_VAL(coefs->br, 31, 22) | FLD_VAL(coefs->bg, 20, 11) |
+ FLD_VAL(coefs->bb, 9, 0);
+
+ dispc_write_reg(DISPC_CPR_COEF_R(channel), coef_r);
+ dispc_write_reg(DISPC_CPR_COEF_G(channel), coef_g);
+ dispc_write_reg(DISPC_CPR_COEF_B(channel), coef_b);
+}
+
static void _dispc_set_vid_color_conv(enum omap_plane plane, bool enable)
{
u32 val;
@@ -1029,9 +1156,7 @@
else
bit = 10;
- enable_clocks(1);
REG_FLD_MOD(DISPC_OVL_ATTRIBUTES(plane), enable, bit, bit);
- enable_clocks(0);
}
void dispc_set_lcd_size(enum omap_channel channel, u16 width, u16 height)
@@ -1039,9 +1164,7 @@
u32 val;
BUG_ON((width > (1 << 11)) || (height > (1 << 11)));
val = FLD_VAL(height - 1, 26, 16) | FLD_VAL(width - 1, 10, 0);
- enable_clocks(1);
dispc_write_reg(DISPC_SIZE_MGR(channel), val);
- enable_clocks(0);
}
void dispc_set_digit_size(u16 width, u16 height)
@@ -1049,9 +1172,7 @@
u32 val;
BUG_ON((width > (1 << 11)) || (height > (1 << 11)));
val = FLD_VAL(height - 1, 26, 16) | FLD_VAL(width - 1, 10, 0);
- enable_clocks(1);
dispc_write_reg(DISPC_SIZE_MGR(OMAP_DSS_CHANNEL_DIGIT), val);
- enable_clocks(0);
}
static void dispc_read_plane_fifo_sizes(void)
@@ -1059,18 +1180,17 @@
u32 size;
int plane;
u8 start, end;
+ u32 unit;
- enable_clocks(1);
+ unit = dss_feat_get_buffer_size_unit();
dss_feat_get_reg_field(FEAT_REG_FIFOSIZE, &start, &end);
for (plane = 0; plane < ARRAY_SIZE(dispc.fifo_size); ++plane) {
- size = FLD_GET(dispc_read_reg(DISPC_OVL_FIFO_SIZE_STATUS(plane)),
- start, end);
+ size = REG_GET(DISPC_OVL_FIFO_SIZE_STATUS(plane), start, end);
+ size *= unit;
dispc.fifo_size[plane] = size;
}
-
- enable_clocks(0);
}
u32 dispc_get_plane_fifo_size(enum omap_plane plane)
@@ -1078,15 +1198,22 @@
return dispc.fifo_size[plane];
}
-void dispc_setup_plane_fifo(enum omap_plane plane, u32 low, u32 high)
+void dispc_set_fifo_threshold(enum omap_plane plane, u32 low, u32 high)
{
u8 hi_start, hi_end, lo_start, lo_end;
+ u32 unit;
+
+ unit = dss_feat_get_buffer_size_unit();
+
+ WARN_ON(low % unit != 0);
+ WARN_ON(high % unit != 0);
+
+ low /= unit;
+ high /= unit;
dss_feat_get_reg_field(FEAT_REG_FIFOHIGHTHRESHOLD, &hi_start, &hi_end);
dss_feat_get_reg_field(FEAT_REG_FIFOLOWTHRESHOLD, &lo_start, &lo_end);
- enable_clocks(1);
-
DSSDBG("fifo(%d) low/high old %u/%u, new %u/%u\n",
plane,
REG_GET(DISPC_OVL_FIFO_THRESHOLD(plane),
@@ -1098,18 +1225,12 @@
dispc_write_reg(DISPC_OVL_FIFO_THRESHOLD(plane),
FLD_VAL(high, hi_start, hi_end) |
FLD_VAL(low, lo_start, lo_end));
-
- enable_clocks(0);
}
void dispc_enable_fifomerge(bool enable)
{
- enable_clocks(1);
-
DSSDBG("FIFO merge %s\n", enable ? "enabled" : "disabled");
REG_FLD_MOD(DISPC_CONFIG, enable ? 1 : 0, 14, 14);
-
- enable_clocks(0);
}
static void _dispc_set_fir(enum omap_plane plane,
@@ -1729,14 +1850,7 @@
return dispc_pclk_rate(channel) * vf * hf;
}
-void dispc_set_channel_out(enum omap_plane plane, enum omap_channel channel_out)
-{
- enable_clocks(1);
- _dispc_set_channel_out(plane, channel_out);
- enable_clocks(0);
-}
-
-static int _dispc_setup_plane(enum omap_plane plane,
+int dispc_setup_plane(enum omap_plane plane,
u32 paddr, u16 screen_width,
u16 pos_x, u16 pos_y,
u16 width, u16 height,
@@ -1744,7 +1858,7 @@
enum omap_color_mode color_mode,
bool ilace,
enum omap_dss_rotation_type rotation_type,
- u8 rotation, int mirror,
+ u8 rotation, bool mirror,
u8 global_alpha, u8 pre_mult_alpha,
enum omap_channel channel, u32 puv_addr)
{
@@ -1758,6 +1872,14 @@
u16 frame_height = height;
unsigned int field_offset = 0;
+ DSSDBG("dispc_setup_plane %d, pa %x, sw %d, %d,%d, %dx%d -> "
+ "%dx%d, ilace %d, cmode %x, rot %d, mir %d chan %d\n",
+ plane, paddr, screen_width, pos_x, pos_y,
+ width, height,
+ out_width, out_height,
+ ilace, color_mode,
+ rotation, mirror, channel);
+
if (paddr == 0)
return -EINVAL;
@@ -1903,9 +2025,13 @@
return 0;
}
-static void _dispc_enable_plane(enum omap_plane plane, bool enable)
+int dispc_enable_plane(enum omap_plane plane, bool enable)
{
+ DSSDBG("dispc_enable_plane %d, %d\n", plane, enable);
+
REG_FLD_MOD(DISPC_OVL_ATTRIBUTES(plane), enable ? 1 : 0, 0, 0);
+
+ return 0;
}
static void dispc_disable_isr(void *data, u32 mask)
@@ -1929,8 +2055,6 @@
int r;
u32 irq;
- enable_clocks(1);
-
/* When we disable LCD output, we need to wait until frame is done.
* Otherwise the DSS is still working, and turning off the clocks
* prevents DSS from going to OFF mode */
@@ -1964,8 +2088,6 @@
if (r)
DSSERR("failed to unregister FRAMEDONE isr\n");
}
-
- enable_clocks(0);
}
static void _enable_digit_out(bool enable)
@@ -1978,12 +2100,8 @@
struct completion frame_done_completion;
int r;
- enable_clocks(1);
-
- if (REG_GET(DISPC_CONTROL, 1, 1) == enable) {
- enable_clocks(0);
+ if (REG_GET(DISPC_CONTROL, 1, 1) == enable)
return;
- }
if (enable) {
unsigned long flags;
@@ -2035,8 +2153,6 @@
_omap_dispc_set_irqs();
spin_unlock_irqrestore(&dispc.irq_lock, flags);
}
-
- enable_clocks(0);
}
bool dispc_is_channel_enabled(enum omap_channel channel)
@@ -2067,9 +2183,7 @@
if (!dss_has_feature(FEAT_LCDENABLEPOL))
return;
- enable_clocks(1);
REG_FLD_MOD(DISPC_CONTROL, act_high ? 1 : 0, 29, 29);
- enable_clocks(0);
}
void dispc_lcd_enable_signal(bool enable)
@@ -2077,9 +2191,7 @@
if (!dss_has_feature(FEAT_LCDENABLESIGNAL))
return;
- enable_clocks(1);
REG_FLD_MOD(DISPC_CONTROL, enable ? 1 : 0, 28, 28);
- enable_clocks(0);
}
void dispc_pck_free_enable(bool enable)
@@ -2087,19 +2199,15 @@
if (!dss_has_feature(FEAT_PCKFREEENABLE))
return;
- enable_clocks(1);
REG_FLD_MOD(DISPC_CONTROL, enable ? 1 : 0, 27, 27);
- enable_clocks(0);
}
void dispc_enable_fifohandcheck(enum omap_channel channel, bool enable)
{
- enable_clocks(1);
if (channel == OMAP_DSS_CHANNEL_LCD2)
REG_FLD_MOD(DISPC_CONFIG2, enable ? 1 : 0, 16, 16);
else
REG_FLD_MOD(DISPC_CONFIG, enable ? 1 : 0, 16, 16);
- enable_clocks(0);
}
@@ -2122,27 +2230,21 @@
return;
}
- enable_clocks(1);
if (channel == OMAP_DSS_CHANNEL_LCD2)
REG_FLD_MOD(DISPC_CONTROL2, mode, 3, 3);
else
REG_FLD_MOD(DISPC_CONTROL, mode, 3, 3);
- enable_clocks(0);
}
void dispc_set_loadmode(enum omap_dss_load_mode mode)
{
- enable_clocks(1);
REG_FLD_MOD(DISPC_CONFIG, mode, 2, 1);
- enable_clocks(0);
}
void dispc_set_default_color(enum omap_channel channel, u32 color)
{
- enable_clocks(1);
dispc_write_reg(DISPC_DEFAULT_COLOR(channel), color);
- enable_clocks(0);
}
u32 dispc_get_default_color(enum omap_channel channel)
@@ -2153,9 +2255,7 @@
channel != OMAP_DSS_CHANNEL_LCD &&
channel != OMAP_DSS_CHANNEL_LCD2);
- enable_clocks(1);
l = dispc_read_reg(DISPC_DEFAULT_COLOR(channel));
- enable_clocks(0);
return l;
}
@@ -2164,7 +2264,6 @@
enum omap_dss_trans_key_type type,
u32 trans_key)
{
- enable_clocks(1);
if (ch == OMAP_DSS_CHANNEL_LCD)
REG_FLD_MOD(DISPC_CONFIG, type, 11, 11);
else if (ch == OMAP_DSS_CHANNEL_DIGIT)
@@ -2173,14 +2272,12 @@
REG_FLD_MOD(DISPC_CONFIG2, type, 11, 11);
dispc_write_reg(DISPC_TRANS_COLOR(ch), trans_key);
- enable_clocks(0);
}
void dispc_get_trans_key(enum omap_channel ch,
enum omap_dss_trans_key_type *type,
u32 *trans_key)
{
- enable_clocks(1);
if (type) {
if (ch == OMAP_DSS_CHANNEL_LCD)
*type = REG_GET(DISPC_CONFIG, 11, 11);
@@ -2194,33 +2291,28 @@
if (trans_key)
*trans_key = dispc_read_reg(DISPC_TRANS_COLOR(ch));
- enable_clocks(0);
}
void dispc_enable_trans_key(enum omap_channel ch, bool enable)
{
- enable_clocks(1);
if (ch == OMAP_DSS_CHANNEL_LCD)
REG_FLD_MOD(DISPC_CONFIG, enable, 10, 10);
else if (ch == OMAP_DSS_CHANNEL_DIGIT)
REG_FLD_MOD(DISPC_CONFIG, enable, 12, 12);
else /* OMAP_DSS_CHANNEL_LCD2 */
REG_FLD_MOD(DISPC_CONFIG2, enable, 10, 10);
- enable_clocks(0);
}
void dispc_enable_alpha_blending(enum omap_channel ch, bool enable)
{
if (!dss_has_feature(FEAT_GLOBAL_ALPHA))
return;
- enable_clocks(1);
if (ch == OMAP_DSS_CHANNEL_LCD)
REG_FLD_MOD(DISPC_CONFIG, enable, 18, 18);
else if (ch == OMAP_DSS_CHANNEL_DIGIT)
REG_FLD_MOD(DISPC_CONFIG, enable, 19, 19);
else /* OMAP_DSS_CHANNEL_LCD2 */
REG_FLD_MOD(DISPC_CONFIG2, enable, 18, 18);
- enable_clocks(0);
}
bool dispc_alpha_blending_enabled(enum omap_channel ch)
{
@@ -2229,7 +2321,6 @@
if (!dss_has_feature(FEAT_GLOBAL_ALPHA))
return false;
- enable_clocks(1);
if (ch == OMAP_DSS_CHANNEL_LCD)
enabled = REG_GET(DISPC_CONFIG, 18, 18);
else if (ch == OMAP_DSS_CHANNEL_DIGIT)
@@ -2238,7 +2329,6 @@
enabled = REG_GET(DISPC_CONFIG2, 18, 18);
else
BUG();
- enable_clocks(0);
return enabled;
}
@@ -2248,7 +2338,6 @@
{
bool enabled;
- enable_clocks(1);
if (ch == OMAP_DSS_CHANNEL_LCD)
enabled = REG_GET(DISPC_CONFIG, 10, 10);
else if (ch == OMAP_DSS_CHANNEL_DIGIT)
@@ -2257,7 +2346,6 @@
enabled = REG_GET(DISPC_CONFIG2, 10, 10);
else
BUG();
- enable_clocks(0);
return enabled;
}
@@ -2285,12 +2373,10 @@
return;
}
- enable_clocks(1);
if (channel == OMAP_DSS_CHANNEL_LCD2)
REG_FLD_MOD(DISPC_CONTROL2, code, 9, 8);
else
REG_FLD_MOD(DISPC_CONTROL, code, 9, 8);
- enable_clocks(0);
}
void dispc_set_parallel_interface_mode(enum omap_channel channel,
@@ -2322,8 +2408,6 @@
return;
}
- enable_clocks(1);
-
if (channel == OMAP_DSS_CHANNEL_LCD2) {
l = dispc_read_reg(DISPC_CONTROL2);
l = FLD_MOD(l, stallmode, 11, 11);
@@ -2335,8 +2419,6 @@
l = FLD_MOD(l, gpout1, 16, 16);
dispc_write_reg(DISPC_CONTROL, l);
}
-
- enable_clocks(0);
}
static bool _dispc_lcd_timings_ok(int hsw, int hfp, int hbp,
@@ -2389,10 +2471,8 @@
FLD_VAL(vbp, 31, 20);
}
- enable_clocks(1);
dispc_write_reg(DISPC_TIMING_H(channel), timing_h);
dispc_write_reg(DISPC_TIMING_V(channel), timing_v);
- enable_clocks(0);
}
/* change name to mode? */
@@ -2435,10 +2515,8 @@
BUG_ON(lck_div < 1);
BUG_ON(pck_div < 2);
- enable_clocks(1);
dispc_write_reg(DISPC_DIVISORo(channel),
FLD_VAL(lck_div, 23, 16) | FLD_VAL(pck_div, 7, 0));
- enable_clocks(0);
}
static void dispc_get_lcd_divisor(enum omap_channel channel, int *lck_div,
@@ -2457,7 +2535,7 @@
switch (dss_get_dispc_clk_source()) {
case OMAP_DSS_CLK_SRC_FCK:
- r = dss_clk_get_rate(DSS_CLK_FCK);
+ r = clk_get_rate(dispc.dss_clk);
break;
case OMAP_DSS_CLK_SRC_DSI_PLL_HSDIV_DISPC:
dsidev = dsi_get_dsidev_from_id(0);
@@ -2487,7 +2565,7 @@
switch (dss_get_lcd_clk_source(channel)) {
case OMAP_DSS_CLK_SRC_FCK:
- r = dss_clk_get_rate(DSS_CLK_FCK);
+ r = clk_get_rate(dispc.dss_clk);
break;
case OMAP_DSS_CLK_SRC_DSI_PLL_HSDIV_DISPC:
dsidev = dsi_get_dsidev_from_id(0);
@@ -2526,7 +2604,8 @@
enum omap_dss_clk_source dispc_clk_src = dss_get_dispc_clk_source();
enum omap_dss_clk_source lcd_clk_src;
- enable_clocks(1);
+ if (dispc_runtime_get())
+ return;
seq_printf(s, "- DISPC -\n");
@@ -2574,7 +2653,8 @@
seq_printf(s, "pck\t\t%-16lupck div\t%u\n",
dispc_pclk_rate(OMAP_DSS_CHANNEL_LCD2), pcd);
}
- enable_clocks(0);
+
+ dispc_runtime_put();
}
#ifdef CONFIG_OMAP2_DSS_COLLECT_IRQ_STATS
@@ -2629,7 +2709,8 @@
{
#define DUMPREG(r) seq_printf(s, "%-50s %08x\n", #r, dispc_read_reg(r))
- dss_clk_enable(DSS_CLK_ICK | DSS_CLK_FCK);
+ if (dispc_runtime_get())
+ return;
DUMPREG(DISPC_REVISION);
DUMPREG(DISPC_SYSCONFIG);
@@ -2649,7 +2730,8 @@
DUMPREG(DISPC_TIMING_V(OMAP_DSS_CHANNEL_LCD));
DUMPREG(DISPC_POL_FREQ(OMAP_DSS_CHANNEL_LCD));
DUMPREG(DISPC_DIVISORo(OMAP_DSS_CHANNEL_LCD));
- DUMPREG(DISPC_GLOBAL_ALPHA);
+ if (dss_has_feature(FEAT_GLOBAL_ALPHA))
+ DUMPREG(DISPC_GLOBAL_ALPHA);
DUMPREG(DISPC_SIZE_MGR(OMAP_DSS_CHANNEL_DIGIT));
DUMPREG(DISPC_SIZE_MGR(OMAP_DSS_CHANNEL_LCD));
if (dss_has_feature(FEAT_MGR_LCD2)) {
@@ -2680,20 +2762,25 @@
DUMPREG(DISPC_DATA_CYCLE2(OMAP_DSS_CHANNEL_LCD));
DUMPREG(DISPC_DATA_CYCLE3(OMAP_DSS_CHANNEL_LCD));
- DUMPREG(DISPC_CPR_COEF_R(OMAP_DSS_CHANNEL_LCD));
- DUMPREG(DISPC_CPR_COEF_G(OMAP_DSS_CHANNEL_LCD));
- DUMPREG(DISPC_CPR_COEF_B(OMAP_DSS_CHANNEL_LCD));
+ if (dss_has_feature(FEAT_CPR)) {
+ DUMPREG(DISPC_CPR_COEF_R(OMAP_DSS_CHANNEL_LCD));
+ DUMPREG(DISPC_CPR_COEF_G(OMAP_DSS_CHANNEL_LCD));
+ DUMPREG(DISPC_CPR_COEF_B(OMAP_DSS_CHANNEL_LCD));
+ }
if (dss_has_feature(FEAT_MGR_LCD2)) {
DUMPREG(DISPC_DATA_CYCLE1(OMAP_DSS_CHANNEL_LCD2));
DUMPREG(DISPC_DATA_CYCLE2(OMAP_DSS_CHANNEL_LCD2));
DUMPREG(DISPC_DATA_CYCLE3(OMAP_DSS_CHANNEL_LCD2));
- DUMPREG(DISPC_CPR_COEF_R(OMAP_DSS_CHANNEL_LCD2));
- DUMPREG(DISPC_CPR_COEF_G(OMAP_DSS_CHANNEL_LCD2));
- DUMPREG(DISPC_CPR_COEF_B(OMAP_DSS_CHANNEL_LCD2));
+ if (dss_has_feature(FEAT_CPR)) {
+ DUMPREG(DISPC_CPR_COEF_R(OMAP_DSS_CHANNEL_LCD2));
+ DUMPREG(DISPC_CPR_COEF_G(OMAP_DSS_CHANNEL_LCD2));
+ DUMPREG(DISPC_CPR_COEF_B(OMAP_DSS_CHANNEL_LCD2));
+ }
}
- DUMPREG(DISPC_OVL_PRELOAD(OMAP_DSS_GFX));
+ if (dss_has_feature(FEAT_PRELOAD))
+ DUMPREG(DISPC_OVL_PRELOAD(OMAP_DSS_GFX));
DUMPREG(DISPC_OVL_BA0(OMAP_DSS_VIDEO1));
DUMPREG(DISPC_OVL_BA1(OMAP_DSS_VIDEO1));
@@ -2744,14 +2831,16 @@
DUMPREG(DISPC_OVL_CONV_COEF(OMAP_DSS_VIDEO1, 2));
DUMPREG(DISPC_OVL_CONV_COEF(OMAP_DSS_VIDEO1, 3));
DUMPREG(DISPC_OVL_CONV_COEF(OMAP_DSS_VIDEO1, 4));
- DUMPREG(DISPC_OVL_FIR_COEF_V(OMAP_DSS_VIDEO1, 0));
- DUMPREG(DISPC_OVL_FIR_COEF_V(OMAP_DSS_VIDEO1, 1));
- DUMPREG(DISPC_OVL_FIR_COEF_V(OMAP_DSS_VIDEO1, 2));
- DUMPREG(DISPC_OVL_FIR_COEF_V(OMAP_DSS_VIDEO1, 3));
- DUMPREG(DISPC_OVL_FIR_COEF_V(OMAP_DSS_VIDEO1, 4));
- DUMPREG(DISPC_OVL_FIR_COEF_V(OMAP_DSS_VIDEO1, 5));
- DUMPREG(DISPC_OVL_FIR_COEF_V(OMAP_DSS_VIDEO1, 6));
- DUMPREG(DISPC_OVL_FIR_COEF_V(OMAP_DSS_VIDEO1, 7));
+ if (dss_has_feature(FEAT_FIR_COEF_V)) {
+ DUMPREG(DISPC_OVL_FIR_COEF_V(OMAP_DSS_VIDEO1, 0));
+ DUMPREG(DISPC_OVL_FIR_COEF_V(OMAP_DSS_VIDEO1, 1));
+ DUMPREG(DISPC_OVL_FIR_COEF_V(OMAP_DSS_VIDEO1, 2));
+ DUMPREG(DISPC_OVL_FIR_COEF_V(OMAP_DSS_VIDEO1, 3));
+ DUMPREG(DISPC_OVL_FIR_COEF_V(OMAP_DSS_VIDEO1, 4));
+ DUMPREG(DISPC_OVL_FIR_COEF_V(OMAP_DSS_VIDEO1, 5));
+ DUMPREG(DISPC_OVL_FIR_COEF_V(OMAP_DSS_VIDEO1, 6));
+ DUMPREG(DISPC_OVL_FIR_COEF_V(OMAP_DSS_VIDEO1, 7));
+ }
if (dss_has_feature(FEAT_HANDLE_UV_SEPARATE)) {
DUMPREG(DISPC_OVL_BA0_UV(OMAP_DSS_VIDEO1));
@@ -2812,14 +2901,17 @@
DUMPREG(DISPC_OVL_CONV_COEF(OMAP_DSS_VIDEO2, 2));
DUMPREG(DISPC_OVL_CONV_COEF(OMAP_DSS_VIDEO2, 3));
DUMPREG(DISPC_OVL_CONV_COEF(OMAP_DSS_VIDEO2, 4));
- DUMPREG(DISPC_OVL_FIR_COEF_V(OMAP_DSS_VIDEO2, 0));
- DUMPREG(DISPC_OVL_FIR_COEF_V(OMAP_DSS_VIDEO2, 1));
- DUMPREG(DISPC_OVL_FIR_COEF_V(OMAP_DSS_VIDEO2, 2));
- DUMPREG(DISPC_OVL_FIR_COEF_V(OMAP_DSS_VIDEO2, 3));
- DUMPREG(DISPC_OVL_FIR_COEF_V(OMAP_DSS_VIDEO2, 4));
- DUMPREG(DISPC_OVL_FIR_COEF_V(OMAP_DSS_VIDEO2, 5));
- DUMPREG(DISPC_OVL_FIR_COEF_V(OMAP_DSS_VIDEO2, 6));
- DUMPREG(DISPC_OVL_FIR_COEF_V(OMAP_DSS_VIDEO2, 7));
+
+ if (dss_has_feature(FEAT_FIR_COEF_V)) {
+ DUMPREG(DISPC_OVL_FIR_COEF_V(OMAP_DSS_VIDEO2, 0));
+ DUMPREG(DISPC_OVL_FIR_COEF_V(OMAP_DSS_VIDEO2, 1));
+ DUMPREG(DISPC_OVL_FIR_COEF_V(OMAP_DSS_VIDEO2, 2));
+ DUMPREG(DISPC_OVL_FIR_COEF_V(OMAP_DSS_VIDEO2, 3));
+ DUMPREG(DISPC_OVL_FIR_COEF_V(OMAP_DSS_VIDEO2, 4));
+ DUMPREG(DISPC_OVL_FIR_COEF_V(OMAP_DSS_VIDEO2, 5));
+ DUMPREG(DISPC_OVL_FIR_COEF_V(OMAP_DSS_VIDEO2, 6));
+ DUMPREG(DISPC_OVL_FIR_COEF_V(OMAP_DSS_VIDEO2, 7));
+ }
if (dss_has_feature(FEAT_HANDLE_UV_SEPARATE)) {
DUMPREG(DISPC_OVL_BA0_UV(OMAP_DSS_VIDEO2));
@@ -2858,10 +2950,12 @@
if (dss_has_feature(FEAT_ATTR2))
DUMPREG(DISPC_OVL_ATTRIBUTES2(OMAP_DSS_VIDEO2));
- DUMPREG(DISPC_OVL_PRELOAD(OMAP_DSS_VIDEO1));
- DUMPREG(DISPC_OVL_PRELOAD(OMAP_DSS_VIDEO2));
+ if (dss_has_feature(FEAT_PRELOAD)) {
+ DUMPREG(DISPC_OVL_PRELOAD(OMAP_DSS_VIDEO1));
+ DUMPREG(DISPC_OVL_PRELOAD(OMAP_DSS_VIDEO2));
+ }
- dss_clk_disable(DSS_CLK_ICK | DSS_CLK_FCK);
+ dispc_runtime_put();
#undef DUMPREG
}
@@ -2882,9 +2976,7 @@
l |= FLD_VAL(acbi, 11, 8);
l |= FLD_VAL(acb, 7, 0);
- enable_clocks(1);
dispc_write_reg(DISPC_POL_FREQ(channel), l);
- enable_clocks(0);
}
void dispc_set_pol_freq(enum omap_channel channel,
@@ -3005,15 +3097,11 @@
mask |= isr_data->mask;
}
- enable_clocks(1);
-
old_mask = dispc_read_reg(DISPC_IRQENABLE);
/* clear the irqstatus for newly enabled irqs */
dispc_write_reg(DISPC_IRQSTATUS, (mask ^ old_mask) & mask);
dispc_write_reg(DISPC_IRQENABLE, mask);
-
- enable_clocks(0);
}
int omap_dispc_register_isr(omap_dispc_isr_t isr, void *arg, u32 mask)
@@ -3522,13 +3610,6 @@
{
u32 l;
- l = dispc_read_reg(DISPC_SYSCONFIG);
- l = FLD_MOD(l, 2, 13, 12); /* MIDLEMODE: smart standby */
- l = FLD_MOD(l, 2, 4, 3); /* SIDLEMODE: smart idle */
- l = FLD_MOD(l, 1, 2, 2); /* ENWAKEUP */
- l = FLD_MOD(l, 1, 0, 0); /* AUTOIDLE */
- dispc_write_reg(DISPC_SYSCONFIG, l);
-
/* Exclusively enable DISPC_CORE_CLK and set divider to 1 */
if (dss_has_feature(FEAT_CORE_CLK_DIV)) {
l = dispc_read_reg(DISPC_DIVISOR);
@@ -3552,58 +3633,8 @@
dispc_set_loadmode(OMAP_DSS_LOAD_FRAME_ONLY);
dispc_read_plane_fifo_sizes();
-}
-int dispc_enable_plane(enum omap_plane plane, bool enable)
-{
- DSSDBG("dispc_enable_plane %d, %d\n", plane, enable);
-
- enable_clocks(1);
- _dispc_enable_plane(plane, enable);
- enable_clocks(0);
-
- return 0;
-}
-
-int dispc_setup_plane(enum omap_plane plane,
- u32 paddr, u16 screen_width,
- u16 pos_x, u16 pos_y,
- u16 width, u16 height,
- u16 out_width, u16 out_height,
- enum omap_color_mode color_mode,
- bool ilace,
- enum omap_dss_rotation_type rotation_type,
- u8 rotation, bool mirror, u8 global_alpha,
- u8 pre_mult_alpha, enum omap_channel channel,
- u32 puv_addr)
-{
- int r = 0;
-
- DSSDBG("dispc_setup_plane %d, pa %x, sw %d, %d, %d, %dx%d -> "
- "%dx%d, ilace %d, cmode %x, rot %d, mir %d chan %d\n",
- plane, paddr, screen_width, pos_x, pos_y,
- width, height,
- out_width, out_height,
- ilace, color_mode,
- rotation, mirror, channel);
-
- enable_clocks(1);
-
- r = _dispc_setup_plane(plane,
- paddr, screen_width,
- pos_x, pos_y,
- width, height,
- out_width, out_height,
- color_mode, ilace,
- rotation_type,
- rotation, mirror,
- global_alpha,
- pre_mult_alpha,
- channel, puv_addr);
-
- enable_clocks(0);
-
- return r;
+ dispc_configure_burst_sizes();
}
/* DISPC HW IP initialisation */
@@ -3612,9 +3643,19 @@
u32 rev;
int r = 0;
struct resource *dispc_mem;
+ struct clk *clk;
dispc.pdev = pdev;
+ clk = clk_get(&pdev->dev, "fck");
+ if (IS_ERR(clk)) {
+ DSSERR("can't get fck\n");
+ r = PTR_ERR(clk);
+ goto err_get_clk;
+ }
+
+ dispc.dss_clk = clk;
+
spin_lock_init(&dispc.irq_lock);
#ifdef CONFIG_OMAP2_DSS_COLLECT_IRQ_STATS
@@ -3628,62 +3669,103 @@
if (!dispc_mem) {
DSSERR("can't get IORESOURCE_MEM DISPC\n");
r = -EINVAL;
- goto fail0;
+ goto err_ioremap;
}
dispc.base = ioremap(dispc_mem->start, resource_size(dispc_mem));
if (!dispc.base) {
DSSERR("can't ioremap DISPC\n");
r = -ENOMEM;
- goto fail0;
+ goto err_ioremap;
}
dispc.irq = platform_get_irq(dispc.pdev, 0);
if (dispc.irq < 0) {
DSSERR("platform_get_irq failed\n");
r = -ENODEV;
- goto fail1;
+ goto err_irq;
}
r = request_irq(dispc.irq, omap_dispc_irq_handler, IRQF_SHARED,
"OMAP DISPC", dispc.pdev);
if (r < 0) {
DSSERR("request_irq failed\n");
- goto fail1;
+ goto err_irq;
}
- enable_clocks(1);
+ pm_runtime_enable(&pdev->dev);
+
+ r = dispc_runtime_get();
+ if (r)
+ goto err_runtime_get;
_omap_dispc_initial_config();
_omap_dispc_initialize_irq();
- dispc_save_context();
-
rev = dispc_read_reg(DISPC_REVISION);
dev_dbg(&pdev->dev, "OMAP DISPC rev %d.%d\n",
FLD_GET(rev, 7, 4), FLD_GET(rev, 3, 0));
- enable_clocks(0);
+ dispc_runtime_put();
return 0;
-fail1:
+
+err_runtime_get:
+ pm_runtime_disable(&pdev->dev);
+ free_irq(dispc.irq, dispc.pdev);
+err_irq:
iounmap(dispc.base);
-fail0:
+err_ioremap:
+ clk_put(dispc.dss_clk);
+err_get_clk:
return r;
}
static int omap_dispchw_remove(struct platform_device *pdev)
{
+ pm_runtime_disable(&pdev->dev);
+
+ clk_put(dispc.dss_clk);
+
free_irq(dispc.irq, dispc.pdev);
iounmap(dispc.base);
return 0;
}
+static int dispc_runtime_suspend(struct device *dev)
+{
+ dispc_save_context();
+ clk_disable(dispc.dss_clk);
+ dss_runtime_put();
+
+ return 0;
+}
+
+static int dispc_runtime_resume(struct device *dev)
+{
+ int r;
+
+ r = dss_runtime_get();
+ if (r < 0)
+ return r;
+
+ clk_enable(dispc.dss_clk);
+ dispc_restore_context();
+
+ return 0;
+}
+
+static const struct dev_pm_ops dispc_pm_ops = {
+ .runtime_suspend = dispc_runtime_suspend,
+ .runtime_resume = dispc_runtime_resume,
+};
+
static struct platform_driver omap_dispchw_driver = {
.probe = omap_dispchw_probe,
.remove = omap_dispchw_remove,
.driver = {
.name = "omapdss_dispc",
.owner = THIS_MODULE,
+ .pm = &dispc_pm_ops,
},
};
diff --git a/drivers/video/omap2/dss/display.c b/drivers/video/omap2/dss/display.c
index c2dfc8c..94495e4 100644
--- a/drivers/video/omap2/dss/display.c
+++ b/drivers/video/omap2/dss/display.c
@@ -29,6 +29,7 @@
#include <video/omapdss.h>
#include "dss.h"
+#include "dss_features.h"
static ssize_t display_enabled_show(struct device *dev,
struct device_attribute *attr, char *buf)
@@ -65,48 +66,6 @@
return size;
}
-static ssize_t display_upd_mode_show(struct device *dev,
- struct device_attribute *attr, char *buf)
-{
- struct omap_dss_device *dssdev = to_dss_device(dev);
- enum omap_dss_update_mode mode = OMAP_DSS_UPDATE_AUTO;
- if (dssdev->driver->get_update_mode)
- mode = dssdev->driver->get_update_mode(dssdev);
- return snprintf(buf, PAGE_SIZE, "%d\n", mode);
-}
-
-static ssize_t display_upd_mode_store(struct device *dev,
- struct device_attribute *attr,
- const char *buf, size_t size)
-{
- struct omap_dss_device *dssdev = to_dss_device(dev);
- int val, r;
- enum omap_dss_update_mode mode;
-
- if (!dssdev->driver->set_update_mode)
- return -EINVAL;
-
- r = kstrtoint(buf, 0, &val);
- if (r)
- return r;
-
- switch (val) {
- case OMAP_DSS_UPDATE_DISABLED:
- case OMAP_DSS_UPDATE_AUTO:
- case OMAP_DSS_UPDATE_MANUAL:
- mode = (enum omap_dss_update_mode)val;
- break;
- default:
- return -EINVAL;
- }
-
- r = dssdev->driver->set_update_mode(dssdev, mode);
- if (r)
- return r;
-
- return size;
-}
-
static ssize_t display_tear_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
@@ -294,8 +253,6 @@
static DEVICE_ATTR(enabled, S_IRUGO|S_IWUSR,
display_enabled_show, display_enabled_store);
-static DEVICE_ATTR(update_mode, S_IRUGO|S_IWUSR,
- display_upd_mode_show, display_upd_mode_store);
static DEVICE_ATTR(tear_elim, S_IRUGO|S_IWUSR,
display_tear_show, display_tear_store);
static DEVICE_ATTR(timings, S_IRUGO|S_IWUSR,
@@ -309,7 +266,6 @@
static struct device_attribute *display_sysfs_attrs[] = {
&dev_attr_enabled,
- &dev_attr_update_mode,
&dev_attr_tear_elim,
&dev_attr_timings,
&dev_attr_rotate,
@@ -327,16 +283,13 @@
EXPORT_SYMBOL(omapdss_default_get_resolution);
void default_get_overlay_fifo_thresholds(enum omap_plane plane,
- u32 fifo_size, enum omap_burst_size *burst_size,
+ u32 fifo_size, u32 burst_size,
u32 *fifo_low, u32 *fifo_high)
{
- unsigned burst_size_bytes;
+ unsigned buf_unit = dss_feat_get_buffer_size_unit();
- *burst_size = OMAP_DSS_BURST_16x32;
- burst_size_bytes = 16 * 32 / 8;
-
- *fifo_high = fifo_size - 1;
- *fifo_low = fifo_size - burst_size_bytes;
+ *fifo_high = fifo_size - buf_unit;
+ *fifo_low = fifo_size - burst_size;
}
int omapdss_default_get_recommended_bpp(struct omap_dss_device *dssdev)
diff --git a/drivers/video/omap2/dss/dpi.c b/drivers/video/omap2/dss/dpi.c
index ff6bd30..f053b18 100644
--- a/drivers/video/omap2/dss/dpi.c
+++ b/drivers/video/omap2/dss/dpi.c
@@ -23,7 +23,6 @@
#define DSS_SUBSYS_NAME "DPI"
#include <linux/kernel.h>
-#include <linux/clk.h>
#include <linux/delay.h>
#include <linux/err.h>
#include <linux/errno.h>
@@ -130,8 +129,6 @@
bool is_tft;
int r = 0;
- dss_clk_enable(DSS_CLK_ICK | DSS_CLK_FCK);
-
dispc_set_pol_freq(dssdev->manager->id, dssdev->panel.config,
dssdev->panel.acbi, dssdev->panel.acb);
@@ -144,7 +141,7 @@
r = dpi_set_dispc_clk(dssdev, is_tft, t->pixel_clock * 1000,
&fck, &lck_div, &pck_div);
if (r)
- goto err0;
+ return r;
pck = fck / lck_div / pck_div / 1000;
@@ -158,12 +155,10 @@
dispc_set_lcd_timings(dssdev->manager->id, t);
-err0:
- dss_clk_disable(DSS_CLK_ICK | DSS_CLK_FCK);
- return r;
+ return 0;
}
-static int dpi_basic_init(struct omap_dss_device *dssdev)
+static void dpi_basic_init(struct omap_dss_device *dssdev)
{
bool is_tft;
@@ -175,8 +170,6 @@
OMAP_DSS_LCD_DISPLAY_TFT : OMAP_DSS_LCD_DISPLAY_STN);
dispc_set_tft_data_lines(dssdev->manager->id,
dssdev->phy.dpi.data_lines);
-
- return 0;
}
int omapdss_dpi_display_enable(struct omap_dss_device *dssdev)
@@ -186,31 +179,38 @@
r = omap_dss_start_device(dssdev);
if (r) {
DSSERR("failed to start device\n");
- goto err0;
+ goto err_start_dev;
}
if (cpu_is_omap34xx()) {
r = regulator_enable(dpi.vdds_dsi_reg);
if (r)
- goto err1;
+ goto err_reg_enable;
}
- dss_clk_enable(DSS_CLK_ICK | DSS_CLK_FCK);
-
- r = dpi_basic_init(dssdev);
+ r = dss_runtime_get();
if (r)
- goto err2;
+ goto err_get_dss;
+
+ r = dispc_runtime_get();
+ if (r)
+ goto err_get_dispc;
+
+ dpi_basic_init(dssdev);
if (dpi_use_dsi_pll(dssdev)) {
- dss_clk_enable(DSS_CLK_SYSCK);
+ r = dsi_runtime_get(dpi.dsidev);
+ if (r)
+ goto err_get_dsi;
+
r = dsi_pll_init(dpi.dsidev, 0, 1);
if (r)
- goto err3;
+ goto err_dsi_pll_init;
}
r = dpi_set_mode(dssdev);
if (r)
- goto err4;
+ goto err_set_mode;
mdelay(2);
@@ -218,19 +218,22 @@
return 0;
-err4:
+err_set_mode:
if (dpi_use_dsi_pll(dssdev))
dsi_pll_uninit(dpi.dsidev, true);
-err3:
+err_dsi_pll_init:
if (dpi_use_dsi_pll(dssdev))
- dss_clk_disable(DSS_CLK_SYSCK);
-err2:
- dss_clk_disable(DSS_CLK_ICK | DSS_CLK_FCK);
+ dsi_runtime_put(dpi.dsidev);
+err_get_dsi:
+ dispc_runtime_put();
+err_get_dispc:
+ dss_runtime_put();
+err_get_dss:
if (cpu_is_omap34xx())
regulator_disable(dpi.vdds_dsi_reg);
-err1:
+err_reg_enable:
omap_dss_stop_device(dssdev);
-err0:
+err_start_dev:
return r;
}
EXPORT_SYMBOL(omapdss_dpi_display_enable);
@@ -242,10 +245,11 @@
if (dpi_use_dsi_pll(dssdev)) {
dss_select_dispc_clk_source(OMAP_DSS_CLK_SRC_FCK);
dsi_pll_uninit(dpi.dsidev, true);
- dss_clk_disable(DSS_CLK_SYSCK);
+ dsi_runtime_put(dpi.dsidev);
}
- dss_clk_disable(DSS_CLK_ICK | DSS_CLK_FCK);
+ dispc_runtime_put();
+ dss_runtime_put();
if (cpu_is_omap34xx())
regulator_disable(dpi.vdds_dsi_reg);
@@ -257,11 +261,26 @@
void dpi_set_timings(struct omap_dss_device *dssdev,
struct omap_video_timings *timings)
{
+ int r;
+
DSSDBG("dpi_set_timings\n");
dssdev->panel.timings = *timings;
if (dssdev->state == OMAP_DSS_DISPLAY_ACTIVE) {
+ r = dss_runtime_get();
+ if (r)
+ return;
+
+ r = dispc_runtime_get();
+ if (r) {
+ dss_runtime_put();
+ return;
+ }
+
dpi_set_mode(dssdev);
dispc_go(dssdev->manager->id);
+
+ dispc_runtime_put();
+ dss_runtime_put();
}
}
EXPORT_SYMBOL(dpi_set_timings);
diff --git a/drivers/video/omap2/dss/dsi.c b/drivers/video/omap2/dss/dsi.c
index 345757c..7adbbeb 100644
--- a/drivers/video/omap2/dss/dsi.c
+++ b/drivers/video/omap2/dss/dsi.c
@@ -36,6 +36,7 @@
#include <linux/sched.h>
#include <linux/slab.h>
#include <linux/debugfs.h>
+#include <linux/pm_runtime.h>
#include <video/omapdss.h>
#include <plat/clock.h>
@@ -267,8 +268,12 @@
struct dsi_data {
struct platform_device *pdev;
void __iomem *base;
+
int irq;
+ struct clk *dss_clk;
+ struct clk *sys_clk;
+
void (*dsi_mux_pads)(bool enable);
struct dsi_clock_info current_cinfo;
@@ -389,15 +394,6 @@
return __raw_readl(dsi->base + idx.idx);
}
-
-void dsi_save_context(void)
-{
-}
-
-void dsi_restore_context(void)
-{
-}
-
void dsi_bus_lock(struct omap_dss_device *dssdev)
{
struct platform_device *dsidev = dsi_get_dsidev_from_dssdev(dssdev);
@@ -493,9 +489,18 @@
total_bytes * 1000 / total_us);
}
#else
-#define dsi_perf_mark_setup(x)
-#define dsi_perf_mark_start(x)
-#define dsi_perf_show(x, y)
+static inline void dsi_perf_mark_setup(struct platform_device *dsidev)
+{
+}
+
+static inline void dsi_perf_mark_start(struct platform_device *dsidev)
+{
+}
+
+static inline void dsi_perf_show(struct platform_device *dsidev,
+ const char *name)
+{
+}
#endif
static void print_irq_status(u32 status)
@@ -1039,13 +1044,27 @@
return e;
}
-/* DSI func clock. this could also be dsi_pll_hsdiv_dsi_clk */
-static inline void enable_clocks(bool enable)
+int dsi_runtime_get(struct platform_device *dsidev)
{
- if (enable)
- dss_clk_enable(DSS_CLK_ICK | DSS_CLK_FCK);
- else
- dss_clk_disable(DSS_CLK_ICK | DSS_CLK_FCK);
+ int r;
+ struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);
+
+ DSSDBG("dsi_runtime_get\n");
+
+ r = pm_runtime_get_sync(&dsi->pdev->dev);
+ WARN_ON(r < 0);
+ return r < 0 ? r : 0;
+}
+
+void dsi_runtime_put(struct platform_device *dsidev)
+{
+ struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);
+ int r;
+
+ DSSDBG("dsi_runtime_put\n");
+
+ r = pm_runtime_put(&dsi->pdev->dev);
+ WARN_ON(r < 0);
}
/* source clock for DSI PLL. this could also be PCLKFREE */
@@ -1055,9 +1074,9 @@
struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);
if (enable)
- dss_clk_enable(DSS_CLK_SYSCK);
+ clk_enable(dsi->sys_clk);
else
- dss_clk_disable(DSS_CLK_SYSCK);
+ clk_disable(dsi->sys_clk);
if (enable && dsi->pll_locked) {
if (wait_for_bit_change(dsidev, DSI_PLL_STATUS, 1, 1) != 1)
@@ -1150,10 +1169,11 @@
{
unsigned long r;
int dsi_module = dsi_get_dsidev_id(dsidev);
+ struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);
if (dss_get_dsi_clk_source(dsi_module) == OMAP_DSS_CLK_SRC_FCK) {
/* DSI FCLK source is DSS_CLK_FCK */
- r = dss_clk_get_rate(DSS_CLK_FCK);
+ r = clk_get_rate(dsi->dss_clk);
} else {
/* DSI FCLK source is dsi_pll_hsdiv_dsi_clk */
r = dsi_get_pll_hsdiv_dsi_rate(dsidev);
@@ -1262,7 +1282,7 @@
return -EINVAL;
if (cinfo->use_sys_clk) {
- cinfo->clkin = dss_clk_get_rate(DSS_CLK_SYSCK);
+ cinfo->clkin = clk_get_rate(dsi->sys_clk);
/* XXX it is unclear if highfreq should be used
* with DSS_SYS_CLK source also */
cinfo->highfreq = 0;
@@ -1311,7 +1331,7 @@
int match = 0;
unsigned long dss_sys_clk, max_dss_fck;
- dss_sys_clk = dss_clk_get_rate(DSS_CLK_SYSCK);
+ dss_sys_clk = clk_get_rate(dsi->sys_clk);
max_dss_fck = dss_feat_get_param_max(FEAT_PARAM_DSS_FCK);
@@ -1601,7 +1621,6 @@
dsi->vdds_dsi_reg = vdds_dsi;
}
- enable_clocks(1);
dsi_enable_pll_clock(dsidev, 1);
/*
* Note: SCP CLK is not required on OMAP3, but it is required on OMAP4.
@@ -1653,7 +1672,6 @@
}
err0:
dsi_disable_scp_clk(dsidev);
- enable_clocks(0);
dsi_enable_pll_clock(dsidev, 0);
return r;
}
@@ -1671,7 +1689,6 @@
}
dsi_disable_scp_clk(dsidev);
- enable_clocks(0);
dsi_enable_pll_clock(dsidev, 0);
DSSDBG("PLL uninit done\n");
@@ -1688,7 +1705,8 @@
dispc_clk_src = dss_get_dispc_clk_source();
dsi_clk_src = dss_get_dsi_clk_source(dsi_module);
- enable_clocks(1);
+ if (dsi_runtime_get(dsidev))
+ return;
seq_printf(s, "- DSI%d PLL -\n", dsi_module + 1);
@@ -1731,7 +1749,7 @@
seq_printf(s, "LP_CLK\t\t%lu\n", cinfo->lp_clk);
- enable_clocks(0);
+ dsi_runtime_put(dsidev);
}
void dsi_dump_clocks(struct seq_file *s)
@@ -1873,7 +1891,8 @@
{
#define DUMPREG(r) seq_printf(s, "%-35s %08x\n", #r, dsi_read_reg(dsidev, r))
- dss_clk_enable(DSS_CLK_ICK | DSS_CLK_FCK);
+ if (dsi_runtime_get(dsidev))
+ return;
dsi_enable_scp_clk(dsidev);
DUMPREG(DSI_REVISION);
@@ -1947,7 +1966,7 @@
DUMPREG(DSI_PLL_CONFIGURATION2);
dsi_disable_scp_clk(dsidev);
- dss_clk_disable(DSS_CLK_ICK | DSS_CLK_FCK);
+ dsi_runtime_put(dsidev);
#undef DUMPREG
}
@@ -2463,28 +2482,6 @@
dsi->dsi_mux_pads(false);
}
-static int _dsi_wait_reset(struct platform_device *dsidev)
-{
- int t = 0;
-
- while (REG_GET(dsidev, DSI_SYSSTATUS, 0, 0) == 0) {
- if (++t > 5) {
- DSSERR("soft reset failed\n");
- return -ENODEV;
- }
- udelay(1);
- }
-
- return 0;
-}
-
-static int _dsi_reset(struct platform_device *dsidev)
-{
- /* Soft reset */
- REG_FLD_MOD(dsidev, DSI_SYSCONFIG, 1, 1, 1);
- return _dsi_wait_reset(dsidev);
-}
-
static void dsi_config_tx_fifo(struct platform_device *dsidev,
enum fifo_size size1, enum fifo_size size2,
enum fifo_size size3, enum fifo_size size4)
@@ -3386,6 +3383,10 @@
dsi_unregister_isr_cio(dsidev, dsi_completion_handler, &completion,
DSI_CIO_IRQ_ULPSACTIVENOT_ALL0);
+ /* Reset LANEx_ULPS_SIG2 */
+ REG_FLD_MOD(dsidev, DSI_COMPLEXIO_CFG2, (0 << 0) | (0 << 1) | (0 << 2),
+ 7, 5);
+
dsi_cio_power(dsidev, DSI_COMPLEXIO_POWER_ULPS);
dsi_if_enable(dsidev, false);
@@ -4198,22 +4199,6 @@
dsi_pll_uninit(dsidev, disconnect_lanes);
}
-static int dsi_core_init(struct platform_device *dsidev)
-{
- /* Autoidle */
- REG_FLD_MOD(dsidev, DSI_SYSCONFIG, 1, 0, 0);
-
- /* ENWAKEUP */
- REG_FLD_MOD(dsidev, DSI_SYSCONFIG, 1, 2, 2);
-
- /* SIDLEMODE smart-idle */
- REG_FLD_MOD(dsidev, DSI_SYSCONFIG, 2, 4, 3);
-
- _dsi_initialize_irq(dsidev);
-
- return 0;
-}
-
int omapdss_dsi_display_enable(struct omap_dss_device *dssdev)
{
struct platform_device *dsidev = dsi_get_dsidev_from_dssdev(dssdev);
@@ -4229,37 +4214,37 @@
r = omap_dss_start_device(dssdev);
if (r) {
DSSERR("failed to start device\n");
- goto err0;
+ goto err_start_dev;
}
- enable_clocks(1);
+ r = dsi_runtime_get(dsidev);
+ if (r)
+ goto err_get_dsi;
+
dsi_enable_pll_clock(dsidev, 1);
- r = _dsi_reset(dsidev);
- if (r)
- goto err1;
-
- dsi_core_init(dsidev);
+ _dsi_initialize_irq(dsidev);
r = dsi_display_init_dispc(dssdev);
if (r)
- goto err1;
+ goto err_init_dispc;
r = dsi_display_init_dsi(dssdev);
if (r)
- goto err2;
+ goto err_init_dsi;
mutex_unlock(&dsi->lock);
return 0;
-err2:
+err_init_dsi:
dsi_display_uninit_dispc(dssdev);
-err1:
- enable_clocks(0);
+err_init_dispc:
dsi_enable_pll_clock(dsidev, 0);
+ dsi_runtime_put(dsidev);
+err_get_dsi:
omap_dss_stop_device(dssdev);
-err0:
+err_start_dev:
mutex_unlock(&dsi->lock);
DSSDBG("dsi_display_enable FAILED\n");
return r;
@@ -4278,11 +4263,16 @@
mutex_lock(&dsi->lock);
+ dsi_sync_vc(dsidev, 0);
+ dsi_sync_vc(dsidev, 1);
+ dsi_sync_vc(dsidev, 2);
+ dsi_sync_vc(dsidev, 3);
+
dsi_display_uninit_dispc(dssdev);
dsi_display_uninit_dsi(dssdev, disconnect_lanes, enter_ulps);
- enable_clocks(0);
+ dsi_runtime_put(dsidev);
dsi_enable_pll_clock(dsidev, 0);
omap_dss_stop_device(dssdev);
@@ -4302,16 +4292,11 @@
EXPORT_SYMBOL(omapdss_dsi_enable_te);
void dsi_get_overlay_fifo_thresholds(enum omap_plane plane,
- u32 fifo_size, enum omap_burst_size *burst_size,
+ u32 fifo_size, u32 burst_size,
u32 *fifo_low, u32 *fifo_high)
{
- unsigned burst_size_bytes;
-
- *burst_size = OMAP_DSS_BURST_16x32;
- burst_size_bytes = 16 * 32 / 8;
-
- *fifo_high = fifo_size - burst_size_bytes;
- *fifo_low = fifo_size - burst_size_bytes * 2;
+ *fifo_high = fifo_size - burst_size;
+ *fifo_low = fifo_size - burst_size * 2;
}
int dsi_init_display(struct omap_dss_device *dssdev)
@@ -4437,7 +4422,47 @@
dsi->lpdiv_max = dss_feat_get_param_max(FEAT_PARAM_DSIPLL_LPDIV);
}
-static int dsi_init(struct platform_device *dsidev)
+static int dsi_get_clocks(struct platform_device *dsidev)
+{
+ struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);
+ struct clk *clk;
+
+ clk = clk_get(&dsidev->dev, "fck");
+ if (IS_ERR(clk)) {
+ DSSERR("can't get fck\n");
+ return PTR_ERR(clk);
+ }
+
+ dsi->dss_clk = clk;
+
+ if (cpu_is_omap34xx() || cpu_is_omap3630())
+ clk = clk_get(&dsidev->dev, "dss2_alwon_fck");
+ else
+ clk = clk_get(&dsidev->dev, "sys_clk");
+ if (IS_ERR(clk)) {
+ DSSERR("can't get sys_clk\n");
+ clk_put(dsi->dss_clk);
+ dsi->dss_clk = NULL;
+ return PTR_ERR(clk);
+ }
+
+ dsi->sys_clk = clk;
+
+ return 0;
+}
+
+static void dsi_put_clocks(struct platform_device *dsidev)
+{
+ struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);
+
+ if (dsi->dss_clk)
+ clk_put(dsi->dss_clk);
+ if (dsi->sys_clk)
+ clk_put(dsi->sys_clk);
+}
+
+/* DSI1 HW IP initialisation */
+static int omap_dsi1hw_probe(struct platform_device *dsidev)
{
struct omap_display_platform_data *dss_plat_data;
struct omap_dss_board_info *board_info;
@@ -4449,7 +4474,7 @@
dsi = kzalloc(sizeof(*dsi), GFP_KERNEL);
if (!dsi) {
r = -ENOMEM;
- goto err0;
+ goto err_alloc;
}
dsi->pdev = dsidev;
@@ -4472,6 +4497,12 @@
mutex_init(&dsi->lock);
sema_init(&dsi->bus_lock, 1);
+ r = dsi_get_clocks(dsidev);
+ if (r)
+ goto err_get_clk;
+
+ pm_runtime_enable(&dsidev->dev);
+
INIT_DELAYED_WORK_DEFERRABLE(&dsi->framedone_timeout_work,
dsi_framedone_timeout_work_callback);
@@ -4484,26 +4515,26 @@
if (!dsi_mem) {
DSSERR("can't get IORESOURCE_MEM DSI\n");
r = -EINVAL;
- goto err1;
+ goto err_ioremap;
}
dsi->base = ioremap(dsi_mem->start, resource_size(dsi_mem));
if (!dsi->base) {
DSSERR("can't ioremap DSI\n");
r = -ENOMEM;
- goto err1;
+ goto err_ioremap;
}
dsi->irq = platform_get_irq(dsi->pdev, 0);
if (dsi->irq < 0) {
DSSERR("platform_get_irq failed\n");
r = -ENODEV;
- goto err2;
+ goto err_get_irq;
}
r = request_irq(dsi->irq, omap_dsi_irq_handler, IRQF_SHARED,
dev_name(&dsidev->dev), dsi->pdev);
if (r < 0) {
DSSERR("request_irq failed\n");
- goto err2;
+ goto err_get_irq;
}
/* DSI VCs initialization */
@@ -4515,7 +4546,9 @@
dsi_calc_clock_param_ranges(dsidev);
- enable_clocks(1);
+ r = dsi_runtime_get(dsidev);
+ if (r)
+ goto err_get_dsi;
rev = dsi_read_reg(dsidev, DSI_REVISION);
dev_dbg(&dsidev->dev, "OMAP DSI rev %d.%d\n",
@@ -4523,21 +4556,32 @@
dsi->num_data_lanes = dsi_get_num_data_lanes(dsidev);
- enable_clocks(0);
+ dsi_runtime_put(dsidev);
return 0;
-err2:
+
+err_get_dsi:
+ free_irq(dsi->irq, dsi->pdev);
+err_get_irq:
iounmap(dsi->base);
-err1:
+err_ioremap:
+ pm_runtime_disable(&dsidev->dev);
+err_get_clk:
kfree(dsi);
-err0:
+err_alloc:
return r;
}
-static void dsi_exit(struct platform_device *dsidev)
+static int omap_dsi1hw_remove(struct platform_device *dsidev)
{
struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);
+ WARN_ON(dsi->scp_clk_refcount > 0);
+
+ pm_runtime_disable(&dsidev->dev);
+
+ dsi_put_clocks(dsidev);
+
if (dsi->vdds_dsi_reg != NULL) {
if (dsi->vdds_dsi_enabled) {
regulator_disable(dsi->vdds_dsi_reg);
@@ -4553,31 +4597,48 @@
kfree(dsi);
- DSSDBG("omap_dsi_exit\n");
+ return 0;
}
-/* DSI1 HW IP initialisation */
-static int omap_dsi1hw_probe(struct platform_device *dsidev)
+static int dsi_runtime_suspend(struct device *dev)
{
+ struct dsi_data *dsi = dsi_get_dsidrv_data(to_platform_device(dev));
+
+ clk_disable(dsi->dss_clk);
+
+ dispc_runtime_put();
+ dss_runtime_put();
+
+ return 0;
+}
+
+static int dsi_runtime_resume(struct device *dev)
+{
+ struct dsi_data *dsi = dsi_get_dsidrv_data(to_platform_device(dev));
int r;
- r = dsi_init(dsidev);
- if (r) {
- DSSERR("Failed to initialize DSI\n");
- goto err_dsi;
- }
-err_dsi:
+ r = dss_runtime_get();
+ if (r)
+ goto err_get_dss;
+
+ r = dispc_runtime_get();
+ if (r)
+ goto err_get_dispc;
+
+ clk_enable(dsi->dss_clk);
+
+ return 0;
+
+err_get_dispc:
+ dss_runtime_put();
+err_get_dss:
return r;
}
-static int omap_dsi1hw_remove(struct platform_device *dsidev)
-{
- struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);
-
- dsi_exit(dsidev);
- WARN_ON(dsi->scp_clk_refcount > 0);
- return 0;
-}
+static const struct dev_pm_ops dsi_pm_ops = {
+ .runtime_suspend = dsi_runtime_suspend,
+ .runtime_resume = dsi_runtime_resume,
+};
static struct platform_driver omap_dsi1hw_driver = {
.probe = omap_dsi1hw_probe,
@@ -4585,6 +4646,7 @@
.driver = {
.name = "omapdss_dsi1",
.owner = THIS_MODULE,
+ .pm = &dsi_pm_ops,
},
};
diff --git a/drivers/video/omap2/dss/dss.c b/drivers/video/omap2/dss/dss.c
index d9489d5..0f9c3a6 100644
--- a/drivers/video/omap2/dss/dss.c
+++ b/drivers/video/omap2/dss/dss.c
@@ -28,6 +28,8 @@
#include <linux/delay.h>
#include <linux/seq_file.h>
#include <linux/clk.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
#include <video/omapdss.h>
#include <plat/clock.h>
@@ -59,15 +61,9 @@
static struct {
struct platform_device *pdev;
void __iomem *base;
- int ctx_id;
struct clk *dpll4_m4_ck;
- struct clk *dss_ick;
- struct clk *dss_fck;
- struct clk *dss_sys_clk;
- struct clk *dss_tv_fck;
- struct clk *dss_video_fck;
- unsigned num_clks_enabled;
+ struct clk *dss_clk;
unsigned long cache_req_pck;
unsigned long cache_prate;
@@ -78,6 +74,7 @@
enum omap_dss_clk_source dispc_clk_source;
enum omap_dss_clk_source lcd_clk_source[MAX_DSS_LCD_MANAGERS];
+ bool ctx_valid;
u32 ctx[DSS_SZ_REGS / sizeof(u32)];
} dss;
@@ -87,13 +84,6 @@
[OMAP_DSS_CLK_SRC_FCK] = "DSS_FCK",
};
-static void dss_clk_enable_all_no_ctx(void);
-static void dss_clk_disable_all_no_ctx(void);
-static void dss_clk_enable_no_ctx(enum dss_clock clks);
-static void dss_clk_disable_no_ctx(enum dss_clock clks);
-
-static int _omap_dss_wait_reset(void);
-
static inline void dss_write_reg(const struct dss_reg idx, u32 val)
{
__raw_writel(val, dss.base + idx.idx);
@@ -109,12 +99,10 @@
#define RR(reg) \
dss_write_reg(DSS_##reg, dss.ctx[(DSS_##reg).idx / sizeof(u32)])
-void dss_save_context(void)
+static void dss_save_context(void)
{
- if (cpu_is_omap24xx())
- return;
+ DSSDBG("dss_save_context\n");
- SR(SYSCONFIG);
SR(CONTROL);
if (dss_feat_get_supported_displays(OMAP_DSS_CHANNEL_LCD) &
@@ -122,14 +110,19 @@
SR(SDI_CONTROL);
SR(PLL_CONTROL);
}
+
+ dss.ctx_valid = true;
+
+ DSSDBG("context saved\n");
}
-void dss_restore_context(void)
+static void dss_restore_context(void)
{
- if (_omap_dss_wait_reset())
- DSSERR("DSS not coming out of reset after sleep\n");
+ DSSDBG("dss_restore_context\n");
- RR(SYSCONFIG);
+ if (!dss.ctx_valid)
+ return;
+
RR(CONTROL);
if (dss_feat_get_supported_displays(OMAP_DSS_CHANNEL_LCD) &
@@ -137,6 +130,8 @@
RR(SDI_CONTROL);
RR(PLL_CONTROL);
}
+
+ DSSDBG("context restored\n");
}
#undef SR
@@ -234,6 +229,7 @@
return dss_generic_clk_source_names[clk_src];
}
+
void dss_dump_clocks(struct seq_file *s)
{
unsigned long dpll4_ck_rate;
@@ -241,13 +237,14 @@
const char *fclk_name, *fclk_real_name;
unsigned long fclk_rate;
- dss_clk_enable(DSS_CLK_ICK | DSS_CLK_FCK);
+ if (dss_runtime_get())
+ return;
seq_printf(s, "- DSS -\n");
fclk_name = dss_get_generic_clk_source_name(OMAP_DSS_CLK_SRC_FCK);
fclk_real_name = dss_feat_get_clk_source_name(OMAP_DSS_CLK_SRC_FCK);
- fclk_rate = dss_clk_get_rate(DSS_CLK_FCK);
+ fclk_rate = clk_get_rate(dss.dss_clk);
if (dss.dpll4_m4_ck) {
dpll4_ck_rate = clk_get_rate(clk_get_parent(dss.dpll4_m4_ck));
@@ -273,14 +270,15 @@
fclk_rate);
}
- dss_clk_disable(DSS_CLK_ICK | DSS_CLK_FCK);
+ dss_runtime_put();
}
void dss_dump_regs(struct seq_file *s)
{
#define DUMPREG(r) seq_printf(s, "%-35s %08x\n", #r, dss_read_reg(r))
- dss_clk_enable(DSS_CLK_ICK | DSS_CLK_FCK);
+ if (dss_runtime_get())
+ return;
DUMPREG(DSS_REVISION);
DUMPREG(DSS_SYSCONFIG);
@@ -294,7 +292,7 @@
DUMPREG(DSS_SDI_STATUS);
}
- dss_clk_disable(DSS_CLK_ICK | DSS_CLK_FCK);
+ dss_runtime_put();
#undef DUMPREG
}
@@ -437,7 +435,7 @@
} else {
if (cinfo->fck_div != 0)
return -EINVAL;
- cinfo->fck = dss_clk_get_rate(DSS_CLK_FCK);
+ cinfo->fck = clk_get_rate(dss.dss_clk);
}
return 0;
@@ -467,7 +465,7 @@
int dss_get_clock_div(struct dss_clock_info *cinfo)
{
- cinfo->fck = dss_clk_get_rate(DSS_CLK_FCK);
+ cinfo->fck = clk_get_rate(dss.dss_clk);
if (dss.dpll4_m4_ck) {
unsigned long prate;
@@ -512,7 +510,7 @@
max_dss_fck = dss_feat_get_param_max(FEAT_PARAM_DSS_FCK);
- fck = dss_clk_get_rate(DSS_CLK_FCK);
+ fck = clk_get_rate(dss.dss_clk);
if (req_pck == dss.cache_req_pck &&
((cpu_is_omap34xx() && prate == dss.cache_prate) ||
dss.cache_dss_cinfo.fck == fck)) {
@@ -539,7 +537,7 @@
if (dss.dpll4_m4_ck == NULL) {
struct dispc_clock_info cur_dispc;
/* XXX can we change the clock on omap2? */
- fck = dss_clk_get_rate(DSS_CLK_FCK);
+ fck = clk_get_rate(dss.dss_clk);
fck_div = 1;
dispc_find_clk_divs(is_tft, req_pck, fck, &cur_dispc);
@@ -616,28 +614,6 @@
return 0;
}
-static int _omap_dss_wait_reset(void)
-{
- int t = 0;
-
- while (REG_GET(DSS_SYSSTATUS, 0, 0) == 0) {
- if (++t > 1000) {
- DSSERR("soft reset failed\n");
- return -ENODEV;
- }
- udelay(1);
- }
-
- return 0;
-}
-
-static int _omap_dss_reset(void)
-{
- /* Soft reset */
- REG_FLD_MOD(DSS_SYSCONFIG, 1, 1, 1);
- return _omap_dss_wait_reset();
-}
-
void dss_set_venc_output(enum omap_dss_venc_type type)
{
int l = 0;
@@ -663,48 +639,127 @@
REG_FLD_MOD(DSS_CONTROL, hdmi, 15, 15); /* VENC_HDMI_SWITCH */
}
-static int dss_init(void)
+static int dss_get_clocks(void)
+{
+ struct clk *clk;
+ int r;
+
+ clk = clk_get(&dss.pdev->dev, "fck");
+ if (IS_ERR(clk)) {
+ DSSERR("can't get clock fck\n");
+ r = PTR_ERR(clk);
+ goto err;
+ }
+
+ dss.dss_clk = clk;
+
+ if (cpu_is_omap34xx()) {
+ clk = clk_get(NULL, "dpll4_m4_ck");
+ if (IS_ERR(clk)) {
+ DSSERR("Failed to get dpll4_m4_ck\n");
+ r = PTR_ERR(clk);
+ goto err;
+ }
+ } else if (cpu_is_omap44xx()) {
+ clk = clk_get(NULL, "dpll_per_m5x2_ck");
+ if (IS_ERR(clk)) {
+ DSSERR("Failed to get dpll_per_m5x2_ck\n");
+ r = PTR_ERR(clk);
+ goto err;
+ }
+ } else { /* omap24xx */
+ clk = NULL;
+ }
+
+ dss.dpll4_m4_ck = clk;
+
+ return 0;
+
+err:
+ if (dss.dss_clk)
+ clk_put(dss.dss_clk);
+ if (dss.dpll4_m4_ck)
+ clk_put(dss.dpll4_m4_ck);
+
+ return r;
+}
+
+static void dss_put_clocks(void)
+{
+ if (dss.dpll4_m4_ck)
+ clk_put(dss.dpll4_m4_ck);
+ clk_put(dss.dss_clk);
+}
+
+struct clk *dss_get_ick(void)
+{
+ return clk_get(&dss.pdev->dev, "ick");
+}
+
+int dss_runtime_get(void)
{
int r;
- u32 rev;
+
+ DSSDBG("dss_runtime_get\n");
+
+ r = pm_runtime_get_sync(&dss.pdev->dev);
+ WARN_ON(r < 0);
+ return r < 0 ? r : 0;
+}
+
+void dss_runtime_put(void)
+{
+ int r;
+
+ DSSDBG("dss_runtime_put\n");
+
+ r = pm_runtime_put(&dss.pdev->dev);
+ WARN_ON(r < 0);
+}
+
+/* DEBUGFS */
+#if defined(CONFIG_DEBUG_FS) && defined(CONFIG_OMAP2_DSS_DEBUG_SUPPORT)
+void dss_debug_dump_clocks(struct seq_file *s)
+{
+ dss_dump_clocks(s);
+ dispc_dump_clocks(s);
+#ifdef CONFIG_OMAP2_DSS_DSI
+ dsi_dump_clocks(s);
+#endif
+}
+#endif
+
+/* DSS HW IP initialisation */
+static int omap_dsshw_probe(struct platform_device *pdev)
+{
struct resource *dss_mem;
- struct clk *dpll4_m4_ck;
+ u32 rev;
+ int r;
+
+ dss.pdev = pdev;
dss_mem = platform_get_resource(dss.pdev, IORESOURCE_MEM, 0);
if (!dss_mem) {
DSSERR("can't get IORESOURCE_MEM DSS\n");
r = -EINVAL;
- goto fail0;
+ goto err_ioremap;
}
dss.base = ioremap(dss_mem->start, resource_size(dss_mem));
if (!dss.base) {
DSSERR("can't ioremap DSS\n");
r = -ENOMEM;
- goto fail0;
+ goto err_ioremap;
}
- /* disable LCD and DIGIT output. This seems to fix the synclost
- * problem that we get, if the bootloader starts the DSS and
- * the kernel resets it */
- omap_writel(omap_readl(0x48050440) & ~0x3, 0x48050440);
+ r = dss_get_clocks();
+ if (r)
+ goto err_clocks;
-#ifdef CONFIG_OMAP2_DSS_SLEEP_BEFORE_RESET
- /* We need to wait here a bit, otherwise we sometimes start to
- * get synclost errors, and after that only power cycle will
- * restore DSS functionality. I have no idea why this happens.
- * And we have to wait _before_ resetting the DSS, but after
- * enabling clocks.
- *
- * This bug was at least present on OMAP3430. It's unknown
- * if it happens on OMAP2 or OMAP3630.
- */
- msleep(50);
-#endif
+ pm_runtime_enable(&pdev->dev);
- _omap_dss_reset();
-
- /* autoidle */
- REG_FLD_MOD(DSS_SYSCONFIG, 1, 0, 0);
+ r = dss_runtime_get();
+ if (r)
+ goto err_runtime_get;
/* Select DPLL */
REG_FLD_MOD(DSS_CONTROL, 0, 0, 0);
@@ -714,404 +769,12 @@
REG_FLD_MOD(DSS_CONTROL, 1, 3, 3); /* venc clock 4x enable */
REG_FLD_MOD(DSS_CONTROL, 0, 2, 2); /* venc clock mode = normal */
#endif
- if (cpu_is_omap34xx()) {
- dpll4_m4_ck = clk_get(NULL, "dpll4_m4_ck");
- if (IS_ERR(dpll4_m4_ck)) {
- DSSERR("Failed to get dpll4_m4_ck\n");
- r = PTR_ERR(dpll4_m4_ck);
- goto fail1;
- }
- } else if (cpu_is_omap44xx()) {
- dpll4_m4_ck = clk_get(NULL, "dpll_per_m5x2_ck");
- if (IS_ERR(dpll4_m4_ck)) {
- DSSERR("Failed to get dpll4_m4_ck\n");
- r = PTR_ERR(dpll4_m4_ck);
- goto fail1;
- }
- } else { /* omap24xx */
- dpll4_m4_ck = NULL;
- }
-
- dss.dpll4_m4_ck = dpll4_m4_ck;
-
dss.dsi_clk_source[0] = OMAP_DSS_CLK_SRC_FCK;
dss.dsi_clk_source[1] = OMAP_DSS_CLK_SRC_FCK;
dss.dispc_clk_source = OMAP_DSS_CLK_SRC_FCK;
dss.lcd_clk_source[0] = OMAP_DSS_CLK_SRC_FCK;
dss.lcd_clk_source[1] = OMAP_DSS_CLK_SRC_FCK;
- dss_save_context();
-
- rev = dss_read_reg(DSS_REVISION);
- printk(KERN_INFO "OMAP DSS rev %d.%d\n",
- FLD_GET(rev, 7, 4), FLD_GET(rev, 3, 0));
-
- return 0;
-
-fail1:
- iounmap(dss.base);
-fail0:
- return r;
-}
-
-static void dss_exit(void)
-{
- if (dss.dpll4_m4_ck)
- clk_put(dss.dpll4_m4_ck);
-
- iounmap(dss.base);
-}
-
-/* CONTEXT */
-static int dss_get_ctx_id(void)
-{
- struct omap_display_platform_data *pdata = dss.pdev->dev.platform_data;
- int r;
-
- if (!pdata->board_data->get_last_off_on_transaction_id)
- return 0;
- r = pdata->board_data->get_last_off_on_transaction_id(&dss.pdev->dev);
- if (r < 0) {
- dev_err(&dss.pdev->dev, "getting transaction ID failed, "
- "will force context restore\n");
- r = -1;
- }
- return r;
-}
-
-int dss_need_ctx_restore(void)
-{
- int id = dss_get_ctx_id();
-
- if (id < 0 || id != dss.ctx_id) {
- DSSDBG("ctx id %d -> id %d\n",
- dss.ctx_id, id);
- dss.ctx_id = id;
- return 1;
- } else {
- return 0;
- }
-}
-
-static void save_all_ctx(void)
-{
- DSSDBG("save context\n");
-
- dss_clk_enable_no_ctx(DSS_CLK_ICK | DSS_CLK_FCK);
-
- dss_save_context();
- dispc_save_context();
-#ifdef CONFIG_OMAP2_DSS_DSI
- dsi_save_context();
-#endif
-
- dss_clk_disable_no_ctx(DSS_CLK_ICK | DSS_CLK_FCK);
-}
-
-static void restore_all_ctx(void)
-{
- DSSDBG("restore context\n");
-
- dss_clk_enable_all_no_ctx();
-
- dss_restore_context();
- dispc_restore_context();
-#ifdef CONFIG_OMAP2_DSS_DSI
- dsi_restore_context();
-#endif
-
- dss_clk_disable_all_no_ctx();
-}
-
-static int dss_get_clock(struct clk **clock, const char *clk_name)
-{
- struct clk *clk;
-
- clk = clk_get(&dss.pdev->dev, clk_name);
-
- if (IS_ERR(clk)) {
- DSSERR("can't get clock %s", clk_name);
- return PTR_ERR(clk);
- }
-
- *clock = clk;
-
- DSSDBG("clk %s, rate %ld\n", clk_name, clk_get_rate(clk));
-
- return 0;
-}
-
-static int dss_get_clocks(void)
-{
- int r;
- struct omap_display_platform_data *pdata = dss.pdev->dev.platform_data;
-
- dss.dss_ick = NULL;
- dss.dss_fck = NULL;
- dss.dss_sys_clk = NULL;
- dss.dss_tv_fck = NULL;
- dss.dss_video_fck = NULL;
-
- r = dss_get_clock(&dss.dss_ick, "ick");
- if (r)
- goto err;
-
- r = dss_get_clock(&dss.dss_fck, "fck");
- if (r)
- goto err;
-
- if (!pdata->opt_clock_available) {
- r = -ENODEV;
- goto err;
- }
-
- if (pdata->opt_clock_available("sys_clk")) {
- r = dss_get_clock(&dss.dss_sys_clk, "sys_clk");
- if (r)
- goto err;
- }
-
- if (pdata->opt_clock_available("tv_clk")) {
- r = dss_get_clock(&dss.dss_tv_fck, "tv_clk");
- if (r)
- goto err;
- }
-
- if (pdata->opt_clock_available("video_clk")) {
- r = dss_get_clock(&dss.dss_video_fck, "video_clk");
- if (r)
- goto err;
- }
-
- return 0;
-
-err:
- if (dss.dss_ick)
- clk_put(dss.dss_ick);
- if (dss.dss_fck)
- clk_put(dss.dss_fck);
- if (dss.dss_sys_clk)
- clk_put(dss.dss_sys_clk);
- if (dss.dss_tv_fck)
- clk_put(dss.dss_tv_fck);
- if (dss.dss_video_fck)
- clk_put(dss.dss_video_fck);
-
- return r;
-}
-
-static void dss_put_clocks(void)
-{
- if (dss.dss_video_fck)
- clk_put(dss.dss_video_fck);
- if (dss.dss_tv_fck)
- clk_put(dss.dss_tv_fck);
- if (dss.dss_sys_clk)
- clk_put(dss.dss_sys_clk);
- clk_put(dss.dss_fck);
- clk_put(dss.dss_ick);
-}
-
-unsigned long dss_clk_get_rate(enum dss_clock clk)
-{
- switch (clk) {
- case DSS_CLK_ICK:
- return clk_get_rate(dss.dss_ick);
- case DSS_CLK_FCK:
- return clk_get_rate(dss.dss_fck);
- case DSS_CLK_SYSCK:
- return clk_get_rate(dss.dss_sys_clk);
- case DSS_CLK_TVFCK:
- return clk_get_rate(dss.dss_tv_fck);
- case DSS_CLK_VIDFCK:
- return clk_get_rate(dss.dss_video_fck);
- }
-
- BUG();
- return 0;
-}
-
-static unsigned count_clk_bits(enum dss_clock clks)
-{
- unsigned num_clks = 0;
-
- if (clks & DSS_CLK_ICK)
- ++num_clks;
- if (clks & DSS_CLK_FCK)
- ++num_clks;
- if (clks & DSS_CLK_SYSCK)
- ++num_clks;
- if (clks & DSS_CLK_TVFCK)
- ++num_clks;
- if (clks & DSS_CLK_VIDFCK)
- ++num_clks;
-
- return num_clks;
-}
-
-static void dss_clk_enable_no_ctx(enum dss_clock clks)
-{
- unsigned num_clks = count_clk_bits(clks);
-
- if (clks & DSS_CLK_ICK)
- clk_enable(dss.dss_ick);
- if (clks & DSS_CLK_FCK)
- clk_enable(dss.dss_fck);
- if ((clks & DSS_CLK_SYSCK) && dss.dss_sys_clk)
- clk_enable(dss.dss_sys_clk);
- if ((clks & DSS_CLK_TVFCK) && dss.dss_tv_fck)
- clk_enable(dss.dss_tv_fck);
- if ((clks & DSS_CLK_VIDFCK) && dss.dss_video_fck)
- clk_enable(dss.dss_video_fck);
-
- dss.num_clks_enabled += num_clks;
-}
-
-void dss_clk_enable(enum dss_clock clks)
-{
- bool check_ctx = dss.num_clks_enabled == 0;
-
- dss_clk_enable_no_ctx(clks);
-
- /*
- * HACK: On omap4 the registers may not be accessible right after
- * enabling the clocks. At some point this will be handled by
- * pm_runtime, but for the time begin this should make things work.
- */
- if (cpu_is_omap44xx() && check_ctx)
- udelay(10);
-
- if (check_ctx && cpu_is_omap34xx() && dss_need_ctx_restore())
- restore_all_ctx();
-}
-
-static void dss_clk_disable_no_ctx(enum dss_clock clks)
-{
- unsigned num_clks = count_clk_bits(clks);
-
- if (clks & DSS_CLK_ICK)
- clk_disable(dss.dss_ick);
- if (clks & DSS_CLK_FCK)
- clk_disable(dss.dss_fck);
- if ((clks & DSS_CLK_SYSCK) && dss.dss_sys_clk)
- clk_disable(dss.dss_sys_clk);
- if ((clks & DSS_CLK_TVFCK) && dss.dss_tv_fck)
- clk_disable(dss.dss_tv_fck);
- if ((clks & DSS_CLK_VIDFCK) && dss.dss_video_fck)
- clk_disable(dss.dss_video_fck);
-
- dss.num_clks_enabled -= num_clks;
-}
-
-void dss_clk_disable(enum dss_clock clks)
-{
- if (cpu_is_omap34xx()) {
- unsigned num_clks = count_clk_bits(clks);
-
- BUG_ON(dss.num_clks_enabled < num_clks);
-
- if (dss.num_clks_enabled == num_clks)
- save_all_ctx();
- }
-
- dss_clk_disable_no_ctx(clks);
-}
-
-static void dss_clk_enable_all_no_ctx(void)
-{
- enum dss_clock clks;
-
- clks = DSS_CLK_ICK | DSS_CLK_FCK | DSS_CLK_SYSCK | DSS_CLK_TVFCK;
- if (cpu_is_omap34xx())
- clks |= DSS_CLK_VIDFCK;
- dss_clk_enable_no_ctx(clks);
-}
-
-static void dss_clk_disable_all_no_ctx(void)
-{
- enum dss_clock clks;
-
- clks = DSS_CLK_ICK | DSS_CLK_FCK | DSS_CLK_SYSCK | DSS_CLK_TVFCK;
- if (cpu_is_omap34xx())
- clks |= DSS_CLK_VIDFCK;
- dss_clk_disable_no_ctx(clks);
-}
-
-#if defined(CONFIG_DEBUG_FS) && defined(CONFIG_OMAP2_DSS_DEBUG_SUPPORT)
-/* CLOCKS */
-static void core_dump_clocks(struct seq_file *s)
-{
- int i;
- struct clk *clocks[5] = {
- dss.dss_ick,
- dss.dss_fck,
- dss.dss_sys_clk,
- dss.dss_tv_fck,
- dss.dss_video_fck
- };
-
- const char *names[5] = {
- "ick",
- "fck",
- "sys_clk",
- "tv_fck",
- "video_fck"
- };
-
- seq_printf(s, "- CORE -\n");
-
- seq_printf(s, "internal clk count\t\t%u\n", dss.num_clks_enabled);
-
- for (i = 0; i < 5; i++) {
- if (!clocks[i])
- continue;
- seq_printf(s, "%s (%s)%*s\t%lu\t%d\n",
- names[i],
- clocks[i]->name,
- 24 - strlen(names[i]) - strlen(clocks[i]->name),
- "",
- clk_get_rate(clocks[i]),
- clocks[i]->usecount);
- }
-}
-#endif /* defined(CONFIG_DEBUG_FS) && defined(CONFIG_OMAP2_DSS_DEBUG_SUPPORT) */
-
-/* DEBUGFS */
-#if defined(CONFIG_DEBUG_FS) && defined(CONFIG_OMAP2_DSS_DEBUG_SUPPORT)
-void dss_debug_dump_clocks(struct seq_file *s)
-{
- core_dump_clocks(s);
- dss_dump_clocks(s);
- dispc_dump_clocks(s);
-#ifdef CONFIG_OMAP2_DSS_DSI
- dsi_dump_clocks(s);
-#endif
-}
-#endif
-
-
-/* DSS HW IP initialisation */
-static int omap_dsshw_probe(struct platform_device *pdev)
-{
- int r;
-
- dss.pdev = pdev;
-
- r = dss_get_clocks();
- if (r)
- goto err_clocks;
-
- dss_clk_enable_all_no_ctx();
-
- dss.ctx_id = dss_get_ctx_id();
- DSSDBG("initial ctx id %u\n", dss.ctx_id);
-
- r = dss_init();
- if (r) {
- DSSERR("Failed to initialize DSS\n");
- goto err_dss;
- }
-
r = dpi_init();
if (r) {
DSSERR("Failed to initialize DPI\n");
@@ -1124,42 +787,66 @@
goto err_sdi;
}
- dss_clk_disable_all_no_ctx();
+ rev = dss_read_reg(DSS_REVISION);
+ printk(KERN_INFO "OMAP DSS rev %d.%d\n",
+ FLD_GET(rev, 7, 4), FLD_GET(rev, 3, 0));
+
+ dss_runtime_put();
+
return 0;
err_sdi:
dpi_exit();
err_dpi:
- dss_exit();
-err_dss:
- dss_clk_disable_all_no_ctx();
+ dss_runtime_put();
+err_runtime_get:
+ pm_runtime_disable(&pdev->dev);
dss_put_clocks();
err_clocks:
+ iounmap(dss.base);
+err_ioremap:
return r;
}
static int omap_dsshw_remove(struct platform_device *pdev)
{
+ dpi_exit();
+ sdi_exit();
- dss_exit();
+ iounmap(dss.base);
- /*
- * As part of hwmod changes, DSS is not the only controller of dss
- * clocks; hwmod framework itself will also enable clocks during hwmod
- * init for dss, and autoidle is set in h/w for DSS. Hence, there's no
- * need to disable clocks if their usecounts > 1.
- */
- WARN_ON(dss.num_clks_enabled > 0);
+ pm_runtime_disable(&pdev->dev);
dss_put_clocks();
+
return 0;
}
+static int dss_runtime_suspend(struct device *dev)
+{
+ dss_save_context();
+ clk_disable(dss.dss_clk);
+ return 0;
+}
+
+static int dss_runtime_resume(struct device *dev)
+{
+ clk_enable(dss.dss_clk);
+ dss_restore_context();
+ return 0;
+}
+
+static const struct dev_pm_ops dss_pm_ops = {
+ .runtime_suspend = dss_runtime_suspend,
+ .runtime_resume = dss_runtime_resume,
+};
+
static struct platform_driver omap_dsshw_driver = {
.probe = omap_dsshw_probe,
.remove = omap_dsshw_remove,
.driver = {
.name = "omapdss_dss",
.owner = THIS_MODULE,
+ .pm = &dss_pm_ops,
},
};
diff --git a/drivers/video/omap2/dss/dss.h b/drivers/video/omap2/dss/dss.h
index 8ab6d43..9c94b11 100644
--- a/drivers/video/omap2/dss/dss.h
+++ b/drivers/video/omap2/dss/dss.h
@@ -97,26 +97,12 @@
#define FLD_MOD(orig, val, start, end) \
(((orig) & ~FLD_MASK(start, end)) | FLD_VAL(val, start, end))
-enum omap_burst_size {
- OMAP_DSS_BURST_4x32 = 0,
- OMAP_DSS_BURST_8x32 = 1,
- OMAP_DSS_BURST_16x32 = 2,
-};
-
enum omap_parallel_interface_mode {
OMAP_DSS_PARALLELMODE_BYPASS, /* MIPI DPI */
OMAP_DSS_PARALLELMODE_RFBI, /* MIPI DBI */
OMAP_DSS_PARALLELMODE_DSI,
};
-enum dss_clock {
- DSS_CLK_ICK = 1 << 0, /* DSS_L3_ICLK and DSS_L4_ICLK */
- DSS_CLK_FCK = 1 << 1, /* DSS1_ALWON_FCLK */
- DSS_CLK_SYSCK = 1 << 2, /* DSS2_ALWON_FCLK */
- DSS_CLK_TVFCK = 1 << 3, /* DSS_TV_FCLK */
- DSS_CLK_VIDFCK = 1 << 4, /* DSS_96M_FCLK*/
-};
-
enum dss_hdmi_venc_clk_source_select {
DSS_VENC_TV_CLK = 0,
DSS_HDMI_M_PCLK = 1,
@@ -194,7 +180,7 @@
bool dss_use_replication(struct omap_dss_device *dssdev,
enum omap_color_mode mode);
void default_get_overlay_fifo_thresholds(enum omap_plane plane,
- u32 fifo_size, enum omap_burst_size *burst_size,
+ u32 fifo_size, u32 burst_size,
u32 *fifo_low, u32 *fifo_high);
/* manager */
@@ -220,13 +206,12 @@
int dss_init_platform_driver(void);
void dss_uninit_platform_driver(void);
+int dss_runtime_get(void);
+void dss_runtime_put(void);
+
+struct clk *dss_get_ick(void);
+
void dss_select_hdmi_venc_clk_source(enum dss_hdmi_venc_clk_source_select);
-void dss_save_context(void);
-void dss_restore_context(void);
-void dss_clk_enable(enum dss_clock clks);
-void dss_clk_disable(enum dss_clock clks);
-unsigned long dss_clk_get_rate(enum dss_clock clk);
-int dss_need_ctx_restore(void);
const char *dss_get_generic_clk_source_name(enum omap_dss_clk_source clk_src);
void dss_dump_clocks(struct seq_file *s);
@@ -283,15 +268,15 @@
int dsi_init_platform_driver(void);
void dsi_uninit_platform_driver(void);
+int dsi_runtime_get(struct platform_device *dsidev);
+void dsi_runtime_put(struct platform_device *dsidev);
+
void dsi_dump_clocks(struct seq_file *s);
void dsi_create_debugfs_files_irq(struct dentry *debugfs_dir,
const struct file_operations *debug_fops);
void dsi_create_debugfs_files_reg(struct dentry *debugfs_dir,
const struct file_operations *debug_fops);
-void dsi_save_context(void);
-void dsi_restore_context(void);
-
int dsi_init_display(struct omap_dss_device *display);
void dsi_irq_handler(void);
unsigned long dsi_get_pll_hsdiv_dispc_rate(struct platform_device *dsidev);
@@ -304,7 +289,7 @@
bool enable_hsdiv);
void dsi_pll_uninit(struct platform_device *dsidev, bool disconnect_lanes);
void dsi_get_overlay_fifo_thresholds(enum omap_plane plane,
- u32 fifo_size, enum omap_burst_size *burst_size,
+ u32 fifo_size, u32 burst_size,
u32 *fifo_low, u32 *fifo_high);
void dsi_wait_pll_hsdiv_dispc_active(struct platform_device *dsidev);
void dsi_wait_pll_hsdiv_dsi_active(struct platform_device *dsidev);
@@ -317,6 +302,13 @@
static inline void dsi_uninit_platform_driver(void)
{
}
+static inline int dsi_runtime_get(struct platform_device *dsidev)
+{
+ return 0;
+}
+static inline void dsi_runtime_put(struct platform_device *dsidev)
+{
+}
static inline unsigned long dsi_get_pll_hsdiv_dispc_rate(struct platform_device *dsidev)
{
WARN("%s: DSI not compiled in, returning rate as 0\n", __func__);
@@ -384,8 +376,8 @@
void dispc_irq_handler(void);
void dispc_fake_vsync_irq(void);
-void dispc_save_context(void);
-void dispc_restore_context(void);
+int dispc_runtime_get(void);
+void dispc_runtime_put(void);
void dispc_enable_sidle(void);
void dispc_disable_sidle(void);
@@ -398,10 +390,12 @@
void dispc_set_lcd_size(enum omap_channel channel, u16 width, u16 height);
void dispc_set_digit_size(u16 width, u16 height);
u32 dispc_get_plane_fifo_size(enum omap_plane plane);
-void dispc_setup_plane_fifo(enum omap_plane plane, u32 low, u32 high);
+void dispc_set_fifo_threshold(enum omap_plane plane, u32 low, u32 high);
void dispc_enable_fifomerge(bool enable);
-void dispc_set_burst_size(enum omap_plane plane,
- enum omap_burst_size burst_size);
+u32 dispc_get_burst_size(enum omap_plane plane);
+void dispc_enable_cpr(enum omap_channel channel, bool enable);
+void dispc_set_cpr_coef(enum omap_channel channel,
+ struct omap_dss_cpr_coefs *coefs);
void dispc_set_plane_ba0(enum omap_plane plane, u32 paddr);
void dispc_set_plane_ba1(enum omap_plane plane, u32 paddr);
diff --git a/drivers/video/omap2/dss/dss_features.c b/drivers/video/omap2/dss/dss_features.c
index 1c18888..b415c4e 100644
--- a/drivers/video/omap2/dss/dss_features.c
+++ b/drivers/video/omap2/dss/dss_features.c
@@ -49,6 +49,9 @@
const enum omap_color_mode *supported_color_modes;
const char * const *clksrc_names;
const struct dss_param_range *dss_params;
+
+ const u32 buffer_size_unit;
+ const u32 burst_size_unit;
};
/* This struct is assigned to one of the below during initialization */
@@ -274,6 +277,8 @@
.supported_color_modes = omap2_dss_supported_color_modes,
.clksrc_names = omap2_dss_clk_source_names,
.dss_params = omap2_dss_param_range,
+ .buffer_size_unit = 1,
+ .burst_size_unit = 8,
};
/* OMAP3 DSS Features */
@@ -286,7 +291,9 @@
FEAT_LCDENABLESIGNAL | FEAT_PCKFREEENABLE |
FEAT_FUNCGATED | FEAT_ROWREPEATENABLE |
FEAT_LINEBUFFERSPLIT | FEAT_RESIZECONF |
- FEAT_DSI_PLL_FREQSEL | FEAT_DSI_REVERSE_TXCLKESC,
+ FEAT_DSI_PLL_FREQSEL | FEAT_DSI_REVERSE_TXCLKESC |
+ FEAT_VENC_REQUIRES_TV_DAC_CLK | FEAT_CPR | FEAT_PRELOAD |
+ FEAT_FIR_COEF_V,
.num_mgrs = 2,
.num_ovls = 3,
@@ -294,6 +301,8 @@
.supported_color_modes = omap3_dss_supported_color_modes,
.clksrc_names = omap3_dss_clk_source_names,
.dss_params = omap3_dss_param_range,
+ .buffer_size_unit = 1,
+ .burst_size_unit = 8,
};
static const struct omap_dss_features omap3630_dss_features = {
@@ -306,7 +315,8 @@
FEAT_PRE_MULT_ALPHA | FEAT_FUNCGATED |
FEAT_ROWREPEATENABLE | FEAT_LINEBUFFERSPLIT |
FEAT_RESIZECONF | FEAT_DSI_PLL_PWR_BUG |
- FEAT_DSI_PLL_FREQSEL,
+ FEAT_DSI_PLL_FREQSEL | FEAT_CPR | FEAT_PRELOAD |
+ FEAT_FIR_COEF_V,
.num_mgrs = 2,
.num_ovls = 3,
@@ -314,6 +324,8 @@
.supported_color_modes = omap3_dss_supported_color_modes,
.clksrc_names = omap3_dss_clk_source_names,
.dss_params = omap3_dss_param_range,
+ .buffer_size_unit = 1,
+ .burst_size_unit = 8,
};
/* OMAP4 DSS Features */
@@ -327,7 +339,8 @@
FEAT_MGR_LCD2 | FEAT_GLOBAL_ALPHA_VID1 |
FEAT_CORE_CLK_DIV | FEAT_LCD_CLK_SRC |
FEAT_DSI_DCS_CMD_CONFIG_VC | FEAT_DSI_VC_OCP_WIDTH |
- FEAT_DSI_GNQ | FEAT_HANDLE_UV_SEPARATE | FEAT_ATTR2,
+ FEAT_DSI_GNQ | FEAT_HANDLE_UV_SEPARATE | FEAT_ATTR2 |
+ FEAT_CPR | FEAT_PRELOAD | FEAT_FIR_COEF_V,
.num_mgrs = 3,
.num_ovls = 3,
@@ -335,6 +348,8 @@
.supported_color_modes = omap4_dss_supported_color_modes,
.clksrc_names = omap4_dss_clk_source_names,
.dss_params = omap4_dss_param_range,
+ .buffer_size_unit = 16,
+ .burst_size_unit = 16,
};
/* For all the other OMAP4 versions */
@@ -348,7 +363,8 @@
FEAT_CORE_CLK_DIV | FEAT_LCD_CLK_SRC |
FEAT_DSI_DCS_CMD_CONFIG_VC | FEAT_DSI_VC_OCP_WIDTH |
FEAT_DSI_GNQ | FEAT_HDMI_CTS_SWMODE |
- FEAT_HANDLE_UV_SEPARATE | FEAT_ATTR2,
+ FEAT_HANDLE_UV_SEPARATE | FEAT_ATTR2 | FEAT_CPR |
+ FEAT_PRELOAD | FEAT_FIR_COEF_V,
.num_mgrs = 3,
.num_ovls = 3,
@@ -356,6 +372,8 @@
.supported_color_modes = omap4_dss_supported_color_modes,
.clksrc_names = omap4_dss_clk_source_names,
.dss_params = omap4_dss_param_range,
+ .buffer_size_unit = 16,
+ .burst_size_unit = 16,
};
/* Functions returning values related to a DSS feature */
@@ -401,6 +419,16 @@
return omap_current_dss_features->clksrc_names[id];
}
+u32 dss_feat_get_buffer_size_unit(void)
+{
+ return omap_current_dss_features->buffer_size_unit;
+}
+
+u32 dss_feat_get_burst_size_unit(void)
+{
+ return omap_current_dss_features->burst_size_unit;
+}
+
/* DSS has_feature check */
bool dss_has_feature(enum dss_feat_id id)
{
diff --git a/drivers/video/omap2/dss/dss_features.h b/drivers/video/omap2/dss/dss_features.h
index 07b346f..b7398cb 100644
--- a/drivers/video/omap2/dss/dss_features.h
+++ b/drivers/video/omap2/dss/dss_features.h
@@ -51,6 +51,10 @@
FEAT_HDMI_CTS_SWMODE = 1 << 19,
FEAT_HANDLE_UV_SEPARATE = 1 << 20,
FEAT_ATTR2 = 1 << 21,
+ FEAT_VENC_REQUIRES_TV_DAC_CLK = 1 << 22,
+ FEAT_CPR = 1 << 23,
+ FEAT_PRELOAD = 1 << 24,
+ FEAT_FIR_COEF_V = 1 << 25,
};
/* DSS register field id */
@@ -90,6 +94,9 @@
enum omap_color_mode color_mode);
const char *dss_feat_get_clk_source_name(enum omap_dss_clk_source id);
+u32 dss_feat_get_buffer_size_unit(void); /* in bytes */
+u32 dss_feat_get_burst_size_unit(void); /* in bytes */
+
bool dss_has_feature(enum dss_feat_id id);
void dss_feat_get_reg_field(enum dss_feat_reg_field id, u8 *start, u8 *end);
void dss_features_init(void);
diff --git a/drivers/video/omap2/dss/hdmi.c b/drivers/video/omap2/dss/hdmi.c
index b0555f4..256f27a 100644
--- a/drivers/video/omap2/dss/hdmi.c
+++ b/drivers/video/omap2/dss/hdmi.c
@@ -29,6 +29,9 @@
#include <linux/mutex.h>
#include <linux/delay.h>
#include <linux/string.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/clk.h>
#include <video/omapdss.h>
#if defined(CONFIG_SND_OMAP_SOC_OMAP4_HDMI) || \
defined(CONFIG_SND_OMAP_SOC_OMAP4_HDMI_MODULE)
@@ -51,6 +54,9 @@
u8 edid_set;
bool custom_set;
struct hdmi_config cfg;
+
+ struct clk *sys_clk;
+ struct clk *hdmi_clk;
} hdmi;
/*
@@ -162,6 +168,27 @@
return val;
}
+static int hdmi_runtime_get(void)
+{
+ int r;
+
+ DSSDBG("hdmi_runtime_get\n");
+
+ r = pm_runtime_get_sync(&hdmi.pdev->dev);
+ WARN_ON(r < 0);
+ return r < 0 ? r : 0;
+}
+
+static void hdmi_runtime_put(void)
+{
+ int r;
+
+ DSSDBG("hdmi_runtime_put\n");
+
+ r = pm_runtime_put(&hdmi.pdev->dev);
+ WARN_ON(r < 0);
+}
+
int hdmi_init_display(struct omap_dss_device *dssdev)
{
DSSDBG("init_display\n");
@@ -311,30 +338,11 @@
return 0;
}
-static int hdmi_wait_softreset(void)
-{
- /* reset W1 */
- REG_FLD_MOD(HDMI_WP_SYSCONFIG, 0x1, 0, 0);
-
- /* wait till SOFTRESET == 0 */
- if (hdmi_wait_for_bit_change(HDMI_WP_SYSCONFIG, 0, 0, 0) != 0) {
- DSSERR("sysconfig reset failed\n");
- return -ETIMEDOUT;
- }
-
- return 0;
-}
-
static int hdmi_pll_program(struct hdmi_pll_info *fmt)
{
u16 r = 0;
enum hdmi_clk_refsel refsel;
- /* wait for wrapper reset */
- r = hdmi_wait_softreset();
- if (r)
- return r;
-
r = hdmi_set_pll_pwr(HDMI_PLLPWRCMD_ALLOFF);
if (r)
return r;
@@ -1064,7 +1072,7 @@
unsigned long clkin, refclk;
u32 mf;
- clkin = dss_clk_get_rate(DSS_CLK_SYSCK) / 10000;
+ clkin = clk_get_rate(hdmi.sys_clk) / 10000;
/*
* Input clock is predivided by N + 1
* out put of which is reference clk
@@ -1098,16 +1106,6 @@
DSSDBG("range = %d sd = %d\n", pi->dcofreq, pi->regsd);
}
-static void hdmi_enable_clocks(int enable)
-{
- if (enable)
- dss_clk_enable(DSS_CLK_ICK | DSS_CLK_FCK |
- DSS_CLK_SYSCK | DSS_CLK_VIDFCK);
- else
- dss_clk_disable(DSS_CLK_ICK | DSS_CLK_FCK |
- DSS_CLK_SYSCK | DSS_CLK_VIDFCK);
-}
-
static int hdmi_power_on(struct omap_dss_device *dssdev)
{
int r, code = 0;
@@ -1115,7 +1113,9 @@
struct omap_video_timings *p;
unsigned long phy;
- hdmi_enable_clocks(1);
+ r = hdmi_runtime_get();
+ if (r)
+ return r;
dispc_enable_channel(OMAP_DSS_CHANNEL_DIGIT, 0);
@@ -1180,7 +1180,7 @@
return 0;
err:
- hdmi_enable_clocks(0);
+ hdmi_runtime_put();
return -EIO;
}
@@ -1191,7 +1191,7 @@
hdmi_wp_video_start(0);
hdmi_phy_off();
hdmi_set_pll_pwr(HDMI_PLLPWRCMD_ALLOFF);
- hdmi_enable_clocks(0);
+ hdmi_runtime_put();
hdmi.edid_set = 0;
}
@@ -1686,14 +1686,43 @@
};
#endif
+static int hdmi_get_clocks(struct platform_device *pdev)
+{
+ struct clk *clk;
+
+ clk = clk_get(&pdev->dev, "sys_clk");
+ if (IS_ERR(clk)) {
+ DSSERR("can't get sys_clk\n");
+ return PTR_ERR(clk);
+ }
+
+ hdmi.sys_clk = clk;
+
+ clk = clk_get(&pdev->dev, "dss_48mhz_clk");
+ if (IS_ERR(clk)) {
+ DSSERR("can't get hdmi_clk\n");
+ clk_put(hdmi.sys_clk);
+ return PTR_ERR(clk);
+ }
+
+ hdmi.hdmi_clk = clk;
+
+ return 0;
+}
+
+static void hdmi_put_clocks(void)
+{
+ if (hdmi.sys_clk)
+ clk_put(hdmi.sys_clk);
+ if (hdmi.hdmi_clk)
+ clk_put(hdmi.hdmi_clk);
+}
+
/* HDMI HW IP initialisation */
static int omapdss_hdmihw_probe(struct platform_device *pdev)
{
struct resource *hdmi_mem;
-#if defined(CONFIG_SND_OMAP_SOC_OMAP4_HDMI) || \
- defined(CONFIG_SND_OMAP_SOC_OMAP4_HDMI_MODULE)
- int ret;
-#endif
+ int r;
hdmi.pdata = pdev->dev.platform_data;
hdmi.pdev = pdev;
@@ -1713,17 +1742,25 @@
return -ENOMEM;
}
+ r = hdmi_get_clocks(pdev);
+ if (r) {
+ iounmap(hdmi.base_wp);
+ return r;
+ }
+
+ pm_runtime_enable(&pdev->dev);
+
hdmi_panel_init();
#if defined(CONFIG_SND_OMAP_SOC_OMAP4_HDMI) || \
defined(CONFIG_SND_OMAP_SOC_OMAP4_HDMI_MODULE)
/* Register ASoC codec DAI */
- ret = snd_soc_register_codec(&pdev->dev, &hdmi_audio_codec_drv,
+ r = snd_soc_register_codec(&pdev->dev, &hdmi_audio_codec_drv,
&hdmi_codec_dai_drv, 1);
- if (ret) {
+ if (r) {
DSSERR("can't register ASoC HDMI audio codec\n");
- return ret;
+ return r;
}
#endif
return 0;
@@ -1738,17 +1775,62 @@
snd_soc_unregister_codec(&pdev->dev);
#endif
+ pm_runtime_disable(&pdev->dev);
+
+ hdmi_put_clocks();
+
iounmap(hdmi.base_wp);
return 0;
}
+static int hdmi_runtime_suspend(struct device *dev)
+{
+ clk_disable(hdmi.hdmi_clk);
+ clk_disable(hdmi.sys_clk);
+
+ dispc_runtime_put();
+ dss_runtime_put();
+
+ return 0;
+}
+
+static int hdmi_runtime_resume(struct device *dev)
+{
+ int r;
+
+ r = dss_runtime_get();
+ if (r < 0)
+ goto err_get_dss;
+
+ r = dispc_runtime_get();
+ if (r < 0)
+ goto err_get_dispc;
+
+
+ clk_enable(hdmi.sys_clk);
+ clk_enable(hdmi.hdmi_clk);
+
+ return 0;
+
+err_get_dispc:
+ dss_runtime_put();
+err_get_dss:
+ return r;
+}
+
+static const struct dev_pm_ops hdmi_pm_ops = {
+ .runtime_suspend = hdmi_runtime_suspend,
+ .runtime_resume = hdmi_runtime_resume,
+};
+
static struct platform_driver omapdss_hdmihw_driver = {
.probe = omapdss_hdmihw_probe,
.remove = omapdss_hdmihw_remove,
.driver = {
.name = "omapdss_hdmi",
.owner = THIS_MODULE,
+ .pm = &hdmi_pm_ops,
},
};
diff --git a/drivers/video/omap2/dss/manager.c b/drivers/video/omap2/dss/manager.c
index 9aeea50..13d72d5 100644
--- a/drivers/video/omap2/dss/manager.c
+++ b/drivers/video/omap2/dss/manager.c
@@ -275,6 +275,108 @@
return size;
}
+static ssize_t manager_cpr_enable_show(struct omap_overlay_manager *mgr,
+ char *buf)
+{
+ return snprintf(buf, PAGE_SIZE, "%d\n", mgr->info.cpr_enable);
+}
+
+static ssize_t manager_cpr_enable_store(struct omap_overlay_manager *mgr,
+ const char *buf, size_t size)
+{
+ struct omap_overlay_manager_info info;
+ int v;
+ int r;
+ bool enable;
+
+ if (!dss_has_feature(FEAT_CPR))
+ return -ENODEV;
+
+ r = kstrtoint(buf, 0, &v);
+ if (r)
+ return r;
+
+ enable = !!v;
+
+ mgr->get_manager_info(mgr, &info);
+
+ if (info.cpr_enable == enable)
+ return size;
+
+ info.cpr_enable = enable;
+
+ r = mgr->set_manager_info(mgr, &info);
+ if (r)
+ return r;
+
+ r = mgr->apply(mgr);
+ if (r)
+ return r;
+
+ return size;
+}
+
+static ssize_t manager_cpr_coef_show(struct omap_overlay_manager *mgr,
+ char *buf)
+{
+ struct omap_overlay_manager_info info;
+
+ mgr->get_manager_info(mgr, &info);
+
+ return snprintf(buf, PAGE_SIZE,
+ "%d %d %d %d %d %d %d %d %d\n",
+ info.cpr_coefs.rr,
+ info.cpr_coefs.rg,
+ info.cpr_coefs.rb,
+ info.cpr_coefs.gr,
+ info.cpr_coefs.gg,
+ info.cpr_coefs.gb,
+ info.cpr_coefs.br,
+ info.cpr_coefs.bg,
+ info.cpr_coefs.bb);
+}
+
+static ssize_t manager_cpr_coef_store(struct omap_overlay_manager *mgr,
+ const char *buf, size_t size)
+{
+ struct omap_overlay_manager_info info;
+ struct omap_dss_cpr_coefs coefs;
+ int r, i;
+ s16 *arr;
+
+ if (!dss_has_feature(FEAT_CPR))
+ return -ENODEV;
+
+ if (sscanf(buf, "%hd %hd %hd %hd %hd %hd %hd %hd %hd",
+ &coefs.rr, &coefs.rg, &coefs.rb,
+ &coefs.gr, &coefs.gg, &coefs.gb,
+ &coefs.br, &coefs.bg, &coefs.bb) != 9)
+ return -EINVAL;
+
+ arr = (s16[]){ coefs.rr, coefs.rg, coefs.rb,
+ coefs.gr, coefs.gg, coefs.gb,
+ coefs.br, coefs.bg, coefs.bb };
+
+ for (i = 0; i < 9; ++i) {
+ if (arr[i] < -512 || arr[i] > 511)
+ return -EINVAL;
+ }
+
+ mgr->get_manager_info(mgr, &info);
+
+ info.cpr_coefs = coefs;
+
+ r = mgr->set_manager_info(mgr, &info);
+ if (r)
+ return r;
+
+ r = mgr->apply(mgr);
+ if (r)
+ return r;
+
+ return size;
+}
+
struct manager_attribute {
struct attribute attr;
ssize_t (*show)(struct omap_overlay_manager *, char *);
@@ -300,6 +402,12 @@
static MANAGER_ATTR(alpha_blending_enabled, S_IRUGO|S_IWUSR,
manager_alpha_blending_enabled_show,
manager_alpha_blending_enabled_store);
+static MANAGER_ATTR(cpr_enable, S_IRUGO|S_IWUSR,
+ manager_cpr_enable_show,
+ manager_cpr_enable_store);
+static MANAGER_ATTR(cpr_coef, S_IRUGO|S_IWUSR,
+ manager_cpr_coef_show,
+ manager_cpr_coef_store);
static struct attribute *manager_sysfs_attrs[] = {
@@ -310,6 +418,8 @@
&manager_attr_trans_key_value.attr,
&manager_attr_trans_key_enabled.attr,
&manager_attr_alpha_blending_enabled.attr,
+ &manager_attr_cpr_enable.attr,
+ &manager_attr_cpr_coef.attr,
NULL
};
@@ -391,33 +501,14 @@
bool enabled;
- u32 paddr;
- void __iomem *vaddr;
- u32 p_uv_addr; /* relevant for NV12 format only */
- u16 screen_width;
- u16 width;
- u16 height;
- enum omap_color_mode color_mode;
- u8 rotation;
- enum omap_dss_rotation_type rotation_type;
- bool mirror;
-
- u16 pos_x;
- u16 pos_y;
- u16 out_width; /* if 0, out_width == width */
- u16 out_height; /* if 0, out_height == height */
- u8 global_alpha;
- u8 pre_mult_alpha;
+ struct omap_overlay_info info;
enum omap_channel channel;
bool replication;
bool ilace;
- enum omap_burst_size burst_size;
u32 fifo_low;
u32 fifo_high;
-
- bool manual_update;
};
struct manager_cache_data {
@@ -429,15 +520,8 @@
* VSYNC/EVSYNC */
bool shadow_dirty;
- u32 default_color;
+ struct omap_overlay_manager_info info;
- enum omap_dss_trans_key_type trans_key_type;
- u32 trans_key;
- bool trans_enabled;
-
- bool alpha_enabled;
-
- bool manual_upd_display;
bool manual_update;
bool do_manual_update;
@@ -539,24 +623,15 @@
if (!dssdev || dssdev->state != OMAP_DSS_DISPLAY_ACTIVE)
return 0;
+ if (dssdev->caps & OMAP_DSS_DISPLAY_CAP_MANUAL_UPDATE)
+ return 0;
+
if (dssdev->type == OMAP_DISPLAY_TYPE_VENC
|| dssdev->type == OMAP_DISPLAY_TYPE_HDMI) {
irq = DISPC_IRQ_EVSYNC_ODD | DISPC_IRQ_EVSYNC_EVEN;
} else {
- if (dssdev->caps & OMAP_DSS_DISPLAY_CAP_MANUAL_UPDATE) {
- enum omap_dss_update_mode mode;
- mode = dssdev->driver->get_update_mode(dssdev);
- if (mode != OMAP_DSS_UPDATE_AUTO)
- return 0;
-
- irq = (dssdev->manager->id == OMAP_DSS_CHANNEL_LCD) ?
- DISPC_IRQ_FRAMEDONE
- : DISPC_IRQ_FRAMEDONE2;
- } else {
- irq = (dssdev->manager->id == OMAP_DSS_CHANNEL_LCD) ?
- DISPC_IRQ_VSYNC
- : DISPC_IRQ_VSYNC2;
- }
+ irq = (dssdev->manager->id == OMAP_DSS_CHANNEL_LCD) ?
+ DISPC_IRQ_VSYNC : DISPC_IRQ_VSYNC2;
}
mc = &dss_cache.manager_cache[mgr->id];
@@ -617,24 +692,15 @@
if (!dssdev || dssdev->state != OMAP_DSS_DISPLAY_ACTIVE)
return 0;
+ if (dssdev->caps & OMAP_DSS_DISPLAY_CAP_MANUAL_UPDATE)
+ return 0;
+
if (dssdev->type == OMAP_DISPLAY_TYPE_VENC
|| dssdev->type == OMAP_DISPLAY_TYPE_HDMI) {
irq = DISPC_IRQ_EVSYNC_ODD | DISPC_IRQ_EVSYNC_EVEN;
} else {
- if (dssdev->caps & OMAP_DSS_DISPLAY_CAP_MANUAL_UPDATE) {
- enum omap_dss_update_mode mode;
- mode = dssdev->driver->get_update_mode(dssdev);
- if (mode != OMAP_DSS_UPDATE_AUTO)
- return 0;
-
- irq = (dssdev->manager->id == OMAP_DSS_CHANNEL_LCD) ?
- DISPC_IRQ_FRAMEDONE
- : DISPC_IRQ_FRAMEDONE2;
- } else {
- irq = (dssdev->manager->id == OMAP_DSS_CHANNEL_LCD) ?
- DISPC_IRQ_VSYNC
- : DISPC_IRQ_VSYNC2;
- }
+ irq = (dssdev->manager->id == OMAP_DSS_CHANNEL_LCD) ?
+ DISPC_IRQ_VSYNC : DISPC_IRQ_VSYNC2;
}
oc = &dss_cache.overlay_cache[ovl->id];
@@ -720,10 +786,12 @@
static bool dispc_is_overlay_scaled(struct overlay_cache_data *oc)
{
- if (oc->out_width != 0 && oc->width != oc->out_width)
+ struct omap_overlay_info *oi = &oc->info;
+
+ if (oi->out_width != 0 && oi->width != oi->out_width)
return true;
- if (oc->out_height != 0 && oc->height != oc->out_height)
+ if (oi->out_height != 0 && oi->height != oi->out_height)
return true;
return false;
@@ -733,6 +801,8 @@
{
struct overlay_cache_data *c;
struct manager_cache_data *mc;
+ struct omap_overlay_info *oi;
+ struct omap_overlay_manager_info *mi;
u16 outw, outh;
u16 x, y, w, h;
u32 paddr;
@@ -742,6 +812,7 @@
DSSDBGF("%d", plane);
c = &dss_cache.overlay_cache[plane];
+ oi = &c->info;
if (!c->enabled) {
dispc_enable_plane(plane, 0);
@@ -749,21 +820,22 @@
}
mc = &dss_cache.manager_cache[c->channel];
+ mi = &mc->info;
- x = c->pos_x;
- y = c->pos_y;
- w = c->width;
- h = c->height;
- outw = c->out_width == 0 ? c->width : c->out_width;
- outh = c->out_height == 0 ? c->height : c->out_height;
- paddr = c->paddr;
+ x = oi->pos_x;
+ y = oi->pos_y;
+ w = oi->width;
+ h = oi->height;
+ outw = oi->out_width == 0 ? oi->width : oi->out_width;
+ outh = oi->out_height == 0 ? oi->height : oi->out_height;
+ paddr = oi->paddr;
orig_w = w;
orig_h = h;
orig_outw = outw;
orig_outh = outh;
- if (c->manual_update && mc->do_manual_update) {
+ if (mc->manual_update && mc->do_manual_update) {
unsigned bpp;
unsigned scale_x_m = w, scale_x_d = outw;
unsigned scale_y_m = h, scale_y_d = outh;
@@ -775,7 +847,7 @@
return 0;
}
- switch (c->color_mode) {
+ switch (oi->color_mode) {
case OMAP_DSS_COLOR_NV12:
bpp = 8;
break;
@@ -805,23 +877,23 @@
BUG();
}
- if (mc->x > c->pos_x) {
+ if (mc->x > oi->pos_x) {
x = 0;
- outw -= (mc->x - c->pos_x);
- paddr += (mc->x - c->pos_x) *
+ outw -= (mc->x - oi->pos_x);
+ paddr += (mc->x - oi->pos_x) *
scale_x_m / scale_x_d * bpp / 8;
} else {
- x = c->pos_x - mc->x;
+ x = oi->pos_x - mc->x;
}
- if (mc->y > c->pos_y) {
+ if (mc->y > oi->pos_y) {
y = 0;
- outh -= (mc->y - c->pos_y);
- paddr += (mc->y - c->pos_y) *
+ outh -= (mc->y - oi->pos_y);
+ paddr += (mc->y - oi->pos_y) *
scale_y_m / scale_y_d *
- c->screen_width * bpp / 8;
+ oi->screen_width * bpp / 8;
} else {
- y = c->pos_y - mc->y;
+ y = oi->pos_y - mc->y;
}
if (mc->w < (x + outw))
@@ -840,8 +912,8 @@
* the width if the original width was bigger.
*/
if ((w & 1) &&
- (c->color_mode == OMAP_DSS_COLOR_YUV2 ||
- c->color_mode == OMAP_DSS_COLOR_UYVY)) {
+ (oi->color_mode == OMAP_DSS_COLOR_YUV2 ||
+ oi->color_mode == OMAP_DSS_COLOR_UYVY)) {
if (orig_w > w)
w += 1;
else
@@ -851,19 +923,19 @@
r = dispc_setup_plane(plane,
paddr,
- c->screen_width,
+ oi->screen_width,
x, y,
w, h,
outw, outh,
- c->color_mode,
+ oi->color_mode,
c->ilace,
- c->rotation_type,
- c->rotation,
- c->mirror,
- c->global_alpha,
- c->pre_mult_alpha,
+ oi->rotation_type,
+ oi->rotation,
+ oi->mirror,
+ oi->global_alpha,
+ oi->pre_mult_alpha,
c->channel,
- c->p_uv_addr);
+ oi->p_uv_addr);
if (r) {
/* this shouldn't happen */
@@ -874,8 +946,7 @@
dispc_enable_replication(plane, c->replication);
- dispc_set_burst_size(plane, c->burst_size);
- dispc_setup_plane_fifo(plane, c->fifo_low, c->fifo_high);
+ dispc_set_fifo_threshold(plane, c->fifo_low, c->fifo_high);
dispc_enable_plane(plane, 1);
@@ -884,16 +955,21 @@
static void configure_manager(enum omap_channel channel)
{
- struct manager_cache_data *c;
+ struct omap_overlay_manager_info *mi;
DSSDBGF("%d", channel);
- c = &dss_cache.manager_cache[channel];
+ /* picking info from the cache */
+ mi = &dss_cache.manager_cache[channel].info;
- dispc_set_default_color(channel, c->default_color);
- dispc_set_trans_key(channel, c->trans_key_type, c->trans_key);
- dispc_enable_trans_key(channel, c->trans_enabled);
- dispc_enable_alpha_blending(channel, c->alpha_enabled);
+ dispc_set_default_color(channel, mi->default_color);
+ dispc_set_trans_key(channel, mi->trans_key_type, mi->trans_key);
+ dispc_enable_trans_key(channel, mi->trans_enabled);
+ dispc_enable_alpha_blending(channel, mi->alpha_enabled);
+ if (dss_has_feature(FEAT_CPR)) {
+ dispc_enable_cpr(channel, mi->cpr_enable);
+ dispc_set_cpr_coef(channel, &mi->cpr_coefs);
+ }
}
/* configure_dispc() tries to write values from cache to shadow registers.
@@ -928,7 +1004,7 @@
if (!oc->dirty)
continue;
- if (oc->manual_update && !mc->do_manual_update)
+ if (mc->manual_update && !mc->do_manual_update)
continue;
if (mgr_busy[oc->channel]) {
@@ -976,7 +1052,7 @@
/* We don't need GO with manual update display. LCD iface will
* always be turned off after frame, and new settings will be
* taken in to use at next update */
- if (!mc->manual_upd_display)
+ if (!mc->manual_update)
dispc_go(i);
}
@@ -1011,6 +1087,7 @@
{
struct overlay_cache_data *oc;
struct manager_cache_data *mc;
+ struct omap_overlay_info *oi;
const int num_ovls = dss_feat_get_num_ovls();
struct omap_overlay_manager *mgr;
int i;
@@ -1053,6 +1130,7 @@
unsigned outw, outh;
oc = &dss_cache.overlay_cache[i];
+ oi = &oc->info;
if (oc->channel != mgr->id)
continue;
@@ -1068,39 +1146,39 @@
if (!dispc_is_overlay_scaled(oc))
continue;
- outw = oc->out_width == 0 ?
- oc->width : oc->out_width;
- outh = oc->out_height == 0 ?
- oc->height : oc->out_height;
+ outw = oi->out_width == 0 ?
+ oi->width : oi->out_width;
+ outh = oi->out_height == 0 ?
+ oi->height : oi->out_height;
/* is the overlay outside the update region? */
if (!rectangle_intersects(x, y, w, h,
- oc->pos_x, oc->pos_y,
+ oi->pos_x, oi->pos_y,
outw, outh))
continue;
/* if the overlay totally inside the update region? */
- if (rectangle_subset(oc->pos_x, oc->pos_y, outw, outh,
+ if (rectangle_subset(oi->pos_x, oi->pos_y, outw, outh,
x, y, w, h))
continue;
- if (x > oc->pos_x)
- x1 = oc->pos_x;
+ if (x > oi->pos_x)
+ x1 = oi->pos_x;
else
x1 = x;
- if (y > oc->pos_y)
- y1 = oc->pos_y;
+ if (y > oi->pos_y)
+ y1 = oi->pos_y;
else
y1 = y;
- if ((x + w) < (oc->pos_x + outw))
- x2 = oc->pos_x + outw;
+ if ((x + w) < (oi->pos_x + outw))
+ x2 = oi->pos_x + outw;
else
x2 = x + w;
- if ((y + h) < (oc->pos_y + outh))
- y2 = oc->pos_y + outh;
+ if ((y + h) < (oi->pos_y + outh))
+ y2 = oi->pos_y + outh;
else
y2 = y + h;
@@ -1236,6 +1314,10 @@
DSSDBG("omap_dss_mgr_apply(%s)\n", mgr->name);
+ r = dispc_runtime_get();
+ if (r)
+ return r;
+
spin_lock_irqsave(&dss_cache.lock, flags);
/* Configure overlays */
@@ -1275,23 +1357,7 @@
ovl->info_dirty = false;
oc->dirty = true;
-
- oc->paddr = ovl->info.paddr;
- oc->vaddr = ovl->info.vaddr;
- oc->p_uv_addr = ovl->info.p_uv_addr;
- oc->screen_width = ovl->info.screen_width;
- oc->width = ovl->info.width;
- oc->height = ovl->info.height;
- oc->color_mode = ovl->info.color_mode;
- oc->rotation = ovl->info.rotation;
- oc->rotation_type = ovl->info.rotation_type;
- oc->mirror = ovl->info.mirror;
- oc->pos_x = ovl->info.pos_x;
- oc->pos_y = ovl->info.pos_y;
- oc->out_width = ovl->info.out_width;
- oc->out_height = ovl->info.out_height;
- oc->global_alpha = ovl->info.global_alpha;
- oc->pre_mult_alpha = ovl->info.pre_mult_alpha;
+ oc->info = ovl->info;
oc->replication =
dss_use_replication(dssdev, ovl->info.color_mode);
@@ -1302,11 +1368,6 @@
oc->enabled = true;
- oc->manual_update =
- dssdev->caps & OMAP_DSS_DISPLAY_CAP_MANUAL_UPDATE &&
- dssdev->driver->get_update_mode(dssdev) !=
- OMAP_DSS_UPDATE_AUTO;
-
++num_planes_enabled;
}
@@ -1334,20 +1395,10 @@
mgr->info_dirty = false;
mc->dirty = true;
-
- mc->default_color = mgr->info.default_color;
- mc->trans_key_type = mgr->info.trans_key_type;
- mc->trans_key = mgr->info.trans_key;
- mc->trans_enabled = mgr->info.trans_enabled;
- mc->alpha_enabled = mgr->info.alpha_enabled;
-
- mc->manual_upd_display =
- dssdev->caps & OMAP_DSS_DISPLAY_CAP_MANUAL_UPDATE;
+ mc->info = mgr->info;
mc->manual_update =
- dssdev->caps & OMAP_DSS_DISPLAY_CAP_MANUAL_UPDATE &&
- dssdev->driver->get_update_mode(dssdev) !=
- OMAP_DSS_UPDATE_AUTO;
+ dssdev->caps & OMAP_DSS_DISPLAY_CAP_MANUAL_UPDATE;
}
/* XXX TODO: Try to get fifomerge working. The problem is that it
@@ -1368,7 +1419,7 @@
/* Configure overlay fifos */
for (i = 0; i < omap_dss_get_num_overlays(); ++i) {
struct omap_dss_device *dssdev;
- u32 size;
+ u32 size, burst_size;
ovl = omap_dss_get_overlay(i);
@@ -1386,6 +1437,8 @@
if (use_fifomerge)
size *= 3;
+ burst_size = dispc_get_burst_size(ovl->id);
+
switch (dssdev->type) {
case OMAP_DISPLAY_TYPE_DPI:
case OMAP_DISPLAY_TYPE_DBI:
@@ -1393,13 +1446,13 @@
case OMAP_DISPLAY_TYPE_VENC:
case OMAP_DISPLAY_TYPE_HDMI:
default_get_overlay_fifo_thresholds(ovl->id, size,
- &oc->burst_size, &oc->fifo_low,
+ burst_size, &oc->fifo_low,
&oc->fifo_high);
break;
#ifdef CONFIG_OMAP2_DSS_DSI
case OMAP_DISPLAY_TYPE_DSI:
dsi_get_overlay_fifo_thresholds(ovl->id, size,
- &oc->burst_size, &oc->fifo_low,
+ burst_size, &oc->fifo_low,
&oc->fifo_high);
break;
#endif
@@ -1409,7 +1462,6 @@
}
r = 0;
- dss_clk_enable(DSS_CLK_ICK | DSS_CLK_FCK);
if (!dss_cache.irq_enabled) {
u32 mask;
@@ -1422,10 +1474,11 @@
dss_cache.irq_enabled = true;
}
configure_dispc();
- dss_clk_disable(DSS_CLK_ICK | DSS_CLK_FCK);
spin_unlock_irqrestore(&dss_cache.lock, flags);
+ dispc_runtime_put();
+
return r;
}
diff --git a/drivers/video/omap2/dss/overlay.c b/drivers/video/omap2/dss/overlay.c
index 0f08025..c84380c 100644
--- a/drivers/video/omap2/dss/overlay.c
+++ b/drivers/video/omap2/dss/overlay.c
@@ -84,32 +84,42 @@
old_mgr = ovl->manager;
+ r = dispc_runtime_get();
+ if (r)
+ return r;
+
/* detach old manager */
if (old_mgr) {
r = ovl->unset_manager(ovl);
if (r) {
DSSERR("detach failed\n");
- return r;
+ goto err;
}
r = old_mgr->apply(old_mgr);
if (r)
- return r;
+ goto err;
}
if (mgr) {
r = ovl->set_manager(ovl, mgr);
if (r) {
DSSERR("Failed to attach overlay\n");
- return r;
+ goto err;
}
r = mgr->apply(mgr);
if (r)
- return r;
+ goto err;
}
+ dispc_runtime_put();
+
return size;
+
+err:
+ dispc_runtime_put();
+ return r;
}
static ssize_t overlay_input_size_show(struct omap_overlay *ovl, char *buf)
@@ -238,6 +248,9 @@
u8 alpha;
struct omap_overlay_info info;
+ if (!dss_has_feature(FEAT_GLOBAL_ALPHA))
+ return -ENODEV;
+
r = kstrtou8(buf, 0, &alpha);
if (r)
return r;
@@ -504,7 +517,6 @@
ovl->manager = mgr;
- dss_clk_enable(DSS_CLK_ICK | DSS_CLK_FCK);
/* XXX: When there is an overlay on a DSI manual update display, and
* the overlay is first disabled, then moved to tv, and enabled, we
* seem to get SYNC_LOST_DIGIT error.
@@ -518,7 +530,6 @@
* the overlay, but before moving the overlay to TV.
*/
dispc_set_channel_out(ovl->id, mgr->id);
- dss_clk_disable(DSS_CLK_ICK | DSS_CLK_FCK);
return 0;
}
@@ -719,6 +730,8 @@
}
if (mgr) {
+ dispc_runtime_get();
+
for (i = 0; i < dss_feat_get_num_ovls(); i++) {
struct omap_overlay *ovl;
ovl = omap_dss_get_overlay(i);
@@ -728,6 +741,8 @@
omap_dss_set_manager(ovl, mgr);
}
}
+
+ dispc_runtime_put();
}
}
diff --git a/drivers/video/omap2/dss/rfbi.c b/drivers/video/omap2/dss/rfbi.c
index c06fbe0..39f4c59 100644
--- a/drivers/video/omap2/dss/rfbi.c
+++ b/drivers/video/omap2/dss/rfbi.c
@@ -33,6 +33,8 @@
#include <linux/hrtimer.h>
#include <linux/seq_file.h>
#include <linux/semaphore.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
#include <video/omapdss.h>
#include "dss.h"
@@ -120,12 +122,25 @@
return __raw_readl(rfbi.base + idx.idx);
}
-static void rfbi_enable_clocks(bool enable)
+static int rfbi_runtime_get(void)
{
- if (enable)
- dss_clk_enable(DSS_CLK_ICK | DSS_CLK_FCK);
- else
- dss_clk_disable(DSS_CLK_ICK | DSS_CLK_FCK);
+ int r;
+
+ DSSDBG("rfbi_runtime_get\n");
+
+ r = pm_runtime_get_sync(&rfbi.pdev->dev);
+ WARN_ON(r < 0);
+ return r < 0 ? r : 0;
+}
+
+static void rfbi_runtime_put(void)
+{
+ int r;
+
+ DSSDBG("rfbi_runtime_put\n");
+
+ r = pm_runtime_put(&rfbi.pdev->dev);
+ WARN_ON(r < 0);
}
void rfbi_bus_lock(void)
@@ -805,7 +820,8 @@
{
#define DUMPREG(r) seq_printf(s, "%-35s %08x\n", #r, rfbi_read_reg(r))
- dss_clk_enable(DSS_CLK_ICK | DSS_CLK_FCK);
+ if (rfbi_runtime_get())
+ return;
DUMPREG(RFBI_REVISION);
DUMPREG(RFBI_SYSCONFIG);
@@ -836,7 +852,7 @@
DUMPREG(RFBI_VSYNC_WIDTH);
DUMPREG(RFBI_HSYNC_WIDTH);
- dss_clk_disable(DSS_CLK_ICK | DSS_CLK_FCK);
+ rfbi_runtime_put();
#undef DUMPREG
}
@@ -844,7 +860,9 @@
{
int r;
- rfbi_enable_clocks(1);
+ r = rfbi_runtime_get();
+ if (r)
+ return r;
r = omap_dss_start_device(dssdev);
if (r) {
@@ -879,6 +897,7 @@
err1:
omap_dss_stop_device(dssdev);
err0:
+ rfbi_runtime_put();
return r;
}
EXPORT_SYMBOL(omapdss_rfbi_display_enable);
@@ -889,7 +908,7 @@
DISPC_IRQ_FRAMEDONE);
omap_dss_stop_device(dssdev);
- rfbi_enable_clocks(0);
+ rfbi_runtime_put();
}
EXPORT_SYMBOL(omapdss_rfbi_display_disable);
@@ -904,8 +923,9 @@
static int omap_rfbihw_probe(struct platform_device *pdev)
{
u32 rev;
- u32 l;
struct resource *rfbi_mem;
+ struct clk *clk;
+ int r;
rfbi.pdev = pdev;
@@ -914,46 +934,102 @@
rfbi_mem = platform_get_resource(rfbi.pdev, IORESOURCE_MEM, 0);
if (!rfbi_mem) {
DSSERR("can't get IORESOURCE_MEM RFBI\n");
- return -EINVAL;
+ r = -EINVAL;
+ goto err_ioremap;
}
rfbi.base = ioremap(rfbi_mem->start, resource_size(rfbi_mem));
if (!rfbi.base) {
DSSERR("can't ioremap RFBI\n");
- return -ENOMEM;
+ r = -ENOMEM;
+ goto err_ioremap;
}
- rfbi_enable_clocks(1);
+ pm_runtime_enable(&pdev->dev);
+
+ r = rfbi_runtime_get();
+ if (r)
+ goto err_get_rfbi;
msleep(10);
- rfbi.l4_khz = dss_clk_get_rate(DSS_CLK_ICK) / 1000;
+ if (cpu_is_omap24xx() || cpu_is_omap34xx() || cpu_is_omap3630())
+ clk = dss_get_ick();
+ else
+ clk = clk_get(&pdev->dev, "ick");
+ if (IS_ERR(clk)) {
+ DSSERR("can't get ick\n");
+ r = PTR_ERR(clk);
+ goto err_get_ick;
+ }
- /* Enable autoidle and smart-idle */
- l = rfbi_read_reg(RFBI_SYSCONFIG);
- l |= (1 << 0) | (2 << 3);
- rfbi_write_reg(RFBI_SYSCONFIG, l);
+ rfbi.l4_khz = clk_get_rate(clk) / 1000;
+
+ clk_put(clk);
rev = rfbi_read_reg(RFBI_REVISION);
dev_dbg(&pdev->dev, "OMAP RFBI rev %d.%d\n",
FLD_GET(rev, 7, 4), FLD_GET(rev, 3, 0));
- rfbi_enable_clocks(0);
+ rfbi_runtime_put();
return 0;
+
+err_get_ick:
+ rfbi_runtime_put();
+err_get_rfbi:
+ pm_runtime_disable(&pdev->dev);
+ iounmap(rfbi.base);
+err_ioremap:
+ return r;
}
static int omap_rfbihw_remove(struct platform_device *pdev)
{
+ pm_runtime_disable(&pdev->dev);
iounmap(rfbi.base);
return 0;
}
+static int rfbi_runtime_suspend(struct device *dev)
+{
+ dispc_runtime_put();
+ dss_runtime_put();
+
+ return 0;
+}
+
+static int rfbi_runtime_resume(struct device *dev)
+{
+ int r;
+
+ r = dss_runtime_get();
+ if (r < 0)
+ goto err_get_dss;
+
+ r = dispc_runtime_get();
+ if (r < 0)
+ goto err_get_dispc;
+
+ return 0;
+
+err_get_dispc:
+ dss_runtime_put();
+err_get_dss:
+ return r;
+}
+
+static const struct dev_pm_ops rfbi_pm_ops = {
+ .runtime_suspend = rfbi_runtime_suspend,
+ .runtime_resume = rfbi_runtime_resume,
+};
+
static struct platform_driver omap_rfbihw_driver = {
.probe = omap_rfbihw_probe,
.remove = omap_rfbihw_remove,
.driver = {
.name = "omapdss_rfbi",
.owner = THIS_MODULE,
+ .pm = &rfbi_pm_ops,
},
};
diff --git a/drivers/video/omap2/dss/sdi.c b/drivers/video/omap2/dss/sdi.c
index 0bd4b03..3a688c8 100644
--- a/drivers/video/omap2/dss/sdi.c
+++ b/drivers/video/omap2/dss/sdi.c
@@ -20,13 +20,11 @@
#define DSS_SUBSYS_NAME "SDI"
#include <linux/kernel.h>
-#include <linux/clk.h>
#include <linux/delay.h>
#include <linux/err.h>
#include <linux/regulator/consumer.h>
#include <video/omapdss.h>
-#include <plat/cpu.h>
#include "dss.h"
static struct {
@@ -60,14 +58,20 @@
r = omap_dss_start_device(dssdev);
if (r) {
DSSERR("failed to start device\n");
- goto err0;
+ goto err_start_dev;
}
r = regulator_enable(sdi.vdds_sdi_reg);
if (r)
- goto err1;
+ goto err_reg_enable;
- dss_clk_enable(DSS_CLK_ICK | DSS_CLK_FCK);
+ r = dss_runtime_get();
+ if (r)
+ goto err_get_dss;
+
+ r = dispc_runtime_get();
+ if (r)
+ goto err_get_dispc;
sdi_basic_init(dssdev);
@@ -80,7 +84,7 @@
r = dss_calc_clock_div(1, t->pixel_clock * 1000,
&dss_cinfo, &dispc_cinfo);
if (r)
- goto err2;
+ goto err_calc_clock_div;
fck = dss_cinfo.fck;
lck_div = dispc_cinfo.lck_div;
@@ -101,27 +105,34 @@
r = dss_set_clock_div(&dss_cinfo);
if (r)
- goto err2;
+ goto err_set_dss_clock_div;
r = dispc_set_clock_div(dssdev->manager->id, &dispc_cinfo);
if (r)
- goto err2;
+ goto err_set_dispc_clock_div;
dss_sdi_init(dssdev->phy.sdi.datapairs);
r = dss_sdi_enable();
if (r)
- goto err1;
+ goto err_sdi_enable;
mdelay(2);
dssdev->manager->enable(dssdev->manager);
return 0;
-err2:
- dss_clk_disable(DSS_CLK_ICK | DSS_CLK_FCK);
+
+err_sdi_enable:
+err_set_dispc_clock_div:
+err_set_dss_clock_div:
+err_calc_clock_div:
+ dispc_runtime_put();
+err_get_dispc:
+ dss_runtime_put();
+err_get_dss:
regulator_disable(sdi.vdds_sdi_reg);
-err1:
+err_reg_enable:
omap_dss_stop_device(dssdev);
-err0:
+err_start_dev:
return r;
}
EXPORT_SYMBOL(omapdss_sdi_display_enable);
@@ -132,7 +143,8 @@
dss_sdi_disable();
- dss_clk_disable(DSS_CLK_ICK | DSS_CLK_FCK);
+ dispc_runtime_put();
+ dss_runtime_put();
regulator_disable(sdi.vdds_sdi_reg);
diff --git a/drivers/video/omap2/dss/venc.c b/drivers/video/omap2/dss/venc.c
index 980f919..173c664 100644
--- a/drivers/video/omap2/dss/venc.c
+++ b/drivers/video/omap2/dss/venc.c
@@ -33,11 +33,13 @@
#include <linux/seq_file.h>
#include <linux/platform_device.h>
#include <linux/regulator/consumer.h>
+#include <linux/pm_runtime.h>
#include <video/omapdss.h>
#include <plat/cpu.h>
#include "dss.h"
+#include "dss_features.h"
/* Venc registers */
#define VENC_REV_ID 0x00
@@ -292,6 +294,9 @@
struct mutex venc_lock;
u32 wss_data;
struct regulator *vdda_dac_reg;
+
+ struct clk *tv_clk;
+ struct clk *tv_dac_clk;
} venc;
static inline void venc_write_reg(int idx, u32 val)
@@ -380,14 +385,25 @@
#endif
}
-static void venc_enable_clocks(int enable)
+static int venc_runtime_get(void)
{
- if (enable)
- dss_clk_enable(DSS_CLK_ICK | DSS_CLK_FCK | DSS_CLK_TVFCK |
- DSS_CLK_VIDFCK);
- else
- dss_clk_disable(DSS_CLK_ICK | DSS_CLK_FCK | DSS_CLK_TVFCK |
- DSS_CLK_VIDFCK);
+ int r;
+
+ DSSDBG("venc_runtime_get\n");
+
+ r = pm_runtime_get_sync(&venc.pdev->dev);
+ WARN_ON(r < 0);
+ return r < 0 ? r : 0;
+}
+
+static void venc_runtime_put(void)
+{
+ int r;
+
+ DSSDBG("venc_runtime_put\n");
+
+ r = pm_runtime_put(&venc.pdev->dev);
+ WARN_ON(r < 0);
}
static const struct venc_config *venc_timings_to_config(
@@ -406,8 +422,6 @@
{
u32 l;
- venc_enable_clocks(1);
-
venc_reset();
venc_write_config(venc_timings_to_config(&dssdev->panel.timings));
@@ -448,8 +462,6 @@
dssdev->platform_disable(dssdev);
regulator_disable(venc.vdda_dac_reg);
-
- venc_enable_clocks(0);
}
@@ -487,6 +499,10 @@
goto err1;
}
+ r = venc_runtime_get();
+ if (r)
+ goto err1;
+
venc_power_on(dssdev);
venc.wss_data = 0;
@@ -520,6 +536,8 @@
venc_power_off(dssdev);
+ venc_runtime_put();
+
dssdev->state = OMAP_DSS_DISPLAY_DISABLED;
omap_dss_stop_device(dssdev);
@@ -538,20 +556,6 @@
return venc_panel_enable(dssdev);
}
-static enum omap_dss_update_mode venc_get_update_mode(
- struct omap_dss_device *dssdev)
-{
- return OMAP_DSS_UPDATE_AUTO;
-}
-
-static int venc_set_update_mode(struct omap_dss_device *dssdev,
- enum omap_dss_update_mode mode)
-{
- if (mode != OMAP_DSS_UPDATE_AUTO)
- return -EINVAL;
- return 0;
-}
-
static void venc_get_timings(struct omap_dss_device *dssdev,
struct omap_video_timings *timings)
{
@@ -598,6 +602,7 @@
static int venc_set_wss(struct omap_dss_device *dssdev, u32 wss)
{
const struct venc_config *config;
+ int r;
DSSDBG("venc_set_wss\n");
@@ -608,16 +613,19 @@
/* Invert due to VENC_L21_WC_CTL:INV=1 */
venc.wss_data = (wss ^ 0xfffff) << 8;
- venc_enable_clocks(1);
+ r = venc_runtime_get();
+ if (r)
+ goto err;
venc_write_reg(VENC_BSTAMP_WSS_DATA, config->bstamp_wss_data |
venc.wss_data);
- venc_enable_clocks(0);
+ venc_runtime_put();
+err:
mutex_unlock(&venc.venc_lock);
- return 0;
+ return r;
}
static struct omap_dss_driver venc_driver = {
@@ -632,9 +640,6 @@
.get_resolution = omapdss_default_get_resolution,
.get_recommended_bpp = omapdss_default_get_recommended_bpp,
- .set_update_mode = venc_set_update_mode,
- .get_update_mode = venc_get_update_mode,
-
.get_timings = venc_get_timings,
.set_timings = venc_set_timings,
.check_timings = venc_check_timings,
@@ -673,7 +678,8 @@
{
#define DUMPREG(r) seq_printf(s, "%-35s %08x\n", #r, venc_read_reg(r))
- venc_enable_clocks(1);
+ if (venc_runtime_get())
+ return;
DUMPREG(VENC_F_CONTROL);
DUMPREG(VENC_VIDOUT_CTRL);
@@ -717,16 +723,56 @@
DUMPREG(VENC_OUTPUT_CONTROL);
DUMPREG(VENC_OUTPUT_TEST);
- venc_enable_clocks(0);
+ venc_runtime_put();
#undef DUMPREG
}
+static int venc_get_clocks(struct platform_device *pdev)
+{
+ struct clk *clk;
+
+ clk = clk_get(&pdev->dev, "fck");
+ if (IS_ERR(clk)) {
+ DSSERR("can't get fck\n");
+ return PTR_ERR(clk);
+ }
+
+ venc.tv_clk = clk;
+
+ if (dss_has_feature(FEAT_VENC_REQUIRES_TV_DAC_CLK)) {
+ if (cpu_is_omap34xx() || cpu_is_omap3630())
+ clk = clk_get(&pdev->dev, "dss_96m_fck");
+ else
+ clk = clk_get(&pdev->dev, "tv_dac_clk");
+ if (IS_ERR(clk)) {
+ DSSERR("can't get tv_dac_clk\n");
+ clk_put(venc.tv_clk);
+ return PTR_ERR(clk);
+ }
+ } else {
+ clk = NULL;
+ }
+
+ venc.tv_dac_clk = clk;
+
+ return 0;
+}
+
+static void venc_put_clocks(void)
+{
+ if (venc.tv_clk)
+ clk_put(venc.tv_clk);
+ if (venc.tv_dac_clk)
+ clk_put(venc.tv_dac_clk);
+}
+
/* VENC HW IP initialisation */
static int omap_venchw_probe(struct platform_device *pdev)
{
u8 rev_id;
struct resource *venc_mem;
+ int r;
venc.pdev = pdev;
@@ -737,22 +783,40 @@
venc_mem = platform_get_resource(venc.pdev, IORESOURCE_MEM, 0);
if (!venc_mem) {
DSSERR("can't get IORESOURCE_MEM VENC\n");
- return -EINVAL;
+ r = -EINVAL;
+ goto err_ioremap;
}
venc.base = ioremap(venc_mem->start, resource_size(venc_mem));
if (!venc.base) {
DSSERR("can't ioremap VENC\n");
- return -ENOMEM;
+ r = -ENOMEM;
+ goto err_ioremap;
}
- venc_enable_clocks(1);
+ r = venc_get_clocks(pdev);
+ if (r)
+ goto err_get_clk;
+
+ pm_runtime_enable(&pdev->dev);
+
+ r = venc_runtime_get();
+ if (r)
+ goto err_get_venc;
rev_id = (u8)(venc_read_reg(VENC_REV_ID) & 0xff);
dev_dbg(&pdev->dev, "OMAP VENC rev %d\n", rev_id);
- venc_enable_clocks(0);
+ venc_runtime_put();
return omap_dss_register_driver(&venc_driver);
+
+err_get_venc:
+ pm_runtime_disable(&pdev->dev);
+ venc_put_clocks();
+err_get_clk:
+ iounmap(venc.base);
+err_ioremap:
+ return r;
}
static int omap_venchw_remove(struct platform_device *pdev)
@@ -763,16 +827,61 @@
}
omap_dss_unregister_driver(&venc_driver);
+ pm_runtime_disable(&pdev->dev);
+ venc_put_clocks();
+
iounmap(venc.base);
return 0;
}
+static int venc_runtime_suspend(struct device *dev)
+{
+ if (venc.tv_dac_clk)
+ clk_disable(venc.tv_dac_clk);
+ clk_disable(venc.tv_clk);
+
+ dispc_runtime_put();
+ dss_runtime_put();
+
+ return 0;
+}
+
+static int venc_runtime_resume(struct device *dev)
+{
+ int r;
+
+ r = dss_runtime_get();
+ if (r < 0)
+ goto err_get_dss;
+
+ r = dispc_runtime_get();
+ if (r < 0)
+ goto err_get_dispc;
+
+ clk_enable(venc.tv_clk);
+ if (venc.tv_dac_clk)
+ clk_enable(venc.tv_dac_clk);
+
+ return 0;
+
+err_get_dispc:
+ dss_runtime_put();
+err_get_dss:
+ return r;
+}
+
+static const struct dev_pm_ops venc_pm_ops = {
+ .runtime_suspend = venc_runtime_suspend,
+ .runtime_resume = venc_runtime_resume,
+};
+
static struct platform_driver omap_venchw_driver = {
.probe = omap_venchw_probe,
.remove = omap_venchw_remove,
.driver = {
.name = "omapdss_venc",
.owner = THIS_MODULE,
+ .pm = &venc_pm_ops,
},
};
diff --git a/drivers/video/omap2/omapfb/omapfb-ioctl.c b/drivers/video/omap2/omapfb/omapfb-ioctl.c
index cff4503..6b1ac23 100644
--- a/drivers/video/omap2/omapfb/omapfb-ioctl.c
+++ b/drivers/video/omap2/omapfb/omapfb-ioctl.c
@@ -316,68 +316,68 @@
}
EXPORT_SYMBOL(omapfb_update_window);
-static int omapfb_set_update_mode(struct fb_info *fbi,
+int omapfb_set_update_mode(struct fb_info *fbi,
enum omapfb_update_mode mode)
{
struct omap_dss_device *display = fb2display(fbi);
- enum omap_dss_update_mode um;
+ struct omapfb_info *ofbi = FB2OFB(fbi);
+ struct omapfb2_device *fbdev = ofbi->fbdev;
+ struct omapfb_display_data *d;
int r;
- if (!display || !display->driver->set_update_mode)
- return -EINVAL;
-
- switch (mode) {
- case OMAPFB_UPDATE_DISABLED:
- um = OMAP_DSS_UPDATE_DISABLED;
- break;
-
- case OMAPFB_AUTO_UPDATE:
- um = OMAP_DSS_UPDATE_AUTO;
- break;
-
- case OMAPFB_MANUAL_UPDATE:
- um = OMAP_DSS_UPDATE_MANUAL;
- break;
-
- default:
- return -EINVAL;
- }
-
- r = display->driver->set_update_mode(display, um);
-
- return r;
-}
-
-static int omapfb_get_update_mode(struct fb_info *fbi,
- enum omapfb_update_mode *mode)
-{
- struct omap_dss_device *display = fb2display(fbi);
- enum omap_dss_update_mode m;
-
if (!display)
return -EINVAL;
- if (!display->driver->get_update_mode) {
- *mode = OMAPFB_AUTO_UPDATE;
+ if (mode != OMAPFB_AUTO_UPDATE && mode != OMAPFB_MANUAL_UPDATE)
+ return -EINVAL;
+
+ omapfb_lock(fbdev);
+
+ d = get_display_data(fbdev, display);
+
+ if (d->update_mode == mode) {
+ omapfb_unlock(fbdev);
return 0;
}
- m = display->driver->get_update_mode(display);
+ r = 0;
- switch (m) {
- case OMAP_DSS_UPDATE_DISABLED:
- *mode = OMAPFB_UPDATE_DISABLED;
- break;
- case OMAP_DSS_UPDATE_AUTO:
- *mode = OMAPFB_AUTO_UPDATE;
- break;
- case OMAP_DSS_UPDATE_MANUAL:
- *mode = OMAPFB_MANUAL_UPDATE;
- break;
- default:
- BUG();
+ if (display->caps & OMAP_DSS_DISPLAY_CAP_MANUAL_UPDATE) {
+ if (mode == OMAPFB_AUTO_UPDATE)
+ omapfb_start_auto_update(fbdev, display);
+ else /* MANUAL_UPDATE */
+ omapfb_stop_auto_update(fbdev, display);
+
+ d->update_mode = mode;
+ } else { /* AUTO_UPDATE */
+ if (mode == OMAPFB_MANUAL_UPDATE)
+ r = -EINVAL;
}
+ omapfb_unlock(fbdev);
+
+ return r;
+}
+
+int omapfb_get_update_mode(struct fb_info *fbi,
+ enum omapfb_update_mode *mode)
+{
+ struct omap_dss_device *display = fb2display(fbi);
+ struct omapfb_info *ofbi = FB2OFB(fbi);
+ struct omapfb2_device *fbdev = ofbi->fbdev;
+ struct omapfb_display_data *d;
+
+ if (!display)
+ return -EINVAL;
+
+ omapfb_lock(fbdev);
+
+ d = get_display_data(fbdev, display);
+
+ *mode = d->update_mode;
+
+ omapfb_unlock(fbdev);
+
return 0;
}
diff --git a/drivers/video/omap2/omapfb/omapfb-main.c b/drivers/video/omap2/omapfb/omapfb-main.c
index 505bc12..602b71a 100644
--- a/drivers/video/omap2/omapfb/omapfb-main.c
+++ b/drivers/video/omap2/omapfb/omapfb-main.c
@@ -46,6 +46,10 @@
static int def_vrfb;
static int def_rotate;
static int def_mirror;
+static bool auto_update;
+static unsigned int auto_update_freq;
+module_param(auto_update, bool, 0);
+module_param(auto_update_freq, uint, 0644);
#ifdef DEBUG
unsigned int omapfb_debug;
@@ -1242,6 +1246,7 @@
struct omapfb_info *ofbi = FB2OFB(fbi);
struct omapfb2_device *fbdev = ofbi->fbdev;
struct omap_dss_device *display = fb2display(fbi);
+ struct omapfb_display_data *d;
int r = 0;
if (!display)
@@ -1249,6 +1254,8 @@
omapfb_lock(fbdev);
+ d = get_display_data(fbdev, display);
+
switch (blank) {
case FB_BLANK_UNBLANK:
if (display->state != OMAP_DSS_DISPLAY_SUSPENDED)
@@ -1257,6 +1264,11 @@
if (display->driver->resume)
r = display->driver->resume(display);
+ if ((display->caps & OMAP_DSS_DISPLAY_CAP_MANUAL_UPDATE) &&
+ d->update_mode == OMAPFB_AUTO_UPDATE &&
+ !d->auto_update_work_enabled)
+ omapfb_start_auto_update(fbdev, display);
+
break;
case FB_BLANK_NORMAL:
@@ -1268,6 +1280,9 @@
if (display->state != OMAP_DSS_DISPLAY_ACTIVE)
goto exit;
+ if (d->auto_update_work_enabled)
+ omapfb_stop_auto_update(fbdev, display);
+
if (display->driver->suspend)
r = display->driver->suspend(display);
@@ -1724,6 +1739,78 @@
return r;
}
+static void omapfb_auto_update_work(struct work_struct *work)
+{
+ struct omap_dss_device *dssdev;
+ struct omap_dss_driver *dssdrv;
+ struct omapfb_display_data *d;
+ u16 w, h;
+ unsigned int freq;
+ struct omapfb2_device *fbdev;
+
+ d = container_of(work, struct omapfb_display_data,
+ auto_update_work.work);
+
+ dssdev = d->dssdev;
+ dssdrv = dssdev->driver;
+ fbdev = d->fbdev;
+
+ if (!dssdrv || !dssdrv->update)
+ return;
+
+ if (dssdrv->sync)
+ dssdrv->sync(dssdev);
+
+ dssdrv->get_resolution(dssdev, &w, &h);
+ dssdrv->update(dssdev, 0, 0, w, h);
+
+ freq = auto_update_freq;
+ if (freq == 0)
+ freq = 20;
+ queue_delayed_work(fbdev->auto_update_wq,
+ &d->auto_update_work, HZ / freq);
+}
+
+void omapfb_start_auto_update(struct omapfb2_device *fbdev,
+ struct omap_dss_device *display)
+{
+ struct omapfb_display_data *d;
+
+ if (fbdev->auto_update_wq == NULL) {
+ struct workqueue_struct *wq;
+
+ wq = create_singlethread_workqueue("omapfb_auto_update");
+
+ if (wq == NULL) {
+ dev_err(fbdev->dev, "Failed to create workqueue for "
+ "auto-update\n");
+ return;
+ }
+
+ fbdev->auto_update_wq = wq;
+ }
+
+ d = get_display_data(fbdev, display);
+
+ INIT_DELAYED_WORK(&d->auto_update_work, omapfb_auto_update_work);
+
+ d->auto_update_work_enabled = true;
+
+ omapfb_auto_update_work(&d->auto_update_work.work);
+}
+
+void omapfb_stop_auto_update(struct omapfb2_device *fbdev,
+ struct omap_dss_device *display)
+{
+ struct omapfb_display_data *d;
+
+ d = get_display_data(fbdev, display);
+
+ cancel_delayed_work_sync(&d->auto_update_work);
+
+ d->auto_update_work_enabled = false;
+}
+
/* initialize fb_info, var, fix to something sane based on the display */
static int omapfb_fb_init(struct omapfb2_device *fbdev, struct fb_info *fbi)
{
@@ -1858,10 +1945,21 @@
}
for (i = 0; i < fbdev->num_displays; i++) {
- if (fbdev->displays[i]->state != OMAP_DSS_DISPLAY_DISABLED)
- fbdev->displays[i]->driver->disable(fbdev->displays[i]);
+ struct omap_dss_device *dssdev = fbdev->displays[i].dssdev;
- omap_dss_put_device(fbdev->displays[i]);
+ if (fbdev->displays[i].auto_update_work_enabled)
+ omapfb_stop_auto_update(fbdev, dssdev);
+
+ if (dssdev->state != OMAP_DSS_DISPLAY_DISABLED)
+ dssdev->driver->disable(dssdev);
+
+ omap_dss_put_device(dssdev);
+ }
+
+ if (fbdev->auto_update_wq != NULL) {
+ flush_workqueue(fbdev->auto_update_wq);
+ destroy_workqueue(fbdev->auto_update_wq);
+ fbdev->auto_update_wq = NULL;
}
dev_set_drvdata(fbdev->dev, NULL);
@@ -2084,14 +2182,14 @@
int r;
u8 bpp;
struct omap_video_timings timings, temp_timings;
+ struct omapfb_display_data *d;
r = omapfb_mode_to_timings(mode_str, &timings, &bpp);
if (r)
return r;
- fbdev->bpp_overrides[fbdev->num_bpp_overrides].dssdev = display;
- fbdev->bpp_overrides[fbdev->num_bpp_overrides].bpp = bpp;
- ++fbdev->num_bpp_overrides;
+ d = get_display_data(fbdev, display);
+ d->bpp_override = bpp;
if (display->driver->check_timings) {
r = display->driver->check_timings(display, &timings);
@@ -2117,14 +2215,14 @@
static int omapfb_get_recommended_bpp(struct omapfb2_device *fbdev,
struct omap_dss_device *dssdev)
{
- int i;
+ struct omapfb_display_data *d;
BUG_ON(dssdev->driver->get_recommended_bpp == NULL);
- for (i = 0; i < fbdev->num_bpp_overrides; ++i) {
- if (dssdev == fbdev->bpp_overrides[i].dssdev)
- return fbdev->bpp_overrides[i].bpp;
- }
+ d = get_display_data(fbdev, dssdev);
+
+ if (d->bpp_override != 0)
+ return d->bpp_override;
return dssdev->driver->get_recommended_bpp(dssdev);
}
@@ -2156,9 +2254,9 @@
display = NULL;
for (i = 0; i < fbdev->num_displays; ++i) {
- if (strcmp(fbdev->displays[i]->name,
+ if (strcmp(fbdev->displays[i].dssdev->name,
display_str) == 0) {
- display = fbdev->displays[i];
+ display = fbdev->displays[i].dssdev;
break;
}
}
@@ -2182,6 +2280,7 @@
struct omap_dss_device *dssdev)
{
struct omap_dss_driver *dssdrv = dssdev->driver;
+ struct omapfb_display_data *d;
int r;
r = dssdrv->enable(dssdev);
@@ -2191,8 +2290,20 @@
return r;
}
+ d = get_display_data(fbdev, dssdev);
+
+ d->fbdev = fbdev;
+
if (dssdev->caps & OMAP_DSS_DISPLAY_CAP_MANUAL_UPDATE) {
u16 w, h;
+
+ if (auto_update) {
+ omapfb_start_auto_update(fbdev, dssdev);
+ d->update_mode = OMAPFB_AUTO_UPDATE;
+ } else {
+ d->update_mode = OMAPFB_MANUAL_UPDATE;
+ }
+
if (dssdrv->enable_te) {
r = dssdrv->enable_te(dssdev, 1);
if (r) {
@@ -2201,16 +2312,6 @@
}
}
- if (dssdrv->set_update_mode) {
- r = dssdrv->set_update_mode(dssdev,
- OMAP_DSS_UPDATE_MANUAL);
- if (r) {
- dev_err(fbdev->dev,
- "Failed to set update mode\n");
- return r;
- }
- }
-
dssdrv->get_resolution(dssdev, &w, &h);
r = dssdrv->update(dssdev, 0, 0, w, h);
if (r) {
@@ -2219,15 +2320,7 @@
return r;
}
} else {
- if (dssdrv->set_update_mode) {
- r = dssdrv->set_update_mode(dssdev,
- OMAP_DSS_UPDATE_AUTO);
- if (r) {
- dev_err(fbdev->dev,
- "Failed to set update mode\n");
- return r;
- }
- }
+ d->update_mode = OMAPFB_AUTO_UPDATE;
}
return 0;
@@ -2275,6 +2368,8 @@
fbdev->num_displays = 0;
dssdev = NULL;
for_each_dss_dev(dssdev) {
+ struct omapfb_display_data *d;
+
omap_dss_get_device(dssdev);
if (!dssdev->driver) {
@@ -2282,7 +2377,12 @@
r = -ENODEV;
}
- fbdev->displays[fbdev->num_displays++] = dssdev;
+ d = &fbdev->displays[fbdev->num_displays++];
+ d->dssdev = dssdev;
+ if (dssdev->caps & OMAP_DSS_DISPLAY_CAP_MANUAL_UPDATE)
+ d->update_mode = OMAPFB_MANUAL_UPDATE;
+ else
+ d->update_mode = OMAPFB_AUTO_UPDATE;
}
if (r)
diff --git a/drivers/video/omap2/omapfb/omapfb-sysfs.c b/drivers/video/omap2/omapfb/omapfb-sysfs.c
index 2f5e817..153bf1a 100644
--- a/drivers/video/omap2/omapfb/omapfb-sysfs.c
+++ b/drivers/video/omap2/omapfb/omapfb-sysfs.c
@@ -518,6 +518,39 @@
return snprintf(buf, PAGE_SIZE, "%p\n", ofbi->region->vaddr);
}
+static ssize_t show_upd_mode(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct fb_info *fbi = dev_get_drvdata(dev);
+ enum omapfb_update_mode mode;
+ int r;
+
+ r = omapfb_get_update_mode(fbi, &mode);
+
+ if (r)
+ return r;
+
+ return snprintf(buf, PAGE_SIZE, "%u\n", (unsigned)mode);
+}
+
+static ssize_t store_upd_mode(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct fb_info *fbi = dev_get_drvdata(dev);
+ unsigned mode;
+ int r;
+
+ r = kstrtouint(buf, 0, &mode);
+ if (r)
+ return r;
+
+ r = omapfb_set_update_mode(fbi, mode);
+ if (r)
+ return r;
+
+ return count;
+}
+
static struct device_attribute omapfb_attrs[] = {
__ATTR(rotate_type, S_IRUGO | S_IWUSR, show_rotate_type,
store_rotate_type),
@@ -528,6 +561,7 @@
store_overlays_rotate),
__ATTR(phys_addr, S_IRUGO, show_phys, NULL),
__ATTR(virt_addr, S_IRUGO, show_virt, NULL),
+ __ATTR(update_mode, S_IRUGO | S_IWUSR, show_upd_mode, store_upd_mode),
};
int omapfb_create_sysfs(struct omapfb2_device *fbdev)
diff --git a/drivers/video/omap2/omapfb/omapfb.h b/drivers/video/omap2/omapfb/omapfb.h
index aa1b1d9..fdf0ede 100644
--- a/drivers/video/omap2/omapfb/omapfb.h
+++ b/drivers/video/omap2/omapfb/omapfb.h
@@ -73,6 +73,15 @@
bool mirror;
};
+struct omapfb_display_data {
+ struct omapfb2_device *fbdev;
+ struct omap_dss_device *dssdev;
+ u8 bpp_override;
+ enum omapfb_update_mode update_mode;
+ bool auto_update_work_enabled;
+ struct delayed_work auto_update_work;
+};
+
struct omapfb2_device {
struct device *dev;
struct mutex mtx;
@@ -86,17 +95,13 @@
struct omapfb2_mem_region regions[10];
unsigned num_displays;
- struct omap_dss_device *displays[10];
+ struct omapfb_display_data displays[10];
unsigned num_overlays;
struct omap_overlay *overlays[10];
unsigned num_managers;
struct omap_overlay_manager *managers[10];
- unsigned num_bpp_overrides;
- struct {
- struct omap_dss_device *dssdev;
- u8 bpp;
- } bpp_overrides[10];
+ struct workqueue_struct *auto_update_wq;
};
struct omapfb_colormode {
@@ -128,6 +133,13 @@
int omapfb_setup_overlay(struct fb_info *fbi, struct omap_overlay *ovl,
u16 posx, u16 posy, u16 outw, u16 outh);
+void omapfb_start_auto_update(struct omapfb2_device *fbdev,
+ struct omap_dss_device *display);
+void omapfb_stop_auto_update(struct omapfb2_device *fbdev,
+ struct omap_dss_device *display);
+int omapfb_get_update_mode(struct fb_info *fbi, enum omapfb_update_mode *mode);
+int omapfb_set_update_mode(struct fb_info *fbi, enum omapfb_update_mode mode);
+
/* find the display connected to this fb, if any */
static inline struct omap_dss_device *fb2display(struct fb_info *fbi)
{
@@ -143,6 +155,19 @@
return NULL;
}
+static inline struct omapfb_display_data *get_display_data(
+ struct omapfb2_device *fbdev, struct omap_dss_device *dssdev)
+{
+ int i;
+
+ for (i = 0; i < fbdev->num_displays; ++i)
+ if (fbdev->displays[i].dssdev == dssdev)
+ return &fbdev->displays[i];
+
+ /* This should never happen */
+ BUG();
+}
+
static inline void omapfb_lock(struct omapfb2_device *fbdev)
{
mutex_lock(&fbdev->mtx);
diff --git a/drivers/video/savage/savagefb.h b/drivers/video/savage/savagefb.h
index 32549d1..dcaab90 100644
--- a/drivers/video/savage/savagefb.h
+++ b/drivers/video/savage/savagefb.h
@@ -55,7 +55,7 @@
#define S3_SAVAGE3D_SERIES(chip) ((chip>=S3_SAVAGE3D) && (chip<=S3_SAVAGE_MX))
-#define S3_SAVAGE4_SERIES(chip) ((chip>=S3_SAVAGE4) || (chip<=S3_PROSAVAGEDDR))
+#define S3_SAVAGE4_SERIES(chip) ((chip>=S3_SAVAGE4) && (chip<=S3_PROSAVAGEDDR))
#define S3_SAVAGE_MOBILE_SERIES(chip) ((chip==S3_SAVAGE_MX) || (chip==S3_SUPERSAVAGE))
diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig
index f441726..86b0735 100644
--- a/drivers/watchdog/Kconfig
+++ b/drivers/watchdog/Kconfig
@@ -36,9 +36,6 @@
and gives them the /dev/watchdog interface (and later also the
sysfs interface).
- To compile this driver as a module, choose M here: the module will
- be called watchdog.
-
config WATCHDOG_NOWAYOUT
bool "Disable watchdog shutdown on close"
help
diff --git a/drivers/watchdog/nv_tco.c b/drivers/watchdog/nv_tco.c
index afa78a5..809f41c 100644
--- a/drivers/watchdog/nv_tco.c
+++ b/drivers/watchdog/nv_tco.c
@@ -458,7 +458,15 @@
static void nv_tco_shutdown(struct platform_device *dev)
{
+ u32 val;
+
tco_timer_stop();
+
+ /* Some BIOSes fail the POST (once) if the NO_REBOOT flag is not
+ * unset during shutdown. */
+ 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);
}
static struct platform_driver nv_tco_driver = {
diff --git a/drivers/watchdog/shwdt.c b/drivers/watchdog/shwdt.c
index db84f23..a267dc0 100644
--- a/drivers/watchdog/shwdt.c
+++ b/drivers/watchdog/shwdt.c
@@ -64,7 +64,7 @@
* misses its deadline, the kernel timer will allow the WDT to overflow.
*/
static int clock_division_ratio = WTCSR_CKS_4096;
-#define next_ping_period(cks) msecs_to_jiffies(cks - 4)
+#define next_ping_period(cks) (jiffies + msecs_to_jiffies(cks - 4))
static const struct watchdog_info sh_wdt_info;
static struct platform_device *sh_wdt_dev;
diff --git a/drivers/xen/Kconfig b/drivers/xen/Kconfig
index f815283..5f7ff8e 100644
--- a/drivers/xen/Kconfig
+++ b/drivers/xen/Kconfig
@@ -11,7 +11,7 @@
config XEN_SELFBALLOONING
bool "Dynamically self-balloon kernel memory to target"
- depends on XEN && XEN_BALLOON && CLEANCACHE && SWAP
+ depends on XEN && XEN_BALLOON && CLEANCACHE && SWAP && XEN_TMEM
default n
help
Self-ballooning dynamically balloons available kernel memory driven
diff --git a/drivers/xen/xen-selfballoon.c b/drivers/xen/xen-selfballoon.c
index 1b4afd8..6ea852e 100644
--- a/drivers/xen/xen-selfballoon.c
+++ b/drivers/xen/xen-selfballoon.c
@@ -70,6 +70,7 @@
#include <linux/kernel.h>
#include <linux/mm.h>
#include <linux/mman.h>
+#include <linux/module.h>
#include <linux/workqueue.h>
#include <xen/balloon.h>
#include <xen/tmem.h>
diff --git a/fs/9p/acl.c b/fs/9p/acl.c
index e9cb57f..9a1d426 100644
--- a/fs/9p/acl.c
+++ b/fs/9p/acl.c
@@ -182,11 +182,11 @@
return 0;
}
-int v9fs_acl_mode(struct inode *dir, mode_t *modep,
+int v9fs_acl_mode(struct inode *dir, umode_t *modep,
struct posix_acl **dpacl, struct posix_acl **pacl)
{
int retval = 0;
- mode_t mode = *modep;
+ umode_t mode = *modep;
struct posix_acl *acl = NULL;
if (!S_ISLNK(mode)) {
@@ -319,7 +319,7 @@
case ACL_TYPE_ACCESS:
name = POSIX_ACL_XATTR_ACCESS;
if (acl) {
- mode_t mode = inode->i_mode;
+ umode_t mode = inode->i_mode;
retval = posix_acl_equiv_mode(acl, &mode);
if (retval < 0)
goto err_out;
diff --git a/fs/9p/acl.h b/fs/9p/acl.h
index ddb7ae1..5595564 100644
--- a/fs/9p/acl.h
+++ b/fs/9p/acl.h
@@ -20,7 +20,7 @@
extern int v9fs_acl_chmod(struct dentry *);
extern int v9fs_set_create_acl(struct dentry *,
struct posix_acl **, struct posix_acl **);
-extern int v9fs_acl_mode(struct inode *dir, mode_t *modep,
+extern int v9fs_acl_mode(struct inode *dir, umode_t *modep,
struct posix_acl **dpacl, struct posix_acl **pacl);
#else
#define v9fs_iop_get_acl NULL
@@ -38,7 +38,7 @@
{
return 0;
}
-static inline int v9fs_acl_mode(struct inode *dir, mode_t *modep,
+static inline int v9fs_acl_mode(struct inode *dir, umode_t *modep,
struct posix_acl **dpacl,
struct posix_acl **pacl)
{
diff --git a/fs/9p/vfs_inode_dotl.c b/fs/9p/vfs_inode_dotl.c
index 9a26dce..b6c8ed2 100644
--- a/fs/9p/vfs_inode_dotl.c
+++ b/fs/9p/vfs_inode_dotl.c
@@ -206,7 +206,7 @@
int err = 0;
gid_t gid;
int flags;
- mode_t mode;
+ umode_t mode;
char *name = NULL;
struct file *filp;
struct p9_qid qid;
@@ -348,7 +348,7 @@
struct p9_fid *fid = NULL, *dfid = NULL;
gid_t gid;
char *name;
- mode_t mode;
+ umode_t mode;
struct inode *inode;
struct p9_qid qid;
struct dentry *dir_dentry;
@@ -751,7 +751,7 @@
int err;
gid_t gid;
char *name;
- mode_t mode;
+ umode_t mode;
struct v9fs_session_info *v9ses;
struct p9_fid *fid = NULL, *dfid = NULL;
struct inode *inode;
diff --git a/fs/Kconfig b/fs/Kconfig
index 19891aa..9fe0b34 100644
--- a/fs/Kconfig
+++ b/fs/Kconfig
@@ -127,14 +127,21 @@
select TMPFS_XATTR
select GENERIC_ACL
help
- POSIX Access Control Lists (ACLs) support permissions for users and
- groups beyond the owner/group/world scheme.
+ POSIX Access Control Lists (ACLs) support additional access rights
+ for users and groups beyond the standard owner/group/world scheme,
+ and this option selects support for ACLs specifically for tmpfs
+ filesystems.
+
+ If you've selected TMPFS, it's possible that you'll also need
+ this option as there are a number of Linux distros that require
+ POSIX ACL support under /dev for certain features to work properly.
+ For example, some distros need this feature for ALSA-related /dev
+ files for sound to work properly. In short, if you're not sure,
+ say Y.
To learn more about Access Control Lists, visit the POSIX ACLs for
Linux website <http://acl.bestbits.at/>.
- If you don't know what Access Control Lists are, say N.
-
config TMPFS_XATTR
bool "Tmpfs extended attributes"
depends on TMPFS
diff --git a/fs/autofs4/autofs_i.h b/fs/autofs4/autofs_i.h
index 475f9c5..326dc08 100644
--- a/fs/autofs4/autofs_i.h
+++ b/fs/autofs4/autofs_i.h
@@ -39,27 +39,17 @@
/* #define DEBUG */
-#ifdef DEBUG
-#define DPRINTK(fmt, args...) \
-do { \
- printk(KERN_DEBUG "pid %d: %s: " fmt "\n", \
- current->pid, __func__, ##args); \
-} while (0)
-#else
-#define DPRINTK(fmt, args...) do {} while (0)
-#endif
+#define DPRINTK(fmt, ...) \
+ pr_debug("pid %d: %s: " fmt "\n", \
+ current->pid, __func__, ##__VA_ARGS__)
-#define AUTOFS_WARN(fmt, args...) \
-do { \
+#define AUTOFS_WARN(fmt, ...) \
printk(KERN_WARNING "pid %d: %s: " fmt "\n", \
- current->pid, __func__, ##args); \
-} while (0)
+ current->pid, __func__, ##__VA_ARGS__)
-#define AUTOFS_ERROR(fmt, args...) \
-do { \
+#define AUTOFS_ERROR(fmt, ...) \
printk(KERN_ERR "pid %d: %s: " fmt "\n", \
- current->pid, __func__, ##args); \
-} while (0)
+ current->pid, __func__, ##__VA_ARGS__)
/* Unified info structure. This is pointed to by both the dentry and
inode structures. Each file in the filesystem has an instance of this
diff --git a/fs/autofs4/waitq.c b/fs/autofs4/waitq.c
index 2543598..e1fbdee 100644
--- a/fs/autofs4/waitq.c
+++ b/fs/autofs4/waitq.c
@@ -104,7 +104,7 @@
size_t pktsz;
DPRINTK("wait id = 0x%08lx, name = %.*s, type=%d",
- wq->wait_queue_token, wq->name.len, wq->name.name, type);
+ (unsigned long) wq->wait_queue_token, wq->name.len, wq->name.name, type);
memset(&pkt,0,sizeof pkt); /* For security reasons */
diff --git a/fs/befs/linuxvfs.c b/fs/befs/linuxvfs.c
index 54b8c28..720d885 100644
--- a/fs/befs/linuxvfs.c
+++ b/fs/befs/linuxvfs.c
@@ -474,17 +474,22 @@
befs_data_stream *data = &befs_ino->i_data.ds;
befs_off_t len = data->size;
- befs_debug(sb, "Follow long symlink");
-
- link = kmalloc(len, GFP_NOFS);
- if (!link) {
- link = ERR_PTR(-ENOMEM);
- } else if (befs_read_lsymlink(sb, data, link, len) != len) {
- kfree(link);
- befs_error(sb, "Failed to read entire long symlink");
+ if (len == 0) {
+ befs_error(sb, "Long symlink with illegal length");
link = ERR_PTR(-EIO);
} else {
- link[len - 1] = '\0';
+ befs_debug(sb, "Follow long symlink");
+
+ link = kmalloc(len, GFP_NOFS);
+ if (!link) {
+ link = ERR_PTR(-ENOMEM);
+ } else if (befs_read_lsymlink(sb, data, link, len) != len) {
+ kfree(link);
+ befs_error(sb, "Failed to read entire long symlink");
+ link = ERR_PTR(-EIO);
+ } else {
+ link[len - 1] = '\0';
+ }
}
} else {
link = befs_ino->i_data.symlink;
diff --git a/fs/block_dev.c b/fs/block_dev.c
index f55aad4..ff77262 100644
--- a/fs/block_dev.c
+++ b/fs/block_dev.c
@@ -387,6 +387,10 @@
struct inode *bd_inode = filp->f_mapping->host;
struct block_device *bdev = I_BDEV(bd_inode);
int error;
+
+ error = filemap_write_and_wait_range(filp->f_mapping, start, end);
+ if (error)
+ return error;
/*
* There is no need to serialise calls to blkdev_issue_flush with
@@ -552,6 +556,7 @@
if (inode->i_state & I_NEW) {
bdev->bd_contains = NULL;
+ bdev->bd_super = NULL;
bdev->bd_inode = inode;
bdev->bd_block_size = (1 << inode->i_blkbits);
bdev->bd_part_count = 0;
diff --git a/fs/btrfs/Makefile b/fs/btrfs/Makefile
index 9b72dcf..40e6ac0 100644
--- a/fs/btrfs/Makefile
+++ b/fs/btrfs/Makefile
@@ -6,5 +6,7 @@
transaction.o inode.o file.o tree-defrag.o \
extent_map.o sysfs.o struct-funcs.o xattr.o ordered-data.o \
extent_io.o volumes.o async-thread.o ioctl.o locking.o orphan.o \
- export.o tree-log.o acl.o free-space-cache.o zlib.o lzo.o \
+ export.o tree-log.o free-space-cache.o zlib.o lzo.o \
compression.o delayed-ref.o relocation.o delayed-inode.o scrub.o
+
+btrfs-$(CONFIG_BTRFS_FS_POSIX_ACL) += acl.o
diff --git a/fs/btrfs/acl.c b/fs/btrfs/acl.c
index 65a735d..eb159aa 100644
--- a/fs/btrfs/acl.c
+++ b/fs/btrfs/acl.c
@@ -28,8 +28,6 @@
#include "btrfs_inode.h"
#include "xattr.h"
-#ifdef CONFIG_BTRFS_FS_POSIX_ACL
-
struct posix_acl *btrfs_get_acl(struct inode *inode, int type)
{
int size;
@@ -111,7 +109,6 @@
int ret, size = 0;
const char *name;
char *value = NULL;
- mode_t mode;
if (acl) {
ret = posix_acl_valid(acl);
@@ -122,13 +119,11 @@
switch (type) {
case ACL_TYPE_ACCESS:
- mode = inode->i_mode;
name = POSIX_ACL_XATTR_ACCESS;
if (acl) {
- ret = posix_acl_equiv_mode(acl, &mode);
+ ret = posix_acl_equiv_mode(acl, &inode->i_mode);
if (ret < 0)
return ret;
- inode->i_mode = mode;
}
ret = 0;
break;
@@ -222,19 +217,16 @@
}
if (IS_POSIXACL(dir) && acl) {
- mode_t mode = inode->i_mode;
-
if (S_ISDIR(inode->i_mode)) {
ret = btrfs_set_acl(trans, inode, acl,
ACL_TYPE_DEFAULT);
if (ret)
goto failed;
}
- ret = posix_acl_create(&acl, GFP_NOFS, &mode);
+ ret = posix_acl_create(&acl, GFP_NOFS, &inode->i_mode);
if (ret < 0)
return ret;
- inode->i_mode = mode;
if (ret > 0) {
/* we need an acl */
ret = btrfs_set_acl(trans, inode, acl, ACL_TYPE_ACCESS);
@@ -282,18 +274,3 @@
.get = btrfs_xattr_acl_get,
.set = btrfs_xattr_acl_set,
};
-
-#else /* CONFIG_BTRFS_FS_POSIX_ACL */
-
-int btrfs_acl_chmod(struct inode *inode)
-{
- return 0;
-}
-
-int btrfs_init_acl(struct btrfs_trans_handle *trans,
- struct inode *inode, struct inode *dir)
-{
- return 0;
-}
-
-#endif /* CONFIG_BTRFS_FS_POSIX_ACL */
diff --git a/fs/btrfs/compression.c b/fs/btrfs/compression.c
index bfe42b0..8ec5d86 100644
--- a/fs/btrfs/compression.c
+++ b/fs/btrfs/compression.c
@@ -338,6 +338,7 @@
u64 first_byte = disk_start;
struct block_device *bdev;
int ret;
+ int skip_sum = BTRFS_I(inode)->flags & BTRFS_INODE_NODATASUM;
WARN_ON(start & ((u64)PAGE_CACHE_SIZE - 1));
cb = kmalloc(compressed_bio_size(root, compressed_len), GFP_NOFS);
@@ -392,8 +393,11 @@
ret = btrfs_bio_wq_end_io(root->fs_info, bio, 0);
BUG_ON(ret);
- ret = btrfs_csum_one_bio(root, inode, bio, start, 1);
- BUG_ON(ret);
+ if (!skip_sum) {
+ ret = btrfs_csum_one_bio(root, inode, bio,
+ start, 1);
+ BUG_ON(ret);
+ }
ret = btrfs_map_bio(root, WRITE, bio, 0, 1);
BUG_ON(ret);
@@ -418,8 +422,10 @@
ret = btrfs_bio_wq_end_io(root->fs_info, bio, 0);
BUG_ON(ret);
- ret = btrfs_csum_one_bio(root, inode, bio, start, 1);
- BUG_ON(ret);
+ if (!skip_sum) {
+ ret = btrfs_csum_one_bio(root, inode, bio, start, 1);
+ BUG_ON(ret);
+ }
ret = btrfs_map_bio(root, WRITE, bio, 0, 1);
BUG_ON(ret);
diff --git a/fs/btrfs/ctree.h b/fs/btrfs/ctree.h
index 365c4e1..03912c5 100644
--- a/fs/btrfs/ctree.h
+++ b/fs/btrfs/ctree.h
@@ -1415,17 +1415,15 @@
#define BTRFS_SETGET_HEADER_FUNCS(name, type, member, bits) \
static inline u##bits btrfs_##name(struct extent_buffer *eb) \
{ \
- type *p = kmap_atomic(eb->first_page, KM_USER0); \
+ type *p = page_address(eb->first_page); \
u##bits res = le##bits##_to_cpu(p->member); \
- kunmap_atomic(p, KM_USER0); \
return res; \
} \
static inline void btrfs_set_##name(struct extent_buffer *eb, \
u##bits val) \
{ \
- type *p = kmap_atomic(eb->first_page, KM_USER0); \
+ type *p = page_address(eb->first_page); \
p->member = cpu_to_le##bits(val); \
- kunmap_atomic(p, KM_USER0); \
}
#define BTRFS_SETGET_STACK_FUNCS(name, type, member, bits) \
@@ -2367,8 +2365,8 @@
int btrfs_next_leaf(struct btrfs_root *root, struct btrfs_path *path);
int btrfs_prev_leaf(struct btrfs_root *root, struct btrfs_path *path);
int btrfs_leaf_free_space(struct btrfs_root *root, struct extent_buffer *leaf);
-int btrfs_drop_snapshot(struct btrfs_root *root,
- struct btrfs_block_rsv *block_rsv, int update_ref);
+void btrfs_drop_snapshot(struct btrfs_root *root,
+ struct btrfs_block_rsv *block_rsv, int update_ref);
int btrfs_drop_subtree(struct btrfs_trans_handle *trans,
struct btrfs_root *root,
struct extent_buffer *node,
@@ -2406,8 +2404,8 @@
btrfs_root_item *item, struct btrfs_key *key);
int btrfs_find_dead_roots(struct btrfs_root *root, u64 objectid);
int btrfs_find_orphan_roots(struct btrfs_root *tree_root);
-int btrfs_set_root_node(struct btrfs_root_item *item,
- struct extent_buffer *node);
+void btrfs_set_root_node(struct btrfs_root_item *item,
+ struct extent_buffer *node);
void btrfs_check_and_init_root_item(struct btrfs_root_item *item);
/* dir-item.c */
@@ -2523,6 +2521,14 @@
#define PageChecked PageFsMisc
#endif
+/* This forces readahead on a given range of bytes in an inode */
+static inline void btrfs_force_ra(struct address_space *mapping,
+ struct file_ra_state *ra, struct file *file,
+ pgoff_t offset, unsigned long req_size)
+{
+ page_cache_sync_readahead(mapping, ra, file, offset, req_size);
+}
+
struct inode *btrfs_lookup_dentry(struct inode *dir, struct dentry *dentry);
int btrfs_set_inode_index(struct inode *dir, u64 *index);
int btrfs_unlink_inode(struct btrfs_trans_handle *trans,
@@ -2551,9 +2557,6 @@
int btrfs_merge_bio_hook(struct page *page, unsigned long offset,
size_t size, struct bio *bio, unsigned long bio_flags);
-unsigned long btrfs_force_ra(struct address_space *mapping,
- struct file_ra_state *ra, struct file *file,
- pgoff_t offset, pgoff_t last_index);
int btrfs_page_mkwrite(struct vm_area_struct *vma, struct vm_fault *vmf);
int btrfs_readpage(struct file *file, struct page *page);
void btrfs_evict_inode(struct inode *inode);
@@ -2648,12 +2651,21 @@
/* acl.c */
#ifdef CONFIG_BTRFS_FS_POSIX_ACL
struct posix_acl *btrfs_get_acl(struct inode *inode, int type);
-#else
-#define btrfs_get_acl NULL
-#endif
int btrfs_init_acl(struct btrfs_trans_handle *trans,
struct inode *inode, struct inode *dir);
int btrfs_acl_chmod(struct inode *inode);
+#else
+#define btrfs_get_acl NULL
+static inline int btrfs_init_acl(struct btrfs_trans_handle *trans,
+ struct inode *inode, struct inode *dir)
+{
+ return 0;
+}
+static inline int btrfs_acl_chmod(struct inode *inode)
+{
+ return 0;
+}
+#endif
/* relocation.c */
int btrfs_relocate_block_group(struct btrfs_root *root, u64 group_start);
diff --git a/fs/btrfs/dir-item.c b/fs/btrfs/dir-item.c
index c360a84..31d84e7 100644
--- a/fs/btrfs/dir-item.c
+++ b/fs/btrfs/dir-item.c
@@ -198,8 +198,6 @@
struct btrfs_key key;
int ins_len = mod < 0 ? -1 : 0;
int cow = mod != 0;
- struct btrfs_key found_key;
- struct extent_buffer *leaf;
key.objectid = dir;
btrfs_set_key_type(&key, BTRFS_DIR_ITEM_KEY);
@@ -209,18 +207,7 @@
ret = btrfs_search_slot(trans, root, &key, path, ins_len, cow);
if (ret < 0)
return ERR_PTR(ret);
- if (ret > 0) {
- if (path->slots[0] == 0)
- return NULL;
- path->slots[0]--;
- }
-
- leaf = path->nodes[0];
- btrfs_item_key_to_cpu(leaf, &found_key, path->slots[0]);
-
- if (found_key.objectid != dir ||
- btrfs_key_type(&found_key) != BTRFS_DIR_ITEM_KEY ||
- found_key.offset != key.offset)
+ if (ret > 0)
return NULL;
return btrfs_match_dir_item_name(root, path, name, name_len);
@@ -315,8 +302,6 @@
struct btrfs_key key;
int ins_len = mod < 0 ? -1 : 0;
int cow = mod != 0;
- struct btrfs_key found_key;
- struct extent_buffer *leaf;
key.objectid = dir;
btrfs_set_key_type(&key, BTRFS_XATTR_ITEM_KEY);
@@ -324,18 +309,7 @@
ret = btrfs_search_slot(trans, root, &key, path, ins_len, cow);
if (ret < 0)
return ERR_PTR(ret);
- if (ret > 0) {
- if (path->slots[0] == 0)
- return NULL;
- path->slots[0]--;
- }
-
- leaf = path->nodes[0];
- btrfs_item_key_to_cpu(leaf, &found_key, path->slots[0]);
-
- if (found_key.objectid != dir ||
- btrfs_key_type(&found_key) != BTRFS_XATTR_ITEM_KEY ||
- found_key.offset != key.offset)
+ if (ret > 0)
return NULL;
return btrfs_match_dir_item_name(root, path, name, name_len);
diff --git a/fs/btrfs/extent-tree.c b/fs/btrfs/extent-tree.c
index 4d08ed7..80d6148 100644
--- a/fs/btrfs/extent-tree.c
+++ b/fs/btrfs/extent-tree.c
@@ -663,7 +663,9 @@
struct btrfs_path *path;
path = btrfs_alloc_path();
- BUG_ON(!path);
+ if (!path)
+ return -ENOMEM;
+
key.objectid = start;
key.offset = len;
btrfs_set_key_type(&key, BTRFS_EXTENT_ITEM_KEY);
@@ -1780,6 +1782,9 @@
for (i = 0; i < multi->num_stripes; i++, stripe++) {
+ if (!stripe->dev->can_discard)
+ continue;
+
ret = btrfs_issue_discard(stripe->dev->bdev,
stripe->physical,
stripe->length);
@@ -1787,11 +1792,16 @@
discarded_bytes += stripe->length;
else if (ret != -EOPNOTSUPP)
break;
+
+ /*
+ * Just in case we get back EOPNOTSUPP for some reason,
+ * just ignore the return value so we don't screw up
+ * people calling discard_extent.
+ */
+ ret = 0;
}
kfree(multi);
}
- if (discarded_bytes && ret == -EOPNOTSUPP)
- ret = 0;
if (actual_bytes)
*actual_bytes = discarded_bytes;
@@ -3272,6 +3282,9 @@
}
ret = btrfs_alloc_chunk(trans, extent_root, flags);
+ if (ret < 0 && ret != -ENOSPC)
+ goto out;
+
spin_lock(&space_info->lock);
if (ret)
space_info->full = 1;
@@ -3281,6 +3294,7 @@
space_info->force_alloc = CHUNK_ALLOC_NO_FORCE;
space_info->chunk_alloc = 0;
spin_unlock(&space_info->lock);
+out:
mutex_unlock(&extent_root->fs_info->chunk_mutex);
return ret;
}
@@ -4456,7 +4470,9 @@
printk(KERN_ERR "umm, got %d back from search"
", was looking for %llu\n", ret,
(unsigned long long)bytenr);
- btrfs_print_leaf(extent_root, path->nodes[0]);
+ if (ret > 0)
+ btrfs_print_leaf(extent_root,
+ path->nodes[0]);
}
BUG_ON(ret);
extent_slot = path->slots[0];
@@ -5073,7 +5089,9 @@
* group is does point to and try again
*/
if (!last_ptr_loop && last_ptr->block_group &&
- last_ptr->block_group != block_group) {
+ last_ptr->block_group != block_group &&
+ index <=
+ get_block_group_index(last_ptr->block_group)) {
btrfs_put_block_group(block_group);
block_group = last_ptr->block_group;
@@ -5501,7 +5519,8 @@
u32 size = sizeof(*extent_item) + sizeof(*block_info) + sizeof(*iref);
path = btrfs_alloc_path();
- BUG_ON(!path);
+ if (!path)
+ return -ENOMEM;
path->leave_spinning = 1;
ret = btrfs_insert_empty_item(trans, fs_info->extent_root, path,
@@ -6258,8 +6277,8 @@
* also make sure backrefs for the shared block and all lower level
* blocks are properly updated.
*/
-int btrfs_drop_snapshot(struct btrfs_root *root,
- struct btrfs_block_rsv *block_rsv, int update_ref)
+void btrfs_drop_snapshot(struct btrfs_root *root,
+ struct btrfs_block_rsv *block_rsv, int update_ref)
{
struct btrfs_path *path;
struct btrfs_trans_handle *trans;
@@ -6272,10 +6291,17 @@
int level;
path = btrfs_alloc_path();
- BUG_ON(!path);
+ if (!path) {
+ err = -ENOMEM;
+ goto out;
+ }
wc = kzalloc(sizeof(*wc), GFP_NOFS);
- BUG_ON(!wc);
+ if (!wc) {
+ btrfs_free_path(path);
+ err = -ENOMEM;
+ goto out;
+ }
trans = btrfs_start_transaction(tree_root, 0);
BUG_ON(IS_ERR(trans));
@@ -6303,7 +6329,7 @@
path->lowest_level = 0;
if (ret < 0) {
err = ret;
- goto out;
+ goto out_free;
}
WARN_ON(ret > 0);
@@ -6410,11 +6436,14 @@
free_extent_buffer(root->commit_root);
kfree(root);
}
-out:
+out_free:
btrfs_end_transaction_throttle(trans, tree_root);
kfree(wc);
btrfs_free_path(path);
- return err;
+out:
+ if (err)
+ btrfs_std_error(root->fs_info, err);
+ return;
}
/*
@@ -6538,8 +6567,6 @@
u64 min_allocable_bytes;
int ret = -ENOSPC;
- if (cache->ro)
- return 0;
/*
* We need some metadata space and system metadata space for
@@ -6555,6 +6582,12 @@
spin_lock(&sinfo->lock);
spin_lock(&cache->lock);
+
+ if (cache->ro) {
+ ret = 0;
+ goto out;
+ }
+
num_bytes = cache->key.offset - cache->reserved - cache->pinned -
cache->bytes_super - btrfs_block_group_used(&cache->item);
@@ -6568,7 +6601,7 @@
cache->ro = 1;
ret = 0;
}
-
+out:
spin_unlock(&cache->lock);
spin_unlock(&sinfo->lock);
return ret;
@@ -6701,6 +6734,10 @@
struct btrfs_space_info *space_info;
struct btrfs_fs_devices *fs_devices = root->fs_info->fs_devices;
struct btrfs_device *device;
+ u64 min_free;
+ int index;
+ int dev_nr = 0;
+ int dev_min = 1;
int full = 0;
int ret = 0;
@@ -6710,8 +6747,10 @@
if (!block_group)
return -1;
+ min_free = btrfs_block_group_used(&block_group->item);
+
/* no bytes used, we're good */
- if (!btrfs_block_group_used(&block_group->item))
+ if (!min_free)
goto out;
space_info = block_group->space_info;
@@ -6727,10 +6766,9 @@
* all of the extents from this block group. If we can, we're good
*/
if ((space_info->total_bytes != block_group->key.offset) &&
- (space_info->bytes_used + space_info->bytes_reserved +
- space_info->bytes_pinned + space_info->bytes_readonly +
- btrfs_block_group_used(&block_group->item) <
- space_info->total_bytes)) {
+ (space_info->bytes_used + space_info->bytes_reserved +
+ space_info->bytes_pinned + space_info->bytes_readonly +
+ min_free < space_info->total_bytes)) {
spin_unlock(&space_info->lock);
goto out;
}
@@ -6747,9 +6785,29 @@
if (full)
goto out;
+ /*
+ * index:
+ * 0: raid10
+ * 1: raid1
+ * 2: dup
+ * 3: raid0
+ * 4: single
+ */
+ index = get_block_group_index(block_group);
+ if (index == 0) {
+ dev_min = 4;
+ min_free /= 2;
+ } else if (index == 1) {
+ dev_min = 2;
+ } else if (index == 2) {
+ min_free *= 2;
+ } else if (index == 3) {
+ dev_min = fs_devices->rw_devices;
+ min_free /= dev_min;
+ }
+
mutex_lock(&root->fs_info->chunk_mutex);
list_for_each_entry(device, &fs_devices->alloc_list, dev_alloc_list) {
- u64 min_free = btrfs_block_group_used(&block_group->item);
u64 dev_offset;
/*
@@ -6760,7 +6818,11 @@
ret = find_free_dev_extent(NULL, device, min_free,
&dev_offset, NULL);
if (!ret)
+ dev_nr++;
+
+ if (dev_nr >= dev_min)
break;
+
ret = -1;
}
}
@@ -7183,11 +7245,15 @@
spin_unlock(&cluster->refill_lock);
path = btrfs_alloc_path();
- BUG_ON(!path);
+ if (!path) {
+ ret = -ENOMEM;
+ goto out;
+ }
inode = lookup_free_space_inode(root, block_group, path);
if (!IS_ERR(inode)) {
- btrfs_orphan_add(trans, inode);
+ ret = btrfs_orphan_add(trans, inode);
+ BUG_ON(ret);
clear_nlink(inode);
/* One for the block groups ref */
spin_lock(&block_group->lock);
diff --git a/fs/btrfs/extent_io.c b/fs/btrfs/extent_io.c
index 067b174..d418164 100644
--- a/fs/btrfs/extent_io.c
+++ b/fs/btrfs/extent_io.c
@@ -254,14 +254,14 @@
*
* This should be called with the tree lock held.
*/
-static int merge_state(struct extent_io_tree *tree,
- struct extent_state *state)
+static void merge_state(struct extent_io_tree *tree,
+ struct extent_state *state)
{
struct extent_state *other;
struct rb_node *other_node;
if (state->state & (EXTENT_IOBITS | EXTENT_BOUNDARY))
- return 0;
+ return;
other_node = rb_prev(&state->rb_node);
if (other_node) {
@@ -287,19 +287,13 @@
free_extent_state(other);
}
}
-
- return 0;
}
-static int set_state_cb(struct extent_io_tree *tree,
+static void set_state_cb(struct extent_io_tree *tree,
struct extent_state *state, int *bits)
{
- if (tree->ops && tree->ops->set_bit_hook) {
- return tree->ops->set_bit_hook(tree->mapping->host,
- state, bits);
- }
-
- return 0;
+ if (tree->ops && tree->ops->set_bit_hook)
+ tree->ops->set_bit_hook(tree->mapping->host, state, bits);
}
static void clear_state_cb(struct extent_io_tree *tree,
@@ -309,6 +303,9 @@
tree->ops->clear_bit_hook(tree->mapping->host, state, bits);
}
+static void set_state_bits(struct extent_io_tree *tree,
+ struct extent_state *state, int *bits);
+
/*
* insert an extent_state struct into the tree. 'bits' are set on the
* struct before it is inserted.
@@ -324,8 +321,6 @@
int *bits)
{
struct rb_node *node;
- int bits_to_set = *bits & ~EXTENT_CTLBITS;
- int ret;
if (end < start) {
printk(KERN_ERR "btrfs end < start %llu %llu\n",
@@ -335,13 +330,9 @@
}
state->start = start;
state->end = end;
- ret = set_state_cb(tree, state, bits);
- if (ret)
- return ret;
- if (bits_to_set & EXTENT_DIRTY)
- tree->dirty_bytes += end - start + 1;
- state->state |= bits_to_set;
+ set_state_bits(tree, state, bits);
+
node = tree_insert(&tree->state, end, &state->rb_node);
if (node) {
struct extent_state *found;
@@ -357,13 +348,11 @@
return 0;
}
-static int split_cb(struct extent_io_tree *tree, struct extent_state *orig,
+static void split_cb(struct extent_io_tree *tree, struct extent_state *orig,
u64 split)
{
if (tree->ops && tree->ops->split_extent_hook)
- return tree->ops->split_extent_hook(tree->mapping->host,
- orig, split);
- return 0;
+ tree->ops->split_extent_hook(tree->mapping->host, orig, split);
}
/*
@@ -659,34 +648,25 @@
if (start > end)
break;
- if (need_resched()) {
- spin_unlock(&tree->lock);
- cond_resched();
- spin_lock(&tree->lock);
- }
+ cond_resched_lock(&tree->lock);
}
out:
spin_unlock(&tree->lock);
return 0;
}
-static int set_state_bits(struct extent_io_tree *tree,
+static void set_state_bits(struct extent_io_tree *tree,
struct extent_state *state,
int *bits)
{
- int ret;
int bits_to_set = *bits & ~EXTENT_CTLBITS;
- ret = set_state_cb(tree, state, bits);
- if (ret)
- return ret;
+ set_state_cb(tree, state, bits);
if ((bits_to_set & EXTENT_DIRTY) && !(state->state & EXTENT_DIRTY)) {
u64 range = state->end - state->start + 1;
tree->dirty_bytes += range;
}
state->state |= bits_to_set;
-
- return 0;
}
static void cache_state(struct extent_state *state,
@@ -779,9 +759,7 @@
goto out;
}
- err = set_state_bits(tree, state, &bits);
- if (err)
- goto out;
+ set_state_bits(tree, state, &bits);
cache_state(state, cached_state);
merge_state(tree, state);
@@ -830,9 +808,7 @@
if (err)
goto out;
if (state->end <= end) {
- err = set_state_bits(tree, state, &bits);
- if (err)
- goto out;
+ set_state_bits(tree, state, &bits);
cache_state(state, cached_state);
merge_state(tree, state);
if (last_end == (u64)-1)
@@ -893,11 +869,7 @@
err = split_state(tree, state, prealloc, end + 1);
BUG_ON(err == -EEXIST);
- err = set_state_bits(tree, prealloc, &bits);
- if (err) {
- prealloc = NULL;
- goto out;
- }
+ set_state_bits(tree, prealloc, &bits);
cache_state(prealloc, cached_state);
merge_state(tree, prealloc);
prealloc = NULL;
@@ -1059,46 +1031,6 @@
return 0;
}
-/*
- * find the first offset in the io tree with 'bits' set. zero is
- * returned if we find something, and *start_ret and *end_ret are
- * set to reflect the state struct that was found.
- *
- * If nothing was found, 1 is returned, < 0 on error
- */
-int find_first_extent_bit(struct extent_io_tree *tree, u64 start,
- u64 *start_ret, u64 *end_ret, int bits)
-{
- struct rb_node *node;
- struct extent_state *state;
- int ret = 1;
-
- spin_lock(&tree->lock);
- /*
- * this search will find all the extents that end after
- * our range starts.
- */
- node = tree_search(tree, start);
- if (!node)
- goto out;
-
- while (1) {
- state = rb_entry(node, struct extent_state, rb_node);
- if (state->end >= start && (state->state & bits)) {
- *start_ret = state->start;
- *end_ret = state->end;
- ret = 0;
- break;
- }
- node = rb_next(node);
- if (!node)
- break;
- }
-out:
- spin_unlock(&tree->lock);
- return ret;
-}
-
/* find the first state struct with 'bits' set after 'start', and
* return it. tree->lock must be held. NULL will returned if
* nothing was found after 'start'
@@ -1131,6 +1063,30 @@
}
/*
+ * find the first offset in the io tree with 'bits' set. zero is
+ * returned if we find something, and *start_ret and *end_ret are
+ * set to reflect the state struct that was found.
+ *
+ * If nothing was found, 1 is returned, < 0 on error
+ */
+int find_first_extent_bit(struct extent_io_tree *tree, u64 start,
+ u64 *start_ret, u64 *end_ret, int bits)
+{
+ struct extent_state *state;
+ int ret = 1;
+
+ spin_lock(&tree->lock);
+ state = find_first_extent_bit_state(tree, start, bits);
+ if (state) {
+ *start_ret = state->start;
+ *end_ret = state->end;
+ ret = 0;
+ }
+ spin_unlock(&tree->lock);
+ return ret;
+}
+
+/*
* find a contiguous range of bytes in the file marked as delalloc, not
* more than 'max_bytes'. start and end are used to return the range,
*
@@ -2546,7 +2502,6 @@
struct writeback_control *wbc)
{
int ret;
- struct address_space *mapping = page->mapping;
struct extent_page_data epd = {
.bio = NULL,
.tree = tree,
@@ -2554,17 +2509,9 @@
.extent_locked = 0,
.sync_io = wbc->sync_mode == WB_SYNC_ALL,
};
- struct writeback_control wbc_writepages = {
- .sync_mode = wbc->sync_mode,
- .nr_to_write = 64,
- .range_start = page_offset(page) + PAGE_CACHE_SIZE,
- .range_end = (loff_t)-1,
- };
ret = __extent_writepage(page, wbc, &epd);
- extent_write_cache_pages(tree, mapping, &wbc_writepages,
- __extent_writepage, &epd, flush_write_bio);
flush_epd_write_bio(&epd);
return ret;
}
diff --git a/fs/btrfs/extent_io.h b/fs/btrfs/extent_io.h
index 21a7ca9..7b2f0c3 100644
--- a/fs/btrfs/extent_io.h
+++ b/fs/btrfs/extent_io.h
@@ -76,15 +76,15 @@
struct extent_state *state);
int (*writepage_end_io_hook)(struct page *page, u64 start, u64 end,
struct extent_state *state, int uptodate);
- int (*set_bit_hook)(struct inode *inode, struct extent_state *state,
- int *bits);
- int (*clear_bit_hook)(struct inode *inode, struct extent_state *state,
- int *bits);
- int (*merge_extent_hook)(struct inode *inode,
- struct extent_state *new,
- struct extent_state *other);
- int (*split_extent_hook)(struct inode *inode,
- struct extent_state *orig, u64 split);
+ void (*set_bit_hook)(struct inode *inode, struct extent_state *state,
+ int *bits);
+ void (*clear_bit_hook)(struct inode *inode, struct extent_state *state,
+ int *bits);
+ void (*merge_extent_hook)(struct inode *inode,
+ struct extent_state *new,
+ struct extent_state *other);
+ void (*split_extent_hook)(struct inode *inode,
+ struct extent_state *orig, u64 split);
int (*write_cache_pages_lock_hook)(struct page *page);
};
@@ -108,8 +108,6 @@
wait_queue_head_t wq;
atomic_t refs;
unsigned long state;
- u64 split_start;
- u64 split_end;
/* for use by the FS */
u64 private;
diff --git a/fs/btrfs/extent_map.c b/fs/btrfs/extent_map.c
index 2d04103..7c97b33 100644
--- a/fs/btrfs/extent_map.c
+++ b/fs/btrfs/extent_map.c
@@ -183,22 +183,10 @@
return 0;
}
-int unpin_extent_cache(struct extent_map_tree *tree, u64 start, u64 len)
+static void try_merge_map(struct extent_map_tree *tree, struct extent_map *em)
{
- int ret = 0;
struct extent_map *merge = NULL;
struct rb_node *rb;
- struct extent_map *em;
-
- write_lock(&tree->lock);
- em = lookup_extent_mapping(tree, start, len);
-
- WARN_ON(!em || em->start != start);
-
- if (!em)
- goto out;
-
- clear_bit(EXTENT_FLAG_PINNED, &em->flags);
if (em->start != 0) {
rb = rb_prev(&em->rb_node);
@@ -225,6 +213,24 @@
merge->in_tree = 0;
free_extent_map(merge);
}
+}
+
+int unpin_extent_cache(struct extent_map_tree *tree, u64 start, u64 len)
+{
+ int ret = 0;
+ struct extent_map *em;
+
+ write_lock(&tree->lock);
+ em = lookup_extent_mapping(tree, start, len);
+
+ WARN_ON(!em || em->start != start);
+
+ if (!em)
+ goto out;
+
+ clear_bit(EXTENT_FLAG_PINNED, &em->flags);
+
+ try_merge_map(tree, em);
free_extent_map(em);
out:
@@ -247,7 +253,6 @@
struct extent_map *em)
{
int ret = 0;
- struct extent_map *merge = NULL;
struct rb_node *rb;
struct extent_map *exist;
@@ -263,30 +268,8 @@
goto out;
}
atomic_inc(&em->refs);
- if (em->start != 0) {
- rb = rb_prev(&em->rb_node);
- if (rb)
- merge = rb_entry(rb, struct extent_map, rb_node);
- if (rb && mergable_maps(merge, em)) {
- em->start = merge->start;
- em->len += merge->len;
- em->block_len += merge->block_len;
- em->block_start = merge->block_start;
- merge->in_tree = 0;
- rb_erase(&merge->rb_node, &tree->map);
- free_extent_map(merge);
- }
- }
- rb = rb_next(&em->rb_node);
- if (rb)
- merge = rb_entry(rb, struct extent_map, rb_node);
- if (rb && mergable_maps(em, merge)) {
- em->len += merge->len;
- em->block_len += merge->len;
- rb_erase(&merge->rb_node, &tree->map);
- merge->in_tree = 0;
- free_extent_map(merge);
- }
+
+ try_merge_map(tree, em);
out:
return ret;
}
@@ -299,6 +282,34 @@
return start + len;
}
+struct extent_map *__lookup_extent_mapping(struct extent_map_tree *tree,
+ u64 start, u64 len, int strict)
+{
+ struct extent_map *em;
+ struct rb_node *rb_node;
+ struct rb_node *prev = NULL;
+ struct rb_node *next = NULL;
+ u64 end = range_end(start, len);
+
+ rb_node = __tree_search(&tree->map, start, &prev, &next);
+ if (!rb_node) {
+ if (prev)
+ rb_node = prev;
+ else if (next)
+ rb_node = next;
+ else
+ return NULL;
+ }
+
+ em = rb_entry(rb_node, struct extent_map, rb_node);
+
+ if (strict && !(end > em->start && start < extent_map_end(em)))
+ return NULL;
+
+ atomic_inc(&em->refs);
+ return em;
+}
+
/**
* lookup_extent_mapping - lookup extent_map
* @tree: tree to lookup in
@@ -313,42 +324,7 @@
struct extent_map *lookup_extent_mapping(struct extent_map_tree *tree,
u64 start, u64 len)
{
- struct extent_map *em;
- struct rb_node *rb_node;
- struct rb_node *prev = NULL;
- struct rb_node *next = NULL;
- u64 end = range_end(start, len);
-
- rb_node = __tree_search(&tree->map, start, &prev, &next);
- if (!rb_node && prev) {
- em = rb_entry(prev, struct extent_map, rb_node);
- if (end > em->start && start < extent_map_end(em))
- goto found;
- }
- if (!rb_node && next) {
- em = rb_entry(next, struct extent_map, rb_node);
- if (end > em->start && start < extent_map_end(em))
- goto found;
- }
- if (!rb_node) {
- em = NULL;
- goto out;
- }
- if (IS_ERR(rb_node)) {
- em = ERR_CAST(rb_node);
- goto out;
- }
- em = rb_entry(rb_node, struct extent_map, rb_node);
- if (end > em->start && start < extent_map_end(em))
- goto found;
-
- em = NULL;
- goto out;
-
-found:
- atomic_inc(&em->refs);
-out:
- return em;
+ return __lookup_extent_mapping(tree, start, len, 1);
}
/**
@@ -365,38 +341,7 @@
struct extent_map *search_extent_mapping(struct extent_map_tree *tree,
u64 start, u64 len)
{
- struct extent_map *em;
- struct rb_node *rb_node;
- struct rb_node *prev = NULL;
- struct rb_node *next = NULL;
-
- rb_node = __tree_search(&tree->map, start, &prev, &next);
- if (!rb_node && prev) {
- em = rb_entry(prev, struct extent_map, rb_node);
- goto found;
- }
- if (!rb_node && next) {
- em = rb_entry(next, struct extent_map, rb_node);
- goto found;
- }
- if (!rb_node) {
- em = NULL;
- goto out;
- }
- if (IS_ERR(rb_node)) {
- em = ERR_CAST(rb_node);
- goto out;
- }
- em = rb_entry(rb_node, struct extent_map, rb_node);
- goto found;
-
- em = NULL;
- goto out;
-
-found:
- atomic_inc(&em->refs);
-out:
- return em;
+ return __lookup_extent_mapping(tree, start, len, 0);
}
/**
diff --git a/fs/btrfs/file-item.c b/fs/btrfs/file-item.c
index 08bcfa9..b910694 100644
--- a/fs/btrfs/file-item.c
+++ b/fs/btrfs/file-item.c
@@ -291,7 +291,8 @@
u16 csum_size = btrfs_super_csum_size(&root->fs_info->super_copy);
path = btrfs_alloc_path();
- BUG_ON(!path);
+ if (!path)
+ return -ENOMEM;
if (search_commit) {
path->skip_locking = 1;
@@ -677,7 +678,9 @@
btrfs_super_csum_size(&root->fs_info->super_copy);
path = btrfs_alloc_path();
- BUG_ON(!path);
+ if (!path)
+ return -ENOMEM;
+
sector_sum = sums->sums;
again:
next_offset = (u64)-1;
diff --git a/fs/btrfs/file.c b/fs/btrfs/file.c
index a35e51c..e7872e4 100644
--- a/fs/btrfs/file.c
+++ b/fs/btrfs/file.c
@@ -74,7 +74,7 @@
* If an existing record is found the defrag item you
* pass in is freed
*/
-static int __btrfs_add_inode_defrag(struct inode *inode,
+static void __btrfs_add_inode_defrag(struct inode *inode,
struct inode_defrag *defrag)
{
struct btrfs_root *root = BTRFS_I(inode)->root;
@@ -106,11 +106,11 @@
BTRFS_I(inode)->in_defrag = 1;
rb_link_node(&defrag->rb_node, parent, p);
rb_insert_color(&defrag->rb_node, &root->fs_info->defrag_inodes);
- return 0;
+ return;
exists:
kfree(defrag);
- return 0;
+ return;
}
@@ -123,7 +123,6 @@
{
struct btrfs_root *root = BTRFS_I(inode)->root;
struct inode_defrag *defrag;
- int ret = 0;
u64 transid;
if (!btrfs_test_opt(root, AUTO_DEFRAG))
@@ -150,9 +149,11 @@
spin_lock(&root->fs_info->defrag_inodes_lock);
if (!BTRFS_I(inode)->in_defrag)
- ret = __btrfs_add_inode_defrag(inode, defrag);
+ __btrfs_add_inode_defrag(inode, defrag);
+ else
+ kfree(defrag);
spin_unlock(&root->fs_info->defrag_inodes_lock);
- return ret;
+ return 0;
}
/*
@@ -855,7 +856,8 @@
btrfs_drop_extent_cache(inode, start, end - 1, 0);
path = btrfs_alloc_path();
- BUG_ON(!path);
+ if (!path)
+ return -ENOMEM;
again:
recow = 0;
split = start;
@@ -1059,7 +1061,7 @@
static noinline int prepare_pages(struct btrfs_root *root, struct file *file,
struct page **pages, size_t num_pages,
loff_t pos, unsigned long first_index,
- unsigned long last_index, size_t write_bytes)
+ size_t write_bytes)
{
struct extent_state *cached_state = NULL;
int i;
@@ -1159,7 +1161,6 @@
struct btrfs_root *root = BTRFS_I(inode)->root;
struct page **pages = NULL;
unsigned long first_index;
- unsigned long last_index;
size_t num_written = 0;
int nrptrs;
int ret = 0;
@@ -1172,7 +1173,6 @@
return -ENOMEM;
first_index = pos >> PAGE_CACHE_SHIFT;
- last_index = (pos + iov_iter_count(i)) >> PAGE_CACHE_SHIFT;
while (iov_iter_count(i) > 0) {
size_t offset = pos & (PAGE_CACHE_SIZE - 1);
@@ -1206,8 +1206,7 @@
* contents of pages from loop to loop
*/
ret = prepare_pages(root, file, pages, num_pages,
- pos, first_index, last_index,
- write_bytes);
+ pos, first_index, write_bytes);
if (ret) {
btrfs_delalloc_release_space(inode,
num_pages << PAGE_CACHE_SHIFT);
@@ -1641,11 +1640,15 @@
cur_offset = alloc_start;
while (1) {
+ u64 actual_end;
+
em = btrfs_get_extent(inode, NULL, 0, cur_offset,
alloc_end - cur_offset, 0);
BUG_ON(IS_ERR_OR_NULL(em));
last_byte = min(extent_map_end(em), alloc_end);
+ actual_end = min_t(u64, extent_map_end(em), offset + len);
last_byte = (last_byte + mask) & ~mask;
+
if (em->block_start == EXTENT_MAP_HOLE ||
(cur_offset >= inode->i_size &&
!test_bit(EXTENT_FLAG_PREALLOC, &em->flags))) {
@@ -1658,6 +1661,16 @@
free_extent_map(em);
break;
}
+ } else if (actual_end > inode->i_size &&
+ !(mode & FALLOC_FL_KEEP_SIZE)) {
+ /*
+ * We didn't need to allocate any more space, but we
+ * still extended the size of the file so we need to
+ * update i_size.
+ */
+ inode->i_ctime = CURRENT_TIME;
+ i_size_write(inode, actual_end);
+ btrfs_ordered_update_i_size(inode, actual_end, NULL);
}
free_extent_map(em);
@@ -1807,10 +1820,14 @@
}
}
- if (offset < 0 && !(file->f_mode & FMODE_UNSIGNED_OFFSET))
- return -EINVAL;
- if (offset > inode->i_sb->s_maxbytes)
- return -EINVAL;
+ if (offset < 0 && !(file->f_mode & FMODE_UNSIGNED_OFFSET)) {
+ ret = -EINVAL;
+ goto out;
+ }
+ if (offset > inode->i_sb->s_maxbytes) {
+ ret = -EINVAL;
+ goto out;
+ }
/* Special lock needed here? */
if (offset != file->f_pos) {
diff --git a/fs/btrfs/free-space-cache.c b/fs/btrfs/free-space-cache.c
index 6377713..6a265b9 100644
--- a/fs/btrfs/free-space-cache.c
+++ b/fs/btrfs/free-space-cache.c
@@ -1168,9 +1168,9 @@
div64_u64(extent_bytes, (sizeof(struct btrfs_free_space)));
}
-static void bitmap_clear_bits(struct btrfs_free_space_ctl *ctl,
- struct btrfs_free_space *info, u64 offset,
- u64 bytes)
+static inline void __bitmap_clear_bits(struct btrfs_free_space_ctl *ctl,
+ struct btrfs_free_space *info,
+ u64 offset, u64 bytes)
{
unsigned long start, count;
@@ -1181,6 +1181,13 @@
bitmap_clear(info->bitmap, start, count);
info->bytes -= bytes;
+}
+
+static void bitmap_clear_bits(struct btrfs_free_space_ctl *ctl,
+ struct btrfs_free_space *info, u64 offset,
+ u64 bytes)
+{
+ __bitmap_clear_bits(ctl, info, offset, bytes);
ctl->free_space -= bytes;
}
@@ -1984,7 +1991,7 @@
return 0;
ret = search_start;
- bitmap_clear_bits(ctl, entry, ret, bytes);
+ __bitmap_clear_bits(ctl, entry, ret, bytes);
return ret;
}
@@ -2039,7 +2046,6 @@
continue;
}
} else {
-
ret = entry->offset;
entry->offset += bytes;
diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c
index 13e6255..0ccc743 100644
--- a/fs/btrfs/inode.c
+++ b/fs/btrfs/inode.c
@@ -1061,7 +1061,8 @@
u64 ino = btrfs_ino(inode);
path = btrfs_alloc_path();
- BUG_ON(!path);
+ if (!path)
+ return -ENOMEM;
nolock = btrfs_is_free_space_inode(root, inode);
@@ -1282,17 +1283,16 @@
return ret;
}
-static int btrfs_split_extent_hook(struct inode *inode,
- struct extent_state *orig, u64 split)
+static void btrfs_split_extent_hook(struct inode *inode,
+ struct extent_state *orig, u64 split)
{
/* not delalloc, ignore it */
if (!(orig->state & EXTENT_DELALLOC))
- return 0;
+ return;
spin_lock(&BTRFS_I(inode)->lock);
BTRFS_I(inode)->outstanding_extents++;
spin_unlock(&BTRFS_I(inode)->lock);
- return 0;
}
/*
@@ -1301,18 +1301,17 @@
* extents, such as when we are doing sequential writes, so we can properly
* account for the metadata space we'll need.
*/
-static int btrfs_merge_extent_hook(struct inode *inode,
- struct extent_state *new,
- struct extent_state *other)
+static void btrfs_merge_extent_hook(struct inode *inode,
+ struct extent_state *new,
+ struct extent_state *other)
{
/* not delalloc, ignore it */
if (!(other->state & EXTENT_DELALLOC))
- return 0;
+ return;
spin_lock(&BTRFS_I(inode)->lock);
BTRFS_I(inode)->outstanding_extents--;
spin_unlock(&BTRFS_I(inode)->lock);
- return 0;
}
/*
@@ -1320,8 +1319,8 @@
* bytes in this file, and to maintain the list of inodes that
* have pending delalloc work to be done.
*/
-static int btrfs_set_bit_hook(struct inode *inode,
- struct extent_state *state, int *bits)
+static void btrfs_set_bit_hook(struct inode *inode,
+ struct extent_state *state, int *bits)
{
/*
@@ -1351,14 +1350,13 @@
}
spin_unlock(&root->fs_info->delalloc_lock);
}
- return 0;
}
/*
* extent_io.c clear_bit_hook, see set_bit_hook for why
*/
-static int btrfs_clear_bit_hook(struct inode *inode,
- struct extent_state *state, int *bits)
+static void btrfs_clear_bit_hook(struct inode *inode,
+ struct extent_state *state, int *bits)
{
/*
* set_bit and clear bit hooks normally require _irqsave/restore
@@ -1395,7 +1393,6 @@
}
spin_unlock(&root->fs_info->delalloc_lock);
}
- return 0;
}
/*
@@ -1645,7 +1642,8 @@
int ret;
path = btrfs_alloc_path();
- BUG_ON(!path);
+ if (!path)
+ return -ENOMEM;
path->leave_spinning = 1;
@@ -2215,7 +2213,8 @@
if (!root->orphan_block_rsv) {
block_rsv = btrfs_alloc_block_rsv(root);
- BUG_ON(!block_rsv);
+ if (!block_rsv)
+ return -ENOMEM;
}
spin_lock(&root->orphan_lock);
@@ -2517,7 +2516,9 @@
filled = true;
path = btrfs_alloc_path();
- BUG_ON(!path);
+ if (!path)
+ goto make_bad;
+
path->leave_spinning = 1;
memcpy(&location, &BTRFS_I(inode)->location, sizeof(location));
@@ -2998,13 +2999,16 @@
ret = btrfs_unlink_inode(trans, root, dir, dentry->d_inode,
dentry->d_name.name, dentry->d_name.len);
- BUG_ON(ret);
+ if (ret)
+ goto out;
if (inode->i_nlink == 0) {
ret = btrfs_orphan_add(trans, inode);
- BUG_ON(ret);
+ if (ret)
+ goto out;
}
+out:
nr = trans->blocks_used;
__unlink_end_trans(trans, root);
btrfs_btree_balance_dirty(root, nr);
@@ -3147,6 +3151,11 @@
BUG_ON(new_size > 0 && min_type != BTRFS_EXTENT_DATA_KEY);
+ path = btrfs_alloc_path();
+ if (!path)
+ return -ENOMEM;
+ path->reada = -1;
+
if (root->ref_cows || root == root->fs_info->tree_root)
btrfs_drop_extent_cache(inode, new_size & (~mask), (u64)-1, 0);
@@ -3159,10 +3168,6 @@
if (min_type == 0 && root == BTRFS_I(inode)->root)
btrfs_kill_delayed_inode_items(inode);
- path = btrfs_alloc_path();
- BUG_ON(!path);
- path->reada = -1;
-
key.objectid = ino;
key.offset = (u64)-1;
key.type = (u8)-1;
@@ -3690,7 +3695,8 @@
int ret = 0;
path = btrfs_alloc_path();
- BUG_ON(!path);
+ if (!path)
+ return -ENOMEM;
di = btrfs_lookup_dir_item(NULL, root, path, btrfs_ino(dir), name,
namelen, 0);
@@ -3946,6 +3952,7 @@
struct btrfs_root *root, int *new)
{
struct inode *inode;
+ int bad_inode = 0;
inode = btrfs_iget_locked(s, location->objectid, root);
if (!inode)
@@ -3955,10 +3962,19 @@
BTRFS_I(inode)->root = root;
memcpy(&BTRFS_I(inode)->location, location, sizeof(*location));
btrfs_read_locked_inode(inode);
- inode_tree_add(inode);
- unlock_new_inode(inode);
- if (new)
- *new = 1;
+ if (!is_bad_inode(inode)) {
+ inode_tree_add(inode);
+ unlock_new_inode(inode);
+ if (new)
+ *new = 1;
+ } else {
+ bad_inode = 1;
+ }
+ }
+
+ if (bad_inode) {
+ iput(inode);
+ inode = ERR_PTR(-ESTALE);
}
return inode;
@@ -3993,12 +4009,19 @@
struct btrfs_root *sub_root = root;
struct btrfs_key location;
int index;
- int ret;
+ int ret = 0;
if (dentry->d_name.len > BTRFS_NAME_LEN)
return ERR_PTR(-ENAMETOOLONG);
- ret = btrfs_inode_by_name(dir, dentry, &location);
+ if (unlikely(d_need_lookup(dentry))) {
+ memcpy(&location, dentry->d_fsdata, sizeof(struct btrfs_key));
+ kfree(dentry->d_fsdata);
+ dentry->d_fsdata = NULL;
+ d_clear_need_lookup(dentry);
+ } else {
+ ret = btrfs_inode_by_name(dir, dentry, &location);
+ }
if (ret < 0)
return ERR_PTR(ret);
@@ -4053,6 +4076,12 @@
return 0;
}
+static void btrfs_dentry_release(struct dentry *dentry)
+{
+ if (dentry->d_fsdata)
+ kfree(dentry->d_fsdata);
+}
+
static struct dentry *btrfs_lookup(struct inode *dir, struct dentry *dentry,
struct nameidata *nd)
{
@@ -4075,6 +4104,7 @@
struct btrfs_path *path;
struct list_head ins_list;
struct list_head del_list;
+ struct qstr q;
int ret;
struct extent_buffer *leaf;
int slot;
@@ -4164,6 +4194,7 @@
while (di_cur < di_total) {
struct btrfs_key location;
+ struct dentry *tmp;
if (verify_dir_item(root, leaf, di))
break;
@@ -4184,6 +4215,33 @@
d_type = btrfs_filetype_table[btrfs_dir_type(leaf, di)];
btrfs_dir_item_key_to_cpu(leaf, di, &location);
+ q.name = name_ptr;
+ q.len = name_len;
+ q.hash = full_name_hash(q.name, q.len);
+ tmp = d_lookup(filp->f_dentry, &q);
+ if (!tmp) {
+ struct btrfs_key *newkey;
+
+ newkey = kzalloc(sizeof(struct btrfs_key),
+ GFP_NOFS);
+ if (!newkey)
+ goto no_dentry;
+ tmp = d_alloc(filp->f_dentry, &q);
+ if (!tmp) {
+ kfree(newkey);
+ dput(tmp);
+ goto no_dentry;
+ }
+ memcpy(newkey, &location,
+ sizeof(struct btrfs_key));
+ tmp->d_fsdata = newkey;
+ tmp->d_flags |= DCACHE_NEED_LOOKUP;
+ d_rehash(tmp);
+ dput(tmp);
+ } else {
+ dput(tmp);
+ }
+no_dentry:
/* is this a reference to our own snapshot? If so
* skip it
*/
@@ -4409,7 +4467,8 @@
int owner;
path = btrfs_alloc_path();
- BUG_ON(!path);
+ if (!path)
+ return ERR_PTR(-ENOMEM);
inode = new_inode(root->fs_info->sb);
if (!inode) {
@@ -6669,19 +6728,6 @@
return 0;
}
-/* helper function for file defrag and space balancing. This
- * forces readahead on a given range of bytes in an inode
- */
-unsigned long btrfs_force_ra(struct address_space *mapping,
- struct file_ra_state *ra, struct file *file,
- pgoff_t offset, pgoff_t last_index)
-{
- pgoff_t req_size = last_index - offset + 1;
-
- page_cache_sync_readahead(mapping, ra, file, offset, req_size);
- return offset + req_size;
-}
-
struct inode *btrfs_alloc_inode(struct super_block *sb)
{
struct btrfs_inode *ei;
@@ -7164,7 +7210,11 @@
goto out_unlock;
path = btrfs_alloc_path();
- BUG_ON(!path);
+ if (!path) {
+ err = -ENOMEM;
+ drop_inode = 1;
+ goto out_unlock;
+ }
key.objectid = btrfs_ino(inode);
key.offset = 0;
btrfs_set_key_type(&key, BTRFS_EXTENT_DATA_KEY);
@@ -7304,11 +7354,15 @@
static int btrfs_permission(struct inode *inode, int mask)
{
struct btrfs_root *root = BTRFS_I(inode)->root;
+ umode_t mode = inode->i_mode;
- if (btrfs_root_readonly(root) && (mask & MAY_WRITE))
- return -EROFS;
- if ((BTRFS_I(inode)->flags & BTRFS_INODE_READONLY) && (mask & MAY_WRITE))
- return -EACCES;
+ if (mask & MAY_WRITE &&
+ (S_ISREG(mode) || S_ISDIR(mode) || S_ISLNK(mode))) {
+ if (btrfs_root_readonly(root))
+ return -EROFS;
+ if (BTRFS_I(inode)->flags & BTRFS_INODE_READONLY)
+ return -EACCES;
+ }
return generic_permission(inode, mask);
}
@@ -7430,4 +7484,5 @@
const struct dentry_operations btrfs_dentry_operations = {
.d_delete = btrfs_dentry_delete,
+ .d_release = btrfs_dentry_release,
};
diff --git a/fs/btrfs/ioctl.c b/fs/btrfs/ioctl.c
index 0b980af..970977a 100644
--- a/fs/btrfs/ioctl.c
+++ b/fs/btrfs/ioctl.c
@@ -1749,11 +1749,10 @@
key.objectid = key.offset;
key.offset = (u64)-1;
dirid = key.objectid;
-
}
if (ptr < name)
goto out;
- memcpy(name, ptr, total_len);
+ memmove(name, ptr, total_len);
name[total_len]='\0';
ret = 0;
out:
@@ -2237,6 +2236,10 @@
btrfs_wait_ordered_range(src, off, len);
}
+ /* truncate page cache pages from target inode range */
+ truncate_inode_pages_range(&inode->i_data, off,
+ ALIGN(off + len, PAGE_CACHE_SIZE) - 1);
+
/* clone data */
key.objectid = btrfs_ino(src);
key.type = BTRFS_EXTENT_DATA_KEY;
diff --git a/fs/btrfs/ref-cache.c b/fs/btrfs/ref-cache.c
deleted file mode 100644
index 82d569c..0000000
--- a/fs/btrfs/ref-cache.c
+++ /dev/null
@@ -1,68 +0,0 @@
-/*
- * Copyright (C) 2008 Oracle. All rights reserved.
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public
- * License v2 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 021110-1307, USA.
- */
-
-#include <linux/sched.h>
-#include <linux/slab.h>
-#include <linux/sort.h>
-#include "ctree.h"
-#include "ref-cache.h"
-#include "transaction.h"
-
-static struct rb_node *tree_insert(struct rb_root *root, u64 bytenr,
- struct rb_node *node)
-{
- struct rb_node **p = &root->rb_node;
- struct rb_node *parent = NULL;
- struct btrfs_leaf_ref *entry;
-
- while (*p) {
- parent = *p;
- entry = rb_entry(parent, struct btrfs_leaf_ref, rb_node);
-
- if (bytenr < entry->bytenr)
- p = &(*p)->rb_left;
- else if (bytenr > entry->bytenr)
- p = &(*p)->rb_right;
- else
- return parent;
- }
-
- entry = rb_entry(node, struct btrfs_leaf_ref, rb_node);
- rb_link_node(node, parent, p);
- rb_insert_color(node, root);
- return NULL;
-}
-
-static struct rb_node *tree_search(struct rb_root *root, u64 bytenr)
-{
- struct rb_node *n = root->rb_node;
- struct btrfs_leaf_ref *entry;
-
- while (n) {
- entry = rb_entry(n, struct btrfs_leaf_ref, rb_node);
- WARN_ON(!entry->in_tree);
-
- if (bytenr < entry->bytenr)
- n = n->rb_left;
- else if (bytenr > entry->bytenr)
- n = n->rb_right;
- else
- return n;
- }
- return NULL;
-}
diff --git a/fs/btrfs/ref-cache.h b/fs/btrfs/ref-cache.h
deleted file mode 100644
index 24f7001..0000000
--- a/fs/btrfs/ref-cache.h
+++ /dev/null
@@ -1,52 +0,0 @@
-/*
- * Copyright (C) 2008 Oracle. All rights reserved.
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public
- * License v2 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 021110-1307, USA.
- */
-#ifndef __REFCACHE__
-#define __REFCACHE__
-
-struct btrfs_extent_info {
- /* bytenr and num_bytes find the extent in the extent allocation tree */
- u64 bytenr;
- u64 num_bytes;
-
- /* objectid and offset find the back reference for the file */
- u64 objectid;
- u64 offset;
-};
-
-struct btrfs_leaf_ref {
- struct rb_node rb_node;
- struct btrfs_leaf_ref_tree *tree;
- int in_tree;
- atomic_t usage;
-
- u64 root_gen;
- u64 bytenr;
- u64 owner;
- u64 generation;
- int nritems;
-
- struct list_head list;
- struct btrfs_extent_info extents[];
-};
-
-static inline size_t btrfs_leaf_ref_size(int nr_extents)
-{
- return sizeof(struct btrfs_leaf_ref) +
- sizeof(struct btrfs_extent_info) * nr_extents;
-}
-#endif
diff --git a/fs/btrfs/root-tree.c b/fs/btrfs/root-tree.c
index ebe4544..f409990 100644
--- a/fs/btrfs/root-tree.c
+++ b/fs/btrfs/root-tree.c
@@ -71,13 +71,12 @@
return ret;
}
-int btrfs_set_root_node(struct btrfs_root_item *item,
- struct extent_buffer *node)
+void btrfs_set_root_node(struct btrfs_root_item *item,
+ struct extent_buffer *node)
{
btrfs_set_root_bytenr(item, node->start);
btrfs_set_root_level(item, btrfs_header_level(node));
btrfs_set_root_generation(item, btrfs_header_generation(node));
- return 0;
}
/*
diff --git a/fs/btrfs/transaction.c b/fs/btrfs/transaction.c
index eb55863..7dc36fa 100644
--- a/fs/btrfs/transaction.c
+++ b/fs/btrfs/transaction.c
@@ -216,17 +216,11 @@
spin_lock(&root->fs_info->trans_lock);
cur_trans = root->fs_info->running_transaction;
if (cur_trans && cur_trans->blocked) {
- DEFINE_WAIT(wait);
atomic_inc(&cur_trans->use_count);
spin_unlock(&root->fs_info->trans_lock);
- while (1) {
- prepare_to_wait(&root->fs_info->transaction_wait, &wait,
- TASK_UNINTERRUPTIBLE);
- if (!cur_trans->blocked)
- break;
- schedule();
- }
- finish_wait(&root->fs_info->transaction_wait, &wait);
+
+ wait_event(root->fs_info->transaction_wait,
+ !cur_trans->blocked);
put_transaction(cur_trans);
} else {
spin_unlock(&root->fs_info->trans_lock);
@@ -357,19 +351,10 @@
}
/* wait for a transaction commit to be fully complete */
-static noinline int wait_for_commit(struct btrfs_root *root,
+static noinline void wait_for_commit(struct btrfs_root *root,
struct btrfs_transaction *commit)
{
- DEFINE_WAIT(wait);
- while (!commit->commit_done) {
- prepare_to_wait(&commit->commit_wait, &wait,
- TASK_UNINTERRUPTIBLE);
- if (commit->commit_done)
- break;
- schedule();
- }
- finish_wait(&commit->commit_wait, &wait);
- return 0;
+ wait_event(commit->commit_wait, commit->commit_done);
}
int btrfs_wait_for_commit(struct btrfs_root *root, u64 transid)
@@ -1085,22 +1070,7 @@
static void wait_current_trans_commit_start(struct btrfs_root *root,
struct btrfs_transaction *trans)
{
- DEFINE_WAIT(wait);
-
- if (trans->in_commit)
- return;
-
- while (1) {
- prepare_to_wait(&root->fs_info->transaction_blocked_wait, &wait,
- TASK_UNINTERRUPTIBLE);
- if (trans->in_commit) {
- finish_wait(&root->fs_info->transaction_blocked_wait,
- &wait);
- break;
- }
- schedule();
- finish_wait(&root->fs_info->transaction_blocked_wait, &wait);
- }
+ wait_event(root->fs_info->transaction_blocked_wait, trans->in_commit);
}
/*
@@ -1110,24 +1080,8 @@
static void wait_current_trans_commit_start_and_unblock(struct btrfs_root *root,
struct btrfs_transaction *trans)
{
- DEFINE_WAIT(wait);
-
- if (trans->commit_done || (trans->in_commit && !trans->blocked))
- return;
-
- while (1) {
- prepare_to_wait(&root->fs_info->transaction_wait, &wait,
- TASK_UNINTERRUPTIBLE);
- if (trans->commit_done ||
- (trans->in_commit && !trans->blocked)) {
- finish_wait(&root->fs_info->transaction_wait,
- &wait);
- break;
- }
- schedule();
- finish_wait(&root->fs_info->transaction_wait,
- &wait);
- }
+ wait_event(root->fs_info->transaction_wait,
+ trans->commit_done || (trans->in_commit && !trans->blocked));
}
/*
@@ -1234,8 +1188,7 @@
atomic_inc(&cur_trans->use_count);
btrfs_end_transaction(trans, root);
- ret = wait_for_commit(root, cur_trans);
- BUG_ON(ret);
+ wait_for_commit(root, cur_trans);
put_transaction(cur_trans);
diff --git a/fs/btrfs/tree-log.c b/fs/btrfs/tree-log.c
index ac278dd..786639f 100644
--- a/fs/btrfs/tree-log.c
+++ b/fs/btrfs/tree-log.c
@@ -799,14 +799,15 @@
struct extent_buffer *eb, int slot,
struct btrfs_key *key)
{
- struct inode *dir;
- int ret;
struct btrfs_inode_ref *ref;
+ struct btrfs_dir_item *di;
+ struct inode *dir;
struct inode *inode;
- char *name;
- int namelen;
unsigned long ref_ptr;
unsigned long ref_end;
+ char *name;
+ int namelen;
+ int ret;
int search_done = 0;
/*
@@ -909,6 +910,25 @@
}
btrfs_release_path(path);
+ /* look for a conflicting sequence number */
+ di = btrfs_lookup_dir_index_item(trans, root, path, btrfs_ino(dir),
+ btrfs_inode_ref_index(eb, ref),
+ name, namelen, 0);
+ if (di && !IS_ERR(di)) {
+ ret = drop_one_dir_item(trans, root, path, dir, di);
+ BUG_ON(ret);
+ }
+ btrfs_release_path(path);
+
+ /* look for a conflicing name */
+ di = btrfs_lookup_dir_item(trans, root, path, btrfs_ino(dir),
+ name, namelen, 0);
+ if (di && !IS_ERR(di)) {
+ ret = drop_one_dir_item(trans, root, path, dir, di);
+ BUG_ON(ret);
+ }
+ btrfs_release_path(path);
+
insert:
/* insert our name */
ret = btrfs_add_link(trans, dir, inode, name, namelen, 0,
@@ -1617,7 +1637,8 @@
return 0;
path = btrfs_alloc_path();
- BUG_ON(!path);
+ if (!path)
+ return -ENOMEM;
nritems = btrfs_header_nritems(eb);
for (i = 0; i < nritems; i++) {
@@ -1723,7 +1744,9 @@
return -ENOMEM;
if (*level == 1) {
- wc->process_func(root, next, wc, ptr_gen);
+ ret = wc->process_func(root, next, wc, ptr_gen);
+ if (ret)
+ return ret;
path->slots[*level]++;
if (wc->free) {
@@ -1788,8 +1811,11 @@
parent = path->nodes[*level + 1];
root_owner = btrfs_header_owner(parent);
- wc->process_func(root, path->nodes[*level], wc,
+ ret = wc->process_func(root, path->nodes[*level], wc,
btrfs_header_generation(path->nodes[*level]));
+ if (ret)
+ return ret;
+
if (wc->free) {
struct extent_buffer *next;
diff --git a/fs/btrfs/volumes.c b/fs/btrfs/volumes.c
index b89e372..f2a4cc7 100644
--- a/fs/btrfs/volumes.c
+++ b/fs/btrfs/volumes.c
@@ -142,6 +142,7 @@
unsigned long limit;
unsigned long last_waited = 0;
int force_reg = 0;
+ int sync_pending = 0;
struct blk_plug plug;
/*
@@ -229,6 +230,22 @@
BUG_ON(atomic_read(&cur->bi_cnt) == 0);
+ /*
+ * if we're doing the sync list, record that our
+ * plug has some sync requests on it
+ *
+ * If we're doing the regular list and there are
+ * sync requests sitting around, unplug before
+ * we add more
+ */
+ if (pending_bios == &device->pending_sync_bios) {
+ sync_pending = 1;
+ } else if (sync_pending) {
+ blk_finish_plug(&plug);
+ blk_start_plug(&plug);
+ sync_pending = 0;
+ }
+
submit_bio(cur->bi_rw, cur);
num_run++;
batch_run++;
@@ -500,6 +517,9 @@
fs_devices->rw_devices--;
}
+ if (device->can_discard)
+ fs_devices->num_can_discard--;
+
new_device = kmalloc(sizeof(*new_device), GFP_NOFS);
BUG_ON(!new_device);
memcpy(new_device, device, sizeof(*new_device));
@@ -508,6 +528,7 @@
new_device->bdev = NULL;
new_device->writeable = 0;
new_device->in_fs_metadata = 0;
+ new_device->can_discard = 0;
list_replace_rcu(&device->dev_list, &new_device->dev_list);
call_rcu(&device->rcu, free_device);
@@ -547,6 +568,7 @@
static int __btrfs_open_devices(struct btrfs_fs_devices *fs_devices,
fmode_t flags, void *holder)
{
+ struct request_queue *q;
struct block_device *bdev;
struct list_head *head = &fs_devices->devices;
struct btrfs_device *device;
@@ -603,6 +625,12 @@
seeding = 0;
}
+ q = bdev_get_queue(bdev);
+ if (blk_queue_discard(q)) {
+ device->can_discard = 1;
+ fs_devices->num_can_discard++;
+ }
+
device->bdev = bdev;
device->in_fs_metadata = 0;
device->mode = flags;
@@ -835,6 +863,7 @@
max_hole_start = search_start;
max_hole_size = 0;
+ hole_size = 0;
if (search_start >= search_end) {
ret = -ENOSPC;
@@ -917,7 +946,14 @@
cond_resched();
}
- hole_size = search_end- search_start;
+ /*
+ * At this point, search_start should be the end of
+ * allocated dev extents, and when shrinking the device,
+ * search_end may be smaller than search_start.
+ */
+ if (search_end > search_start)
+ hole_size = search_end - search_start;
+
if (hole_size > max_hole_size) {
max_hole_start = search_start;
max_hole_size = hole_size;
@@ -1037,7 +1073,8 @@
struct btrfs_key found_key;
path = btrfs_alloc_path();
- BUG_ON(!path);
+ if (!path)
+ return -ENOMEM;
key.objectid = objectid;
key.offset = (u64)-1;
@@ -1542,6 +1579,7 @@
int btrfs_init_new_device(struct btrfs_root *root, char *device_path)
{
+ struct request_queue *q;
struct btrfs_trans_handle *trans;
struct btrfs_device *device;
struct block_device *bdev;
@@ -1611,6 +1649,9 @@
lock_chunks(root);
+ q = bdev_get_queue(bdev);
+ if (blk_queue_discard(q))
+ device->can_discard = 1;
device->writeable = 1;
device->work.func = pending_bios_fn;
generate_random_uuid(device->uuid);
@@ -1646,6 +1687,8 @@
root->fs_info->fs_devices->num_devices++;
root->fs_info->fs_devices->open_devices++;
root->fs_info->fs_devices->rw_devices++;
+ if (device->can_discard)
+ root->fs_info->fs_devices->num_can_discard++;
root->fs_info->fs_devices->total_rw_bytes += device->total_bytes;
if (!blk_queue_nonrot(bdev_get_queue(bdev)))
@@ -2061,8 +2104,10 @@
/* step two, relocate all the chunks */
path = btrfs_alloc_path();
- BUG_ON(!path);
-
+ if (!path) {
+ ret = -ENOMEM;
+ goto error;
+ }
key.objectid = BTRFS_FIRST_CHUNK_TREE_OBJECTID;
key.offset = (u64)-1;
key.type = BTRFS_CHUNK_ITEM_KEY;
@@ -2410,9 +2455,10 @@
total_avail = device->total_bytes - device->bytes_used;
else
total_avail = 0;
- /* avail is off by max(alloc_start, 1MB), but that is the same
- * for all devices, so it doesn't hurt the sorting later on
- */
+
+ /* If there is no space on this device, skip it. */
+ if (total_avail == 0)
+ continue;
ret = find_free_dev_extent(trans, device,
max_stripe_size * dev_stripes,
@@ -2661,7 +2707,8 @@
ret = find_next_chunk(fs_info->chunk_root,
BTRFS_FIRST_CHUNK_TREE_OBJECTID, &chunk_offset);
- BUG_ON(ret);
+ if (ret)
+ return ret;
alloc_profile = BTRFS_BLOCK_GROUP_METADATA |
(fs_info->metadata_alloc_profile &
diff --git a/fs/btrfs/volumes.h b/fs/btrfs/volumes.h
index 7c12d61..6d866db 100644
--- a/fs/btrfs/volumes.h
+++ b/fs/btrfs/volumes.h
@@ -48,6 +48,7 @@
int writeable;
int in_fs_metadata;
int missing;
+ int can_discard;
spinlock_t io_lock;
@@ -104,6 +105,7 @@
u64 rw_devices;
u64 missing_devices;
u64 total_rw_bytes;
+ u64 num_can_discard;
struct block_device *latest_bdev;
/* all of the devices in the FS, protected by a mutex
diff --git a/fs/cifs/cifs_debug.c b/fs/cifs/cifs_debug.c
index 2fe3cf1..6d40656 100644
--- a/fs/cifs/cifs_debug.c
+++ b/fs/cifs/cifs_debug.c
@@ -176,7 +176,7 @@
#ifdef CONFIG_CIFS_STATS2
seq_printf(m, " In Send: %d In MaxReq Wait: %d",
- atomic_read(&server->inSend),
+ atomic_read(&server->in_send),
atomic_read(&server->num_waiters));
#endif
diff --git a/fs/cifs/cifs_dfs_ref.c b/fs/cifs/cifs_dfs_ref.c
index 8d8f28c..6873bb6 100644
--- a/fs/cifs/cifs_dfs_ref.c
+++ b/fs/cifs/cifs_dfs_ref.c
@@ -141,10 +141,11 @@
rc = dns_resolve_server_name_to_ip(*devname, &srvIP);
if (rc < 0) {
- cERROR(1, "%s: Failed to resolve server part of %s to IP: %d",
- __func__, *devname, rc);
+ cFYI(1, "%s: Failed to resolve server part of %s to IP: %d",
+ __func__, *devname, rc);
goto compose_mount_options_err;
}
+
/* md_len = strlen(...) + 12 for 'sep+prefixpath='
* assuming that we have 'unc=' and 'ip=' in
* the original sb_mountdata
diff --git a/fs/cifs/cifsacl.c b/fs/cifs/cifsacl.c
index 21de1d6..d0f59fa 100644
--- a/fs/cifs/cifsacl.c
+++ b/fs/cifs/cifsacl.c
@@ -991,24 +991,6 @@
return pntsd;
}
-static int set_cifs_acl_by_fid(struct cifs_sb_info *cifs_sb, __u16 fid,
- struct cifs_ntsd *pnntsd, u32 acllen)
-{
- int xid, rc;
- struct tcon_link *tlink = cifs_sb_tlink(cifs_sb);
-
- if (IS_ERR(tlink))
- return PTR_ERR(tlink);
-
- xid = GetXid();
- rc = CIFSSMBSetCIFSACL(xid, tlink_tcon(tlink), fid, pnntsd, acllen);
- FreeXid(xid);
- cifs_put_tlink(tlink);
-
- cFYI(DBG2, "SetCIFSACL rc = %d", rc);
- return rc;
-}
-
static int set_cifs_acl_by_path(struct cifs_sb_info *cifs_sb, const char *path,
struct cifs_ntsd *pnntsd, u32 acllen)
{
@@ -1047,18 +1029,10 @@
struct inode *inode, const char *path)
{
struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb);
- struct cifsFileInfo *open_file;
- int rc;
cFYI(DBG2, "set ACL for %s from mode 0x%x", path, inode->i_mode);
- open_file = find_readable_file(CIFS_I(inode), true);
- if (!open_file)
- return set_cifs_acl_by_path(cifs_sb, path, pnntsd, acllen);
-
- rc = set_cifs_acl_by_fid(cifs_sb, open_file->netfid, pnntsd, acllen);
- cifsFileInfo_put(open_file);
- return rc;
+ return set_cifs_acl_by_path(cifs_sb, path, pnntsd, acllen);
}
/* Translate the CIFS ACL (simlar to NTFS ACL) for a file into mode bits */
diff --git a/fs/cifs/cifsencrypt.c b/fs/cifs/cifsencrypt.c
index 259991b..e76bfeb 100644
--- a/fs/cifs/cifsencrypt.c
+++ b/fs/cifs/cifsencrypt.c
@@ -87,9 +87,15 @@
if ((cifs_pdu == NULL) || (server == NULL))
return -EINVAL;
- if ((cifs_pdu->Flags2 & SMBFLG2_SECURITY_SIGNATURE) == 0)
+ if (!(cifs_pdu->Flags2 & SMBFLG2_SECURITY_SIGNATURE) ||
+ server->tcpStatus == CifsNeedNegotiate)
return rc;
+ if (!server->session_estab) {
+ strncpy(cifs_pdu->Signature.SecuritySignature, "BSRSPYL", 8);
+ return rc;
+ }
+
cifs_pdu->Signature.Sequence.SequenceNumber =
cpu_to_le32(server->sequence_number);
cifs_pdu->Signature.Sequence.Reserved = 0;
@@ -178,9 +184,15 @@
if ((cifs_pdu == NULL) || (server == NULL))
return -EINVAL;
- if ((cifs_pdu->Flags2 & SMBFLG2_SECURITY_SIGNATURE) == 0)
+ if (!(cifs_pdu->Flags2 & SMBFLG2_SECURITY_SIGNATURE) ||
+ server->tcpStatus == CifsNeedNegotiate)
return rc;
+ if (!server->session_estab) {
+ strncpy(cifs_pdu->Signature.SecuritySignature, "BSRSPYL", 8);
+ return rc;
+ }
+
cifs_pdu->Signature.Sequence.SequenceNumber =
cpu_to_le32(server->sequence_number);
cifs_pdu->Signature.Sequence.Reserved = 0;
diff --git a/fs/cifs/cifsfs.c b/fs/cifs/cifsfs.c
index 8655174..f93eb94 100644
--- a/fs/cifs/cifsfs.c
+++ b/fs/cifs/cifsfs.c
@@ -86,24 +86,6 @@
extern mempool_t *cifs_req_poolp;
extern mempool_t *cifs_mid_poolp;
-void
-cifs_sb_active(struct super_block *sb)
-{
- struct cifs_sb_info *server = CIFS_SB(sb);
-
- if (atomic_inc_return(&server->active) == 1)
- atomic_inc(&sb->s_active);
-}
-
-void
-cifs_sb_deactive(struct super_block *sb)
-{
- struct cifs_sb_info *server = CIFS_SB(sb);
-
- if (atomic_dec_and_test(&server->active))
- deactivate_super(sb);
-}
-
static int
cifs_read_super(struct super_block *sb)
{
@@ -581,6 +563,10 @@
mutex_unlock(&dir->i_mutex);
dput(dentry);
dentry = child;
+ if (!dentry->d_inode) {
+ dput(dentry);
+ dentry = ERR_PTR(-ENOENT);
+ }
} while (!IS_ERR(dentry));
_FreeXid(xid);
kfree(full_path);
diff --git a/fs/cifs/cifsfs.h b/fs/cifs/cifsfs.h
index fbd050c..95da802 100644
--- a/fs/cifs/cifsfs.h
+++ b/fs/cifs/cifsfs.h
@@ -41,10 +41,6 @@
extern const struct address_space_operations cifs_addr_ops;
extern const struct address_space_operations cifs_addr_ops_smallbuf;
-/* Functions related to super block operations */
-extern void cifs_sb_active(struct super_block *sb);
-extern void cifs_sb_deactive(struct super_block *sb);
-
/* Functions related to inodes */
extern const struct inode_operations cifs_dir_inode_ops;
extern struct inode *cifs_root_iget(struct super_block *);
@@ -129,5 +125,5 @@
extern const struct export_operations cifs_export_ops;
#endif /* CIFS_NFSD_EXPORT */
-#define CIFS_VERSION "1.74"
+#define CIFS_VERSION "1.75"
#endif /* _CIFSFS_H */
diff --git a/fs/cifs/cifsglob.h b/fs/cifs/cifsglob.h
index 1fcf4e5..95dad9d 100644
--- a/fs/cifs/cifsglob.h
+++ b/fs/cifs/cifsglob.h
@@ -291,7 +291,7 @@
struct fscache_cookie *fscache; /* client index cache cookie */
#endif
#ifdef CONFIG_CIFS_STATS2
- atomic_t inSend; /* requests trying to send */
+ atomic_t in_send; /* requests trying to send */
atomic_t num_waiters; /* blocked waiting to get in sendrecv */
#endif
};
@@ -672,12 +672,54 @@
bool multiEnd:1; /* both received */
};
-struct oplock_q_entry {
- struct list_head qhead;
- struct inode *pinode;
- struct cifs_tcon *tcon;
- __u16 netfid;
-};
+/* Make code in transport.c a little cleaner by moving
+ update of optional stats into function below */
+#ifdef CONFIG_CIFS_STATS2
+
+static inline void cifs_in_send_inc(struct TCP_Server_Info *server)
+{
+ atomic_inc(&server->in_send);
+}
+
+static inline void cifs_in_send_dec(struct TCP_Server_Info *server)
+{
+ atomic_dec(&server->in_send);
+}
+
+static inline void cifs_num_waiters_inc(struct TCP_Server_Info *server)
+{
+ atomic_inc(&server->num_waiters);
+}
+
+static inline void cifs_num_waiters_dec(struct TCP_Server_Info *server)
+{
+ atomic_dec(&server->num_waiters);
+}
+
+static inline void cifs_save_when_sent(struct mid_q_entry *mid)
+{
+ mid->when_sent = jiffies;
+}
+#else
+static inline void cifs_in_send_inc(struct TCP_Server_Info *server)
+{
+}
+static inline void cifs_in_send_dec(struct TCP_Server_Info *server)
+{
+}
+
+static inline void cifs_num_waiters_inc(struct TCP_Server_Info *server)
+{
+}
+
+static inline void cifs_num_waiters_dec(struct TCP_Server_Info *server)
+{
+}
+
+static inline void cifs_save_when_sent(struct mid_q_entry *mid)
+{
+}
+#endif
/* for pending dnotify requests */
struct dir_notify_req {
@@ -942,8 +984,6 @@
GLOBAL_EXTERN spinlock_t sidgidlock;
void cifs_oplock_break(struct work_struct *work);
-void cifs_oplock_break_get(struct cifsFileInfo *cfile);
-void cifs_oplock_break_put(struct cifsFileInfo *cfile);
extern const struct slow_work_ops cifs_oplock_break_ops;
diff --git a/fs/cifs/cifssmb.c b/fs/cifs/cifssmb.c
index 1a9fe7f..aac37d9 100644
--- a/fs/cifs/cifssmb.c
+++ b/fs/cifs/cifssmb.c
@@ -107,7 +107,7 @@
static int
cifs_reconnect_tcon(struct cifs_tcon *tcon, int smb_command)
{
- int rc = 0;
+ int rc;
struct cifs_ses *ses;
struct TCP_Server_Info *server;
struct nls_table *nls_codepage;
@@ -5720,6 +5720,7 @@
char *temp_ptr;
char *end_of_smb;
__u16 params, byte_count, data_offset;
+ unsigned int ea_name_len = ea_name ? strlen(ea_name) : 0;
cFYI(1, "In Query All EAs path %s", searchName);
QAllEAsRetry:
@@ -5837,7 +5838,8 @@
}
if (ea_name) {
- if (strncmp(ea_name, temp_ptr, name_len) == 0) {
+ if (ea_name_len == name_len &&
+ strncmp(ea_name, temp_ptr, name_len) == 0) {
temp_ptr += name_len + 1;
rc = value_len;
if (buf_size == 0)
diff --git a/fs/cifs/connect.c b/fs/cifs/connect.c
index e66297b..633c246 100644
--- a/fs/cifs/connect.c
+++ b/fs/cifs/connect.c
@@ -319,344 +319,221 @@
queue_delayed_work(system_nrt_wq, &server->echo, SMB_ECHO_INTERVAL);
}
-static int
-cifs_demultiplex_thread(void *p)
+static bool
+allocate_buffers(char **bigbuf, char **smallbuf, unsigned int size,
+ bool is_large_buf)
{
- int length;
- struct TCP_Server_Info *server = p;
- unsigned int pdu_length, total_read;
- struct smb_hdr *smb_buffer = NULL;
- struct smb_hdr *bigbuf = NULL;
- struct smb_hdr *smallbuf = NULL;
- struct msghdr smb_msg;
- struct kvec iov;
- struct socket *csocket = server->ssocket;
- struct list_head *tmp, *tmp2;
- struct task_struct *task_to_wake = NULL;
- struct mid_q_entry *mid_entry;
- char temp;
- bool isLargeBuf = false;
- bool isMultiRsp;
- int reconnect;
+ char *bbuf = *bigbuf, *sbuf = *smallbuf;
- current->flags |= PF_MEMALLOC;
- cFYI(1, "Demultiplex PID: %d", task_pid_nr(current));
-
- length = atomic_inc_return(&tcpSesAllocCount);
- if (length > 1)
- mempool_resize(cifs_req_poolp, length + cifs_min_rcv,
- GFP_KERNEL);
-
- set_freezable();
- while (server->tcpStatus != CifsExiting) {
- if (try_to_freeze())
- continue;
- if (bigbuf == NULL) {
- bigbuf = cifs_buf_get();
- if (!bigbuf) {
- cERROR(1, "No memory for large SMB response");
- msleep(3000);
- /* retry will check if exiting */
- continue;
- }
- } else if (isLargeBuf) {
- /* we are reusing a dirty large buf, clear its start */
- memset(bigbuf, 0, sizeof(struct smb_hdr));
+ if (bbuf == NULL) {
+ bbuf = (char *)cifs_buf_get();
+ if (!bbuf) {
+ cERROR(1, "No memory for large SMB response");
+ msleep(3000);
+ /* retry will check if exiting */
+ return false;
}
+ } else if (is_large_buf) {
+ /* we are reusing a dirty large buf, clear its start */
+ memset(bbuf, 0, size);
+ }
- if (smallbuf == NULL) {
- smallbuf = cifs_small_buf_get();
- if (!smallbuf) {
- cERROR(1, "No memory for SMB response");
- msleep(1000);
- /* retry will check if exiting */
- continue;
- }
- /* beginning of smb buffer is cleared in our buf_get */
- } else /* if existing small buf clear beginning */
- memset(smallbuf, 0, sizeof(struct smb_hdr));
-
- isLargeBuf = false;
- isMultiRsp = false;
- smb_buffer = smallbuf;
- iov.iov_base = smb_buffer;
- iov.iov_len = 4;
- smb_msg.msg_control = NULL;
- smb_msg.msg_controllen = 0;
- pdu_length = 4; /* enough to get RFC1001 header */
-
-incomplete_rcv:
- if (echo_retries > 0 && server->tcpStatus == CifsGood &&
- time_after(jiffies, server->lstrp +
- (echo_retries * SMB_ECHO_INTERVAL))) {
- cERROR(1, "Server %s has not responded in %d seconds. "
- "Reconnecting...", server->hostname,
- (echo_retries * SMB_ECHO_INTERVAL / HZ));
- cifs_reconnect(server);
- csocket = server->ssocket;
- wake_up(&server->response_q);
- continue;
+ if (sbuf == NULL) {
+ sbuf = (char *)cifs_small_buf_get();
+ if (!sbuf) {
+ cERROR(1, "No memory for SMB response");
+ msleep(1000);
+ /* retry will check if exiting */
+ return false;
}
+ /* beginning of smb buffer is cleared in our buf_get */
+ } else {
+ /* if existing small buf clear beginning */
+ memset(sbuf, 0, size);
+ }
- length =
- kernel_recvmsg(csocket, &smb_msg,
- &iov, 1, pdu_length, 0 /* BB other flags? */);
+ *bigbuf = bbuf;
+ *smallbuf = sbuf;
+ return true;
+}
+
+static int
+read_from_socket(struct TCP_Server_Info *server, struct msghdr *smb_msg,
+ struct kvec *iov, unsigned int to_read,
+ unsigned int *ptotal_read, bool is_header_read)
+{
+ int length, rc = 0;
+ unsigned int total_read;
+ char *buf = iov->iov_base;
+
+ for (total_read = 0; total_read < to_read; total_read += length) {
+ length = kernel_recvmsg(server->ssocket, smb_msg, iov, 1,
+ to_read - total_read, 0);
if (server->tcpStatus == CifsExiting) {
+ /* then will exit */
+ rc = 2;
break;
} else if (server->tcpStatus == CifsNeedReconnect) {
- cFYI(1, "Reconnect after server stopped responding");
cifs_reconnect(server);
- cFYI(1, "call to reconnect done");
- csocket = server->ssocket;
- continue;
+ /* Reconnect wakes up rspns q */
+ /* Now we will reread sock */
+ rc = 1;
+ break;
} else if (length == -ERESTARTSYS ||
length == -EAGAIN ||
length == -EINTR) {
- msleep(1); /* minimum sleep to prevent looping
- allowing socket to clear and app threads to set
- tcpStatus CifsNeedReconnect if server hung */
- if (pdu_length < 4) {
- iov.iov_base = (4 - pdu_length) +
- (char *)smb_buffer;
- iov.iov_len = pdu_length;
- smb_msg.msg_control = NULL;
- smb_msg.msg_controllen = 0;
- goto incomplete_rcv;
- } else
- continue;
- } else if (length <= 0) {
- cFYI(1, "Reconnect after unexpected peek error %d",
- length);
- cifs_reconnect(server);
- csocket = server->ssocket;
- wake_up(&server->response_q);
- continue;
- } else if (length < pdu_length) {
- cFYI(1, "requested %d bytes but only got %d bytes",
- pdu_length, length);
- pdu_length -= length;
- msleep(1);
- goto incomplete_rcv;
- }
-
- /* The right amount was read from socket - 4 bytes */
- /* so we can now interpret the length field */
-
- /* the first byte big endian of the length field,
- is actually not part of the length but the type
- with the most common, zero, as regular data */
- temp = *((char *) smb_buffer);
-
- /* Note that FC 1001 length is big endian on the wire,
- but we convert it here so it is always manipulated
- as host byte order */
- pdu_length = be32_to_cpu(smb_buffer->smb_buf_length);
-
- cFYI(1, "rfc1002 length 0x%x", pdu_length+4);
-
- if (temp == (char) RFC1002_SESSION_KEEP_ALIVE) {
- continue;
- } else if (temp == (char)RFC1002_POSITIVE_SESSION_RESPONSE) {
- cFYI(1, "Good RFC 1002 session rsp");
- continue;
- } else if (temp == (char)RFC1002_NEGATIVE_SESSION_RESPONSE) {
- /* we get this from Windows 98 instead of
- an error on SMB negprot response */
- cFYI(1, "Negative RFC1002 Session Response Error 0x%x)",
- pdu_length);
- /* give server a second to clean up */
- msleep(1000);
- /* always try 445 first on reconnect since we get NACK
- * on some if we ever connected to port 139 (the NACK
- * is since we do not begin with RFC1001 session
- * initialize frame)
+ /*
+ * Minimum sleep to prevent looping, allowing socket
+ * to clear and app threads to set tcpStatus
+ * CifsNeedReconnect if server hung.
*/
- cifs_set_port((struct sockaddr *)
- &server->dstaddr, CIFS_PORT);
- cifs_reconnect(server);
- csocket = server->ssocket;
- wake_up(&server->response_q);
- continue;
- } else if (temp != (char) 0) {
- cERROR(1, "Unknown RFC 1002 frame");
- cifs_dump_mem(" Received Data: ", (char *)smb_buffer,
- length);
- cifs_reconnect(server);
- csocket = server->ssocket;
- continue;
- }
-
- /* else we have an SMB response */
- if ((pdu_length > CIFSMaxBufSize + MAX_CIFS_HDR_SIZE - 4) ||
- (pdu_length < sizeof(struct smb_hdr) - 1 - 4)) {
- cERROR(1, "Invalid size SMB length %d pdu_length %d",
- length, pdu_length+4);
- cifs_reconnect(server);
- csocket = server->ssocket;
- wake_up(&server->response_q);
- continue;
- }
-
- /* else length ok */
- reconnect = 0;
-
- if (pdu_length > MAX_CIFS_SMALL_BUFFER_SIZE - 4) {
- isLargeBuf = true;
- memcpy(bigbuf, smallbuf, 4);
- smb_buffer = bigbuf;
- }
- length = 0;
- iov.iov_base = 4 + (char *)smb_buffer;
- iov.iov_len = pdu_length;
- for (total_read = 0; total_read < pdu_length;
- total_read += length) {
- length = kernel_recvmsg(csocket, &smb_msg, &iov, 1,
- pdu_length - total_read, 0);
- if (server->tcpStatus == CifsExiting) {
- /* then will exit */
- reconnect = 2;
- break;
- } else if (server->tcpStatus == CifsNeedReconnect) {
- cifs_reconnect(server);
- csocket = server->ssocket;
- /* Reconnect wakes up rspns q */
- /* Now we will reread sock */
- reconnect = 1;
- break;
- } else if (length == -ERESTARTSYS ||
- length == -EAGAIN ||
- length == -EINTR) {
- msleep(1); /* minimum sleep to prevent looping,
- allowing socket to clear and app
- threads to set tcpStatus
- CifsNeedReconnect if server hung*/
- length = 0;
+ usleep_range(1000, 2000);
+ length = 0;
+ if (!is_header_read)
continue;
- } else if (length <= 0) {
- cERROR(1, "Received no data, expecting %d",
- pdu_length - total_read);
- cifs_reconnect(server);
- csocket = server->ssocket;
- reconnect = 1;
- break;
- }
- }
- if (reconnect == 2)
+ /* Special handling for header read */
+ if (total_read) {
+ iov->iov_base = (to_read - total_read) +
+ buf;
+ iov->iov_len = to_read - total_read;
+ smb_msg->msg_control = NULL;
+ smb_msg->msg_controllen = 0;
+ rc = 3;
+ } else
+ rc = 1;
break;
- else if (reconnect == 1)
- continue;
+ } else if (length <= 0) {
+ cERROR(1, "Received no data, expecting %d",
+ to_read - total_read);
+ cifs_reconnect(server);
+ rc = 1;
+ break;
+ }
+ }
- total_read += 4; /* account for rfc1002 hdr */
+ *ptotal_read = total_read;
+ return rc;
+}
- dump_smb(smb_buffer, total_read);
+static bool
+check_rfc1002_header(struct TCP_Server_Info *server, char *buf)
+{
+ char temp = *buf;
+ unsigned int pdu_length = be32_to_cpu(
+ ((struct smb_hdr *)buf)->smb_buf_length);
+ /*
+ * The first byte big endian of the length field,
+ * is actually not part of the length but the type
+ * with the most common, zero, as regular data.
+ */
+ if (temp == (char) RFC1002_SESSION_KEEP_ALIVE) {
+ return false;
+ } else if (temp == (char)RFC1002_POSITIVE_SESSION_RESPONSE) {
+ cFYI(1, "Good RFC 1002 session rsp");
+ return false;
+ } else if (temp == (char)RFC1002_NEGATIVE_SESSION_RESPONSE) {
/*
- * We know that we received enough to get to the MID as we
- * checked the pdu_length earlier. Now check to see
- * if the rest of the header is OK. We borrow the length
- * var for the rest of the loop to avoid a new stack var.
- *
- * 48 bytes is enough to display the header and a little bit
- * into the payload for debugging purposes.
+ * We get this from Windows 98 instead of an error on
+ * SMB negprot response.
*/
- length = checkSMB(smb_buffer, smb_buffer->Mid, total_read);
- if (length != 0)
- cifs_dump_mem("Bad SMB: ", smb_buffer,
- min_t(unsigned int, total_read, 48));
+ cFYI(1, "Negative RFC1002 Session Response Error 0x%x)",
+ pdu_length);
+ /* give server a second to clean up */
+ msleep(1000);
+ /*
+ * Always try 445 first on reconnect since we get NACK
+ * on some if we ever connected to port 139 (the NACK
+ * is since we do not begin with RFC1001 session
+ * initialize frame).
+ */
+ cifs_set_port((struct sockaddr *)
+ &server->dstaddr, CIFS_PORT);
+ cifs_reconnect(server);
+ wake_up(&server->response_q);
+ return false;
+ } else if (temp != (char) 0) {
+ cERROR(1, "Unknown RFC 1002 frame");
+ cifs_dump_mem(" Received Data: ", buf, 4);
+ cifs_reconnect(server);
+ return false;
+ }
- mid_entry = NULL;
- server->lstrp = jiffies;
+ /* else we have an SMB response */
+ if ((pdu_length > CIFSMaxBufSize + MAX_CIFS_HDR_SIZE - 4) ||
+ (pdu_length < sizeof(struct smb_hdr) - 1 - 4)) {
+ cERROR(1, "Invalid size SMB length %d pdu_length %d",
+ 4, pdu_length+4);
+ cifs_reconnect(server);
+ wake_up(&server->response_q);
+ return false;
+ }
- spin_lock(&GlobalMid_Lock);
- list_for_each_safe(tmp, tmp2, &server->pending_mid_q) {
- mid_entry = list_entry(tmp, struct mid_q_entry, qhead);
+ return true;
+}
- if (mid_entry->mid != smb_buffer->Mid ||
- mid_entry->midState != MID_REQUEST_SUBMITTED ||
- mid_entry->command != smb_buffer->Command) {
- mid_entry = NULL;
- continue;
- }
+static struct mid_q_entry *
+find_cifs_mid(struct TCP_Server_Info *server, struct smb_hdr *buf,
+ int *length, bool is_large_buf, bool *is_multi_rsp, char **bigbuf)
+{
+ struct mid_q_entry *mid = NULL, *tmp_mid, *ret = NULL;
- if (length == 0 &&
- check2ndT2(smb_buffer, server->maxBuf) > 0) {
- /* We have a multipart transact2 resp */
- isMultiRsp = true;
- if (mid_entry->resp_buf) {
- /* merge response - fix up 1st*/
- length = coalesce_t2(smb_buffer,
- mid_entry->resp_buf);
- if (length > 0) {
- length = 0;
- mid_entry->multiRsp = true;
- break;
- } else {
- /* all parts received or
- * packet is malformed
- */
- mid_entry->multiEnd = true;
- goto multi_t2_fnd;
- }
- } else {
- if (!isLargeBuf) {
- /*
- * FIXME: switch to already
- * allocated largebuf?
- */
- cERROR(1, "1st trans2 resp "
- "needs bigbuf");
- } else {
- /* Have first buffer */
- mid_entry->resp_buf =
- smb_buffer;
- mid_entry->largeBuf = true;
- bigbuf = NULL;
- }
+ spin_lock(&GlobalMid_Lock);
+ list_for_each_entry_safe(mid, tmp_mid, &server->pending_mid_q, qhead) {
+ if (mid->mid != buf->Mid ||
+ mid->midState != MID_REQUEST_SUBMITTED ||
+ mid->command != buf->Command)
+ continue;
+
+ if (*length == 0 && check2ndT2(buf, server->maxBuf) > 0) {
+ /* We have a multipart transact2 resp */
+ *is_multi_rsp = true;
+ if (mid->resp_buf) {
+ /* merge response - fix up 1st*/
+ *length = coalesce_t2(buf, mid->resp_buf);
+ if (*length > 0) {
+ *length = 0;
+ mid->multiRsp = true;
+ break;
}
- break;
+ /* All parts received or packet is malformed. */
+ mid->multiEnd = true;
+ goto multi_t2_fnd;
}
- mid_entry->resp_buf = smb_buffer;
- mid_entry->largeBuf = isLargeBuf;
-multi_t2_fnd:
- if (length == 0)
- mid_entry->midState = MID_RESPONSE_RECEIVED;
- else
- mid_entry->midState = MID_RESPONSE_MALFORMED;
-#ifdef CONFIG_CIFS_STATS2
- mid_entry->when_received = jiffies;
-#endif
- list_del_init(&mid_entry->qhead);
+ if (!is_large_buf) {
+ /*FIXME: switch to already allocated largebuf?*/
+ cERROR(1, "1st trans2 resp needs bigbuf");
+ } else {
+ /* Have first buffer */
+ mid->resp_buf = buf;
+ mid->largeBuf = true;
+ *bigbuf = NULL;
+ }
break;
}
- spin_unlock(&GlobalMid_Lock);
+ mid->resp_buf = buf;
+ mid->largeBuf = is_large_buf;
+multi_t2_fnd:
+ if (*length == 0)
+ mid->midState = MID_RESPONSE_RECEIVED;
+ else
+ mid->midState = MID_RESPONSE_MALFORMED;
+#ifdef CONFIG_CIFS_STATS2
+ mid->when_received = jiffies;
+#endif
+ list_del_init(&mid->qhead);
+ ret = mid;
+ break;
+ }
+ spin_unlock(&GlobalMid_Lock);
- if (mid_entry != NULL) {
- mid_entry->callback(mid_entry);
- /* Was previous buf put in mpx struct for multi-rsp? */
- if (!isMultiRsp) {
- /* smb buffer will be freed by user thread */
- if (isLargeBuf)
- bigbuf = NULL;
- else
- smallbuf = NULL;
- }
- } else if (length != 0) {
- /* response sanity checks failed */
- continue;
- } else if (!is_valid_oplock_break(smb_buffer, server) &&
- !isMultiRsp) {
- cERROR(1, "No task to wake, unknown frame received! "
- "NumMids %d", atomic_read(&midCount));
- cifs_dump_mem("Received Data is: ", (char *)smb_buffer,
- sizeof(struct smb_hdr));
-#ifdef CONFIG_CIFS_DEBUG2
- cifs_dump_detail(smb_buffer);
- cifs_dump_mids(server);
-#endif /* CIFS_DEBUG2 */
+ return ret;
+}
- }
- } /* end while !EXITING */
+static void clean_demultiplex_info(struct TCP_Server_Info *server)
+{
+ int length;
/* take it off the list, if it's not already */
spin_lock(&cifs_tcp_ses_lock);
@@ -668,35 +545,39 @@
spin_unlock(&GlobalMid_Lock);
wake_up_all(&server->response_q);
- /* check if we have blocked requests that need to free */
- /* Note that cifs_max_pending is normally 50, but
- can be set at module install time to as little as two */
+ /*
+ * Check if we have blocked requests that need to free. Note that
+ * cifs_max_pending is normally 50, but can be set at module install
+ * time to as little as two.
+ */
spin_lock(&GlobalMid_Lock);
if (atomic_read(&server->inFlight) >= cifs_max_pending)
atomic_set(&server->inFlight, cifs_max_pending - 1);
- /* We do not want to set the max_pending too low or we
- could end up with the counter going negative */
+ /*
+ * We do not want to set the max_pending too low or we could end up
+ * with the counter going negative.
+ */
spin_unlock(&GlobalMid_Lock);
- /* Although there should not be any requests blocked on
- this queue it can not hurt to be paranoid and try to wake up requests
- that may haven been blocked when more than 50 at time were on the wire
- to the same server - they now will see the session is in exit state
- and get out of SendReceive. */
+ /*
+ * Although there should not be any requests blocked on this queue it
+ * can not hurt to be paranoid and try to wake up requests that may
+ * haven been blocked when more than 50 at time were on the wire to the
+ * same server - they now will see the session is in exit state and get
+ * out of SendReceive.
+ */
wake_up_all(&server->request_q);
/* give those requests time to exit */
msleep(125);
if (server->ssocket) {
- sock_release(csocket);
+ sock_release(server->ssocket);
server->ssocket = NULL;
}
- /* buffer usually freed in free_mid - need to free it here on exit */
- cifs_buf_release(bigbuf);
- if (smallbuf) /* no sense logging a debug message if NULL */
- cifs_small_buf_release(smallbuf);
if (!list_empty(&server->pending_mid_q)) {
struct list_head dispose_list;
+ struct mid_q_entry *mid_entry;
+ struct list_head *tmp, *tmp2;
INIT_LIST_HEAD(&dispose_list);
spin_lock(&GlobalMid_Lock);
@@ -720,26 +601,184 @@
}
if (!list_empty(&server->pending_mid_q)) {
- /* mpx threads have not exited yet give them
- at least the smb send timeout time for long ops */
- /* due to delays on oplock break requests, we need
- to wait at least 45 seconds before giving up
- on a request getting a response and going ahead
- and killing cifsd */
+ /*
+ * mpx threads have not exited yet give them at least the smb
+ * send timeout time for long ops.
+ *
+ * Due to delays on oplock break requests, we need to wait at
+ * least 45 seconds before giving up on a request getting a
+ * response and going ahead and killing cifsd.
+ */
cFYI(1, "Wait for exit from demultiplex thread");
msleep(46000);
- /* if threads still have not exited they are probably never
- coming home not much else we can do but free the memory */
+ /*
+ * If threads still have not exited they are probably never
+ * coming home not much else we can do but free the memory.
+ */
}
kfree(server->hostname);
- task_to_wake = xchg(&server->tsk, NULL);
kfree(server);
length = atomic_dec_return(&tcpSesAllocCount);
- if (length > 0)
+ if (length > 0)
mempool_resize(cifs_req_poolp, length + cifs_min_rcv,
GFP_KERNEL);
+}
+
+static int
+cifs_demultiplex_thread(void *p)
+{
+ int length;
+ struct TCP_Server_Info *server = p;
+ unsigned int pdu_length, total_read;
+ char *buf = NULL, *bigbuf = NULL, *smallbuf = NULL;
+ struct smb_hdr *smb_buffer = NULL;
+ struct msghdr smb_msg;
+ struct kvec iov;
+ struct task_struct *task_to_wake = NULL;
+ struct mid_q_entry *mid_entry;
+ bool isLargeBuf = false;
+ bool isMultiRsp = false;
+ int rc;
+
+ current->flags |= PF_MEMALLOC;
+ cFYI(1, "Demultiplex PID: %d", task_pid_nr(current));
+
+ length = atomic_inc_return(&tcpSesAllocCount);
+ if (length > 1)
+ mempool_resize(cifs_req_poolp, length + cifs_min_rcv,
+ GFP_KERNEL);
+
+ set_freezable();
+ while (server->tcpStatus != CifsExiting) {
+ if (try_to_freeze())
+ continue;
+
+ if (!allocate_buffers(&bigbuf, &smallbuf,
+ sizeof(struct smb_hdr), isLargeBuf))
+ continue;
+
+ isLargeBuf = false;
+ isMultiRsp = false;
+ smb_buffer = (struct smb_hdr *)smallbuf;
+ buf = smallbuf;
+ iov.iov_base = buf;
+ iov.iov_len = 4;
+ smb_msg.msg_control = NULL;
+ smb_msg.msg_controllen = 0;
+ pdu_length = 4; /* enough to get RFC1001 header */
+
+incomplete_rcv:
+ if (echo_retries > 0 && server->tcpStatus == CifsGood &&
+ time_after(jiffies, server->lstrp +
+ (echo_retries * SMB_ECHO_INTERVAL))) {
+ cERROR(1, "Server %s has not responded in %d seconds. "
+ "Reconnecting...", server->hostname,
+ (echo_retries * SMB_ECHO_INTERVAL / HZ));
+ cifs_reconnect(server);
+ wake_up(&server->response_q);
+ continue;
+ }
+
+ rc = read_from_socket(server, &smb_msg, &iov, pdu_length,
+ &total_read, true /* header read */);
+ if (rc == 3)
+ goto incomplete_rcv;
+ else if (rc == 2)
+ break;
+ else if (rc == 1)
+ continue;
+
+ /*
+ * The right amount was read from socket - 4 bytes,
+ * so we can now interpret the length field.
+ */
+
+ /*
+ * Note that RFC 1001 length is big endian on the wire,
+ * but we convert it here so it is always manipulated
+ * as host byte order.
+ */
+ pdu_length = be32_to_cpu(smb_buffer->smb_buf_length);
+
+ cFYI(1, "rfc1002 length 0x%x", pdu_length+4);
+ if (!check_rfc1002_header(server, buf))
+ continue;
+
+ /* else length ok */
+ if (pdu_length > MAX_CIFS_SMALL_BUFFER_SIZE - 4) {
+ isLargeBuf = true;
+ memcpy(bigbuf, smallbuf, 4);
+ smb_buffer = (struct smb_hdr *)bigbuf;
+ buf = bigbuf;
+ }
+
+ iov.iov_base = 4 + buf;
+ iov.iov_len = pdu_length;
+ rc = read_from_socket(server, &smb_msg, &iov, pdu_length,
+ &total_read, false);
+ if (rc == 2)
+ break;
+ else if (rc == 1)
+ continue;
+
+ total_read += 4; /* account for rfc1002 hdr */
+
+ dump_smb(smb_buffer, total_read);
+
+ /*
+ * We know that we received enough to get to the MID as we
+ * checked the pdu_length earlier. Now check to see
+ * if the rest of the header is OK. We borrow the length
+ * var for the rest of the loop to avoid a new stack var.
+ *
+ * 48 bytes is enough to display the header and a little bit
+ * into the payload for debugging purposes.
+ */
+ length = checkSMB(smb_buffer, smb_buffer->Mid, total_read);
+ if (length != 0)
+ cifs_dump_mem("Bad SMB: ", buf,
+ min_t(unsigned int, total_read, 48));
+
+ server->lstrp = jiffies;
+
+ mid_entry = find_cifs_mid(server, smb_buffer, &length,
+ isLargeBuf, &isMultiRsp, &bigbuf);
+ if (mid_entry != NULL) {
+ mid_entry->callback(mid_entry);
+ /* Was previous buf put in mpx struct for multi-rsp? */
+ if (!isMultiRsp) {
+ /* smb buffer will be freed by user thread */
+ if (isLargeBuf)
+ bigbuf = NULL;
+ else
+ smallbuf = NULL;
+ }
+ } else if (length != 0) {
+ /* response sanity checks failed */
+ continue;
+ } else if (!is_valid_oplock_break(smb_buffer, server) &&
+ !isMultiRsp) {
+ cERROR(1, "No task to wake, unknown frame received! "
+ "NumMids %d", atomic_read(&midCount));
+ cifs_dump_mem("Received Data is: ", buf,
+ sizeof(struct smb_hdr));
+#ifdef CONFIG_CIFS_DEBUG2
+ cifs_dump_detail(smb_buffer);
+ cifs_dump_mids(server);
+#endif /* CIFS_DEBUG2 */
+
+ }
+ } /* end while !EXITING */
+
+ /* buffer usually freed in free_mid - need to free it here on exit */
+ cifs_buf_release(bigbuf);
+ if (smallbuf) /* no sense logging a debug message if NULL */
+ cifs_small_buf_release(smallbuf);
+
+ task_to_wake = xchg(&server->tsk, NULL);
+ clean_demultiplex_info(server);
/* if server->tsk was NULL then wait for a signal before exiting */
if (!task_to_wake) {
@@ -2839,7 +2878,8 @@
kfree(volume_info->username);
kzfree(volume_info->password);
kfree(volume_info->UNC);
- kfree(volume_info->UNCip);
+ if (volume_info->UNCip != volume_info->UNC + 2)
+ kfree(volume_info->UNCip);
kfree(volume_info->domainname);
kfree(volume_info->iocharset);
kfree(volume_info->prepath);
@@ -3193,15 +3233,9 @@
else
cifs_put_tcp_session(srvTcp);
bdi_destroy(&cifs_sb->bdi);
- goto out;
}
- /* volume_info->password is freed above when existing session found
- (in which case it is not needed anymore) but when new sesion is created
- the password ptr is put in the new session structure (in which case the
- password will be freed at unmount time) */
out:
- /* zero out password before freeing */
FreeXid(xid);
return rc;
}
diff --git a/fs/cifs/dir.c b/fs/cifs/dir.c
index ae576fb..72d448b 100644
--- a/fs/cifs/dir.c
+++ b/fs/cifs/dir.c
@@ -105,8 +105,8 @@
}
rcu_read_unlock();
if (namelen != dfsplen || read_seqretry(&rename_lock, seq)) {
- cERROR(1, "did not end path lookup where expected namelen is %d",
- namelen);
+ cFYI(1, "did not end path lookup where expected. namelen=%d "
+ "dfsplen=%d", namelen, dfsplen);
/* presumably this is only possible if racing with a rename
of one of the parent directories (we can not lock the dentries
above us to prevent this, but retrying should be harmless) */
diff --git a/fs/cifs/dns_resolve.c b/fs/cifs/dns_resolve.c
index 548f062..1d2d91d 100644
--- a/fs/cifs/dns_resolve.c
+++ b/fs/cifs/dns_resolve.c
@@ -79,8 +79,8 @@
/* Perform the upcall */
rc = dns_query(NULL, hostname, len, NULL, ip_addr, NULL);
if (rc < 0)
- cERROR(1, "%s: unable to resolve: %*.*s",
- __func__, len, len, hostname);
+ cFYI(1, "%s: unable to resolve: %*.*s",
+ __func__, len, len, hostname);
else
cFYI(1, "%s: resolved: %*.*s to %s",
__func__, len, len, hostname, *ip_addr);
diff --git a/fs/cifs/file.c b/fs/cifs/file.c
index 378acda..9f41a10 100644
--- a/fs/cifs/file.c
+++ b/fs/cifs/file.c
@@ -314,6 +314,8 @@
}
spin_unlock(&cifs_file_list_lock);
+ cancel_work_sync(&cifs_file->oplock_break);
+
if (!tcon->need_reconnect && !cifs_file->invalidHandle) {
int xid, rc;
@@ -2418,31 +2420,6 @@
cinode->clientCanCacheRead ? 1 : 0);
cFYI(1, "Oplock release rc = %d", rc);
}
-
- /*
- * We might have kicked in before is_valid_oplock_break()
- * finished grabbing reference for us. Make sure it's done by
- * waiting for cifs_file_list_lock.
- */
- spin_lock(&cifs_file_list_lock);
- spin_unlock(&cifs_file_list_lock);
-
- cifs_oplock_break_put(cfile);
-}
-
-/* must be called while holding cifs_file_list_lock */
-void cifs_oplock_break_get(struct cifsFileInfo *cfile)
-{
- cifs_sb_active(cfile->dentry->d_sb);
- cifsFileInfo_get(cfile);
-}
-
-void cifs_oplock_break_put(struct cifsFileInfo *cfile)
-{
- struct super_block *sb = cfile->dentry->d_sb;
-
- cifsFileInfo_put(cfile);
- cifs_sb_deactive(sb);
}
const struct address_space_operations cifs_addr_ops = {
diff --git a/fs/cifs/inode.c b/fs/cifs/inode.c
index 9b018c8..a7b2dcd 100644
--- a/fs/cifs/inode.c
+++ b/fs/cifs/inode.c
@@ -764,20 +764,10 @@
if (full_path == NULL)
return full_path;
- if (dfsplen) {
+ if (dfsplen)
strncpy(full_path, tcon->treeName, dfsplen);
- /* switch slash direction in prepath depending on whether
- * windows or posix style path names
- */
- if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_POSIX_PATHS) {
- int i;
- for (i = 0; i < dfsplen; i++) {
- if (full_path[i] == '\\')
- full_path[i] = '/';
- }
- }
- }
strncpy(full_path + dfsplen, vol->prepath, pplen);
+ convert_delimiter(full_path, CIFS_DIR_SEP(cifs_sb));
full_path[dfsplen + pplen] = 0; /* add trailing null */
return full_path;
}
diff --git a/fs/cifs/misc.c b/fs/cifs/misc.c
index 03a1f49..7c16933 100644
--- a/fs/cifs/misc.c
+++ b/fs/cifs/misc.c
@@ -585,15 +585,8 @@
cifs_set_oplock_level(pCifsInode,
pSMB->OplockLevel ? OPLOCK_READ : 0);
- /*
- * cifs_oplock_break_put() can't be called
- * from here. Get reference after queueing
- * succeeded. cifs_oplock_break() will
- * synchronize using cifs_file_list_lock.
- */
- if (queue_work(system_nrt_wq,
- &netfile->oplock_break))
- cifs_oplock_break_get(netfile);
+ queue_work(system_nrt_wq,
+ &netfile->oplock_break);
netfile->oplock_break_cancelled = false;
spin_unlock(&cifs_file_list_lock);
diff --git a/fs/cifs/transport.c b/fs/cifs/transport.c
index 147aa22..10ca6b2 100644
--- a/fs/cifs/transport.c
+++ b/fs/cifs/transport.c
@@ -266,15 +266,11 @@
while (1) {
if (atomic_read(&server->inFlight) >= cifs_max_pending) {
spin_unlock(&GlobalMid_Lock);
-#ifdef CONFIG_CIFS_STATS2
- atomic_inc(&server->num_waiters);
-#endif
+ cifs_num_waiters_inc(server);
wait_event(server->request_q,
atomic_read(&server->inFlight)
< cifs_max_pending);
-#ifdef CONFIG_CIFS_STATS2
- atomic_dec(&server->num_waiters);
-#endif
+ cifs_num_waiters_dec(server);
spin_lock(&GlobalMid_Lock);
} else {
if (server->tcpStatus == CifsExiting) {
@@ -362,6 +358,8 @@
mid = AllocMidQEntry(hdr, server);
if (mid == NULL) {
mutex_unlock(&server->srv_mutex);
+ atomic_dec(&server->inFlight);
+ wake_up(&server->request_q);
return -ENOMEM;
}
@@ -379,15 +377,13 @@
mid->callback = callback;
mid->callback_data = cbdata;
mid->midState = MID_REQUEST_SUBMITTED;
-#ifdef CONFIG_CIFS_STATS2
- atomic_inc(&server->inSend);
-#endif
+
+ cifs_in_send_inc(server);
rc = smb_sendv(server, iov, nvec);
-#ifdef CONFIG_CIFS_STATS2
- atomic_dec(&server->inSend);
- mid->when_sent = jiffies;
-#endif
+ cifs_in_send_dec(server);
+ cifs_save_when_sent(mid);
mutex_unlock(&server->srv_mutex);
+
if (rc)
goto out_err;
@@ -573,14 +569,10 @@
}
midQ->midState = MID_REQUEST_SUBMITTED;
-#ifdef CONFIG_CIFS_STATS2
- atomic_inc(&ses->server->inSend);
-#endif
+ cifs_in_send_inc(ses->server);
rc = smb_sendv(ses->server, iov, n_vec);
-#ifdef CONFIG_CIFS_STATS2
- atomic_dec(&ses->server->inSend);
- midQ->when_sent = jiffies;
-#endif
+ cifs_in_send_dec(ses->server);
+ cifs_save_when_sent(midQ);
mutex_unlock(&ses->server->srv_mutex);
@@ -701,14 +693,11 @@
}
midQ->midState = MID_REQUEST_SUBMITTED;
-#ifdef CONFIG_CIFS_STATS2
- atomic_inc(&ses->server->inSend);
-#endif
+
+ cifs_in_send_inc(ses->server);
rc = smb_send(ses->server, in_buf, be32_to_cpu(in_buf->smb_buf_length));
-#ifdef CONFIG_CIFS_STATS2
- atomic_dec(&ses->server->inSend);
- midQ->when_sent = jiffies;
-#endif
+ cifs_in_send_dec(ses->server);
+ cifs_save_when_sent(midQ);
mutex_unlock(&ses->server->srv_mutex);
if (rc < 0)
@@ -841,14 +830,10 @@
}
midQ->midState = MID_REQUEST_SUBMITTED;
-#ifdef CONFIG_CIFS_STATS2
- atomic_inc(&ses->server->inSend);
-#endif
+ cifs_in_send_inc(ses->server);
rc = smb_send(ses->server, in_buf, be32_to_cpu(in_buf->smb_buf_length));
-#ifdef CONFIG_CIFS_STATS2
- atomic_dec(&ses->server->inSend);
- midQ->when_sent = jiffies;
-#endif
+ cifs_in_send_dec(ses->server);
+ cifs_save_when_sent(midQ);
mutex_unlock(&ses->server->srv_mutex);
if (rc < 0) {
diff --git a/fs/compat_ioctl.c b/fs/compat_ioctl.c
index 8be086e..51352de 100644
--- a/fs/compat_ioctl.c
+++ b/fs/compat_ioctl.c
@@ -1003,6 +1003,7 @@
COMPATIBLE_IOCTL(PPPIOCDISCONN)
COMPATIBLE_IOCTL(PPPIOCATTCHAN)
COMPATIBLE_IOCTL(PPPIOCGCHAN)
+COMPATIBLE_IOCTL(PPPIOCGL2TPSTATS)
/* PPPOX */
COMPATIBLE_IOCTL(PPPOEIOCSFWD)
COMPATIBLE_IOCTL(PPPOEIOCDFWD)
diff --git a/fs/dcache.c b/fs/dcache.c
index b05aac3..a88948b 100644
--- a/fs/dcache.c
+++ b/fs/dcache.c
@@ -301,6 +301,27 @@
return parent;
}
+/*
+ * Unhash a dentry without inserting an RCU walk barrier or checking that
+ * dentry->d_lock is locked. The caller must take care of that, if
+ * appropriate.
+ */
+static void __d_shrink(struct dentry *dentry)
+{
+ if (!d_unhashed(dentry)) {
+ struct hlist_bl_head *b;
+ if (unlikely(dentry->d_flags & DCACHE_DISCONNECTED))
+ b = &dentry->d_sb->s_anon;
+ else
+ b = d_hash(dentry->d_parent, dentry->d_name.hash);
+
+ hlist_bl_lock(b);
+ __hlist_bl_del(&dentry->d_hash);
+ dentry->d_hash.pprev = NULL;
+ hlist_bl_unlock(b);
+ }
+}
+
/**
* d_drop - drop a dentry
* @dentry: dentry to drop
@@ -319,17 +340,7 @@
void __d_drop(struct dentry *dentry)
{
if (!d_unhashed(dentry)) {
- struct hlist_bl_head *b;
- if (unlikely(dentry->d_flags & DCACHE_DISCONNECTED))
- b = &dentry->d_sb->s_anon;
- else
- b = d_hash(dentry->d_parent, dentry->d_name.hash);
-
- hlist_bl_lock(b);
- __hlist_bl_del(&dentry->d_hash);
- dentry->d_hash.pprev = NULL;
- hlist_bl_unlock(b);
-
+ __d_shrink(dentry);
dentry_rcuwalk_barrier(dentry);
}
}
@@ -784,6 +795,7 @@
/**
* prune_dcache_sb - shrink the dcache
+ * @sb: superblock
* @nr_to_scan: number of entries to try to free
*
* Attempt to shrink the superblock dcache LRU by @nr_to_scan entries. This is
@@ -828,44 +840,24 @@
static void shrink_dcache_for_umount_subtree(struct dentry *dentry)
{
struct dentry *parent;
- unsigned detached = 0;
BUG_ON(!IS_ROOT(dentry));
- /* detach this root from the system */
- spin_lock(&dentry->d_lock);
- dentry_lru_del(dentry);
- __d_drop(dentry);
- spin_unlock(&dentry->d_lock);
-
for (;;) {
/* descend to the first leaf in the current subtree */
- while (!list_empty(&dentry->d_subdirs)) {
- struct dentry *loop;
-
- /* this is a branch with children - detach all of them
- * from the system in one go */
- spin_lock(&dentry->d_lock);
- list_for_each_entry(loop, &dentry->d_subdirs,
- d_u.d_child) {
- spin_lock_nested(&loop->d_lock,
- DENTRY_D_LOCK_NESTED);
- dentry_lru_del(loop);
- __d_drop(loop);
- spin_unlock(&loop->d_lock);
- }
- spin_unlock(&dentry->d_lock);
-
- /* move to the first child */
+ while (!list_empty(&dentry->d_subdirs))
dentry = list_entry(dentry->d_subdirs.next,
struct dentry, d_u.d_child);
- }
/* consume the dentries from this leaf up through its parents
* until we find one with children or run out altogether */
do {
struct inode *inode;
+ /* detach from the system */
+ dentry_lru_del(dentry);
+ __d_shrink(dentry);
+
if (dentry->d_count != 0) {
printk(KERN_ERR
"BUG: Dentry %p{i=%lx,n=%s}"
@@ -886,14 +878,10 @@
list_del(&dentry->d_u.d_child);
} else {
parent = dentry->d_parent;
- spin_lock(&parent->d_lock);
parent->d_count--;
list_del(&dentry->d_u.d_child);
- spin_unlock(&parent->d_lock);
}
- detached++;
-
inode = dentry->d_inode;
if (inode) {
dentry->d_inode = NULL;
@@ -938,9 +926,7 @@
dentry = sb->s_root;
sb->s_root = NULL;
- spin_lock(&dentry->d_lock);
dentry->d_count--;
- spin_unlock(&dentry->d_lock);
shrink_dcache_for_umount_subtree(dentry);
while (!hlist_bl_empty(&sb->s_anon)) {
@@ -1743,7 +1729,7 @@
*/
if (read_seqcount_retry(&dentry->d_seq, *seq))
goto seqretry;
- if (parent->d_flags & DCACHE_OP_COMPARE) {
+ if (unlikely(parent->d_flags & DCACHE_OP_COMPARE)) {
if (parent->d_op->d_compare(parent, *inode,
dentry, i,
tlen, tname, name))
diff --git a/fs/ecryptfs/Kconfig b/fs/ecryptfs/Kconfig
index 1cd6d9d..cc16562 100644
--- a/fs/ecryptfs/Kconfig
+++ b/fs/ecryptfs/Kconfig
@@ -1,6 +1,6 @@
config ECRYPT_FS
tristate "eCrypt filesystem layer support (EXPERIMENTAL)"
- depends on EXPERIMENTAL && KEYS && CRYPTO
+ depends on EXPERIMENTAL && KEYS && CRYPTO && (ENCRYPTED_KEYS || ENCRYPTED_KEYS=n)
select CRYPTO_ECB
select CRYPTO_CBC
select CRYPTO_MD5
diff --git a/fs/ecryptfs/keystore.c b/fs/ecryptfs/keystore.c
index 08a2b52..ac1ad48 100644
--- a/fs/ecryptfs/keystore.c
+++ b/fs/ecryptfs/keystore.c
@@ -1973,7 +1973,7 @@
{
struct ecryptfs_msg_ctx *msg_ctx = NULL;
char *payload = NULL;
- size_t payload_len;
+ size_t payload_len = 0;
struct ecryptfs_message *msg;
int rc;
diff --git a/fs/ecryptfs/main.c b/fs/ecryptfs/main.c
index 9f1bb74..b4a6bef 100644
--- a/fs/ecryptfs/main.c
+++ b/fs/ecryptfs/main.c
@@ -175,6 +175,7 @@
ecryptfs_opt_encrypted_view, ecryptfs_opt_fnek_sig,
ecryptfs_opt_fn_cipher, ecryptfs_opt_fn_cipher_key_bytes,
ecryptfs_opt_unlink_sigs, ecryptfs_opt_mount_auth_tok_only,
+ ecryptfs_opt_check_dev_ruid,
ecryptfs_opt_err };
static const match_table_t tokens = {
@@ -191,6 +192,7 @@
{ecryptfs_opt_fn_cipher_key_bytes, "ecryptfs_fn_key_bytes=%u"},
{ecryptfs_opt_unlink_sigs, "ecryptfs_unlink_sigs"},
{ecryptfs_opt_mount_auth_tok_only, "ecryptfs_mount_auth_tok_only"},
+ {ecryptfs_opt_check_dev_ruid, "ecryptfs_check_dev_ruid"},
{ecryptfs_opt_err, NULL}
};
@@ -236,6 +238,7 @@
* ecryptfs_parse_options
* @sb: The ecryptfs super block
* @options: The options passed to the kernel
+ * @check_ruid: set to 1 if device uid should be checked against the ruid
*
* Parse mount options:
* debug=N - ecryptfs_verbosity level for debug output
@@ -251,7 +254,8 @@
*
* Returns zero on success; non-zero on error
*/
-static int ecryptfs_parse_options(struct ecryptfs_sb_info *sbi, char *options)
+static int ecryptfs_parse_options(struct ecryptfs_sb_info *sbi, char *options,
+ uid_t *check_ruid)
{
char *p;
int rc = 0;
@@ -276,6 +280,8 @@
char *cipher_key_bytes_src;
char *fn_cipher_key_bytes_src;
+ *check_ruid = 0;
+
if (!options) {
rc = -EINVAL;
goto out;
@@ -380,6 +386,9 @@
mount_crypt_stat->flags |=
ECRYPTFS_GLOBAL_MOUNT_AUTH_TOK_ONLY;
break;
+ case ecryptfs_opt_check_dev_ruid:
+ *check_ruid = 1;
+ break;
case ecryptfs_opt_err:
default:
printk(KERN_WARNING
@@ -475,6 +484,7 @@
const char *err = "Getting sb failed";
struct inode *inode;
struct path path;
+ uid_t check_ruid;
int rc;
sbi = kmem_cache_zalloc(ecryptfs_sb_info_cache, GFP_KERNEL);
@@ -483,7 +493,7 @@
goto out;
}
- rc = ecryptfs_parse_options(sbi, raw_data);
+ rc = ecryptfs_parse_options(sbi, raw_data, &check_ruid);
if (rc) {
err = "Error parsing options";
goto out;
@@ -521,6 +531,15 @@
"known incompatibilities\n");
goto out_free;
}
+
+ if (check_ruid && path.dentry->d_inode->i_uid != current_uid()) {
+ rc = -EPERM;
+ printk(KERN_ERR "Mount of device (uid: %d) not owned by "
+ "requested user (uid: %d)\n",
+ path.dentry->d_inode->i_uid, current_uid());
+ goto out_free;
+ }
+
ecryptfs_set_superblock_lower(s, path.dentry->d_sb);
s->s_maxbytes = path.dentry->d_sb->s_maxbytes;
s->s_blocksize = path.dentry->d_sb->s_blocksize;
diff --git a/fs/ecryptfs/read_write.c b/fs/ecryptfs/read_write.c
index 85d4309..3745f7c 100644
--- a/fs/ecryptfs/read_write.c
+++ b/fs/ecryptfs/read_write.c
@@ -39,15 +39,16 @@
int ecryptfs_write_lower(struct inode *ecryptfs_inode, char *data,
loff_t offset, size_t size)
{
- struct ecryptfs_inode_info *inode_info;
+ struct file *lower_file;
mm_segment_t fs_save;
ssize_t rc;
- inode_info = ecryptfs_inode_to_private(ecryptfs_inode);
- BUG_ON(!inode_info->lower_file);
+ lower_file = ecryptfs_inode_to_private(ecryptfs_inode)->lower_file;
+ if (!lower_file)
+ return -EIO;
fs_save = get_fs();
set_fs(get_ds());
- rc = vfs_write(inode_info->lower_file, data, size, &offset);
+ rc = vfs_write(lower_file, data, size, &offset);
set_fs(fs_save);
mark_inode_dirty_sync(ecryptfs_inode);
return rc;
@@ -225,15 +226,16 @@
int ecryptfs_read_lower(char *data, loff_t offset, size_t size,
struct inode *ecryptfs_inode)
{
- struct ecryptfs_inode_info *inode_info =
- ecryptfs_inode_to_private(ecryptfs_inode);
+ struct file *lower_file;
mm_segment_t fs_save;
ssize_t rc;
- BUG_ON(!inode_info->lower_file);
+ lower_file = ecryptfs_inode_to_private(ecryptfs_inode)->lower_file;
+ if (!lower_file)
+ return -EIO;
fs_save = get_fs();
set_fs(get_ds());
- rc = vfs_read(inode_info->lower_file, data, size, &offset);
+ rc = vfs_read(lower_file, data, size, &offset);
set_fs(fs_save);
return rc;
}
diff --git a/fs/exec.c b/fs/exec.c
index da80612..25dcbe5 100644
--- a/fs/exec.c
+++ b/fs/exec.c
@@ -1459,6 +1459,23 @@
struct files_struct *displaced;
bool clear_in_exec;
int retval;
+ const struct cred *cred = current_cred();
+
+ /*
+ * We move the actual failure in case of RLIMIT_NPROC excess from
+ * set*uid() to execve() because too many poorly written programs
+ * don't check setuid() return code. Here we additionally recheck
+ * whether NPROC limit is still exceeded.
+ */
+ if ((current->flags & PF_NPROC_EXCEEDED) &&
+ atomic_read(&cred->user->processes) > rlimit(RLIMIT_NPROC)) {
+ retval = -EAGAIN;
+ goto out_ret;
+ }
+
+ /* We're below the limit (still or again), so we don't want to make
+ * further execve() calls fail. */
+ current->flags &= ~PF_NPROC_EXCEEDED;
retval = unshare_files(&displaced);
if (retval)
diff --git a/fs/exofs/Kbuild b/fs/exofs/Kbuild
index 2d0f757..c5a5855 100644
--- a/fs/exofs/Kbuild
+++ b/fs/exofs/Kbuild
@@ -12,5 +12,8 @@
# Kbuild - Gets included from the Kernels Makefile and build system
#
-exofs-y := ios.o inode.o file.o symlink.o namei.o dir.o super.o
+# ore module library
+obj-$(CONFIG_ORE) += ore.o
+
+exofs-y := inode.o file.o symlink.o namei.o dir.o super.o
obj-$(CONFIG_EXOFS_FS) += exofs.o
diff --git a/fs/exofs/Kconfig b/fs/exofs/Kconfig
index 86194b2..70bae41 100644
--- a/fs/exofs/Kconfig
+++ b/fs/exofs/Kconfig
@@ -1,6 +1,10 @@
+config ORE
+ tristate
+
config EXOFS_FS
tristate "exofs: OSD based file system support"
depends on SCSI_OSD_ULD
+ select ORE
help
EXOFS is a file system that uses an OSD storage device,
as its backing storage.
diff --git a/fs/exofs/exofs.h b/fs/exofs/exofs.h
index c965806..f4e442e 100644
--- a/fs/exofs/exofs.h
+++ b/fs/exofs/exofs.h
@@ -36,12 +36,9 @@
#include <linux/fs.h>
#include <linux/time.h>
#include <linux/backing-dev.h>
-#include "common.h"
+#include <scsi/osd_ore.h>
-/* FIXME: Remove once pnfs hits mainline
- * #include <linux/exportfs/pnfs_osd_xdr.h>
- */
-#include "pnfs.h"
+#include "common.h"
#define EXOFS_ERR(fmt, a...) printk(KERN_ERR "exofs: " fmt, ##a)
@@ -56,27 +53,11 @@
/* u64 has problems with printk this will cast it to unsigned long long */
#define _LLU(x) (unsigned long long)(x)
-struct exofs_layout {
- osd_id s_pid; /* partition ID of file system*/
-
- /* Our way of looking at the data_map */
- unsigned stripe_unit;
- unsigned mirrors_p1;
-
- unsigned group_width;
- u64 group_depth;
- unsigned group_count;
-
- enum exofs_inode_layout_gen_functions lay_func;
-
- unsigned s_numdevs; /* Num of devices in array */
- struct osd_dev *s_ods[0]; /* Variable length */
-};
-
/*
* our extension to the in-memory superblock
*/
struct exofs_sb_info {
+ struct backing_dev_info bdi; /* register our bdi with VFS */
struct exofs_sb_stats s_ess; /* Written often, pre-allocate*/
int s_timeout; /* timeout for OSD operations */
uint64_t s_nextid; /* highest object ID used */
@@ -84,16 +65,13 @@
spinlock_t s_next_gen_lock; /* spinlock for gen # update */
u32 s_next_generation; /* next gen # to use */
atomic_t s_curr_pending; /* number of pending commands */
- uint8_t s_cred[OSD_CAP_LEN]; /* credential for the fscb */
- struct backing_dev_info bdi; /* register our bdi with VFS */
struct pnfs_osd_data_map data_map; /* Default raid to use
* FIXME: Needed ?
*/
-/* struct exofs_layout dir_layout;*/ /* Default dir layout */
- struct exofs_layout layout; /* Default files layout,
- * contains the variable osd_dev
- * array. Keep last */
+ struct ore_layout layout; /* Default files layout */
+ struct ore_comp one_comp; /* id & cred of partition id=0*/
+ struct ore_components comps; /* comps for the partition */
struct osd_dev *_min_one_dev[1]; /* Place holder for one dev */
};
@@ -107,7 +85,8 @@
uint32_t i_data[EXOFS_IDATA];/*short symlink names and device #s*/
uint32_t i_dir_start_lookup; /* which page to start lookup */
uint64_t i_commit_size; /* the object's written length */
- uint8_t i_cred[OSD_CAP_LEN];/* all-powerful credential */
+ struct ore_comp one_comp; /* same component for all devices */
+ struct ore_components comps; /* inode view of the device table */
};
static inline osd_id exofs_oi_objno(struct exofs_i_info *oi)
@@ -115,52 +94,6 @@
return oi->vfs_inode.i_ino + EXOFS_OBJ_OFF;
}
-struct exofs_io_state;
-typedef void (*exofs_io_done_fn)(struct exofs_io_state *or, void *private);
-
-struct exofs_io_state {
- struct kref kref;
-
- void *private;
- exofs_io_done_fn done;
-
- struct exofs_layout *layout;
- struct osd_obj_id obj;
- u8 *cred;
-
- /* Global read/write IO*/
- loff_t offset;
- unsigned long length;
- void *kern_buff;
-
- struct page **pages;
- unsigned nr_pages;
- unsigned pgbase;
- unsigned pages_consumed;
-
- /* Attributes */
- unsigned in_attr_len;
- struct osd_attr *in_attr;
- unsigned out_attr_len;
- struct osd_attr *out_attr;
-
- /* Variable array of size numdevs */
- unsigned numdevs;
- struct exofs_per_dev_state {
- struct osd_request *or;
- struct bio *bio;
- loff_t offset;
- unsigned length;
- unsigned dev;
- } per_dev[];
-};
-
-static inline unsigned exofs_io_state_size(unsigned numdevs)
-{
- return sizeof(struct exofs_io_state) +
- sizeof(struct exofs_per_dev_state) * numdevs;
-}
-
/*
* our inode flags
*/
@@ -205,12 +138,6 @@
}
/*
- * Given a layout, object_number and stripe_index return the associated global
- * dev_index
- */
-unsigned exofs_layout_od_id(struct exofs_layout *layout,
- osd_id obj_no, unsigned layout_index);
-/*
* Maximum count of links to a file
*/
#define EXOFS_LINK_MAX 32000
@@ -219,44 +146,8 @@
* function declarations *
*************************/
-/* ios.c */
-void exofs_make_credential(u8 cred_a[OSD_CAP_LEN],
- const struct osd_obj_id *obj);
-int exofs_read_kern(struct osd_dev *od, u8 *cred, struct osd_obj_id *obj,
- u64 offset, void *p, unsigned length);
-
-int exofs_get_io_state(struct exofs_layout *layout,
- struct exofs_io_state **ios);
-void exofs_put_io_state(struct exofs_io_state *ios);
-
-int exofs_check_io(struct exofs_io_state *ios, u64 *resid);
-
-int exofs_sbi_create(struct exofs_io_state *ios);
-int exofs_sbi_remove(struct exofs_io_state *ios);
-int exofs_sbi_write(struct exofs_io_state *ios);
-int exofs_sbi_read(struct exofs_io_state *ios);
-
-int extract_attr_from_ios(struct exofs_io_state *ios, struct osd_attr *attr);
-
-int exofs_oi_truncate(struct exofs_i_info *oi, u64 new_len);
-static inline int exofs_oi_write(struct exofs_i_info *oi,
- struct exofs_io_state *ios)
-{
- ios->obj.id = exofs_oi_objno(oi);
- ios->cred = oi->i_cred;
- return exofs_sbi_write(ios);
-}
-
-static inline int exofs_oi_read(struct exofs_i_info *oi,
- struct exofs_io_state *ios)
-{
- ios->obj.id = exofs_oi_objno(oi);
- ios->cred = oi->i_cred;
- return exofs_sbi_read(ios);
-}
-
/* inode.c */
-unsigned exofs_max_io_pages(struct exofs_layout *layout,
+unsigned exofs_max_io_pages(struct ore_layout *layout,
unsigned expected_pages);
int exofs_setattr(struct dentry *, struct iattr *);
int exofs_write_begin(struct file *file, struct address_space *mapping,
@@ -281,6 +172,8 @@
struct inode *);
/* super.c */
+void exofs_make_credential(u8 cred_a[OSD_CAP_LEN],
+ const struct osd_obj_id *obj);
int exofs_sbi_write_stats(struct exofs_sb_info *sbi);
/*********************
@@ -295,7 +188,6 @@
/* inode.c */
extern const struct address_space_operations exofs_aops;
-extern const struct osd_attr g_attr_logical_length;
/* namei.c */
extern const struct inode_operations exofs_dir_inode_operations;
@@ -305,4 +197,33 @@
extern const struct inode_operations exofs_symlink_inode_operations;
extern const struct inode_operations exofs_fast_symlink_inode_operations;
+/* exofs_init_comps will initialize an ore_components device array
+ * pointing to a single ore_comp struct, and a round-robin view
+ * of the device table.
+ * The first device of each inode is the [inode->ino % num_devices]
+ * and the rest of the devices sequentially following where the
+ * first device is after the last device.
+ * It is assumed that the global device array at @sbi is twice
+ * bigger and that the device table repeats twice.
+ * See: exofs_read_lookup_dev_table()
+ */
+static inline void exofs_init_comps(struct ore_components *comps,
+ struct ore_comp *one_comp,
+ struct exofs_sb_info *sbi, osd_id oid)
+{
+ unsigned dev_mod = (unsigned)oid, first_dev;
+
+ one_comp->obj.partition = sbi->one_comp.obj.partition;
+ one_comp->obj.id = oid;
+ exofs_make_credential(one_comp->cred, &one_comp->obj);
+
+ comps->numdevs = sbi->comps.numdevs;
+ comps->single_comp = EC_SINGLE_COMP;
+ comps->comps = one_comp;
+
+ /* Round robin device view of the table */
+ first_dev = (dev_mod * sbi->layout.mirrors_p1) % sbi->comps.numdevs;
+ comps->ods = sbi->comps.ods + first_dev;
+}
+
#endif
diff --git a/fs/exofs/inode.c b/fs/exofs/inode.c
index 8472c09..f39a38f 100644
--- a/fs/exofs/inode.c
+++ b/fs/exofs/inode.c
@@ -43,7 +43,7 @@
PAGE_SIZE / sizeof(struct page *),
};
-unsigned exofs_max_io_pages(struct exofs_layout *layout,
+unsigned exofs_max_io_pages(struct ore_layout *layout,
unsigned expected_pages)
{
unsigned pages = min_t(unsigned, expected_pages, MAX_PAGES_KMALLOC);
@@ -58,7 +58,7 @@
struct exofs_sb_info *sbi;
struct inode *inode;
unsigned expected_pages;
- struct exofs_io_state *ios;
+ struct ore_io_state *ios;
struct page **pages;
unsigned alloc_pages;
@@ -110,13 +110,6 @@
{
unsigned pages;
- if (!pcol->ios) { /* First time allocate io_state */
- int ret = exofs_get_io_state(&pcol->sbi->layout, &pcol->ios);
-
- if (ret)
- return ret;
- }
-
/* TODO: easily support bio chaining */
pages = exofs_max_io_pages(&pcol->sbi->layout, pcol->expected_pages);
@@ -140,7 +133,7 @@
pcol->pages = NULL;
if (pcol->ios) {
- exofs_put_io_state(pcol->ios);
+ ore_put_io_state(pcol->ios);
pcol->ios = NULL;
}
}
@@ -200,7 +193,7 @@
u64 resid;
u64 good_bytes;
u64 length = 0;
- int ret = exofs_check_io(pcol->ios, &resid);
+ int ret = ore_check_io(pcol->ios, &resid);
if (likely(!ret))
good_bytes = pcol->length;
@@ -241,7 +234,7 @@
}
/* callback of async reads */
-static void readpages_done(struct exofs_io_state *ios, void *p)
+static void readpages_done(struct ore_io_state *ios, void *p)
{
struct page_collect *pcol = p;
@@ -269,20 +262,28 @@
static int read_exec(struct page_collect *pcol)
{
struct exofs_i_info *oi = exofs_i(pcol->inode);
- struct exofs_io_state *ios = pcol->ios;
+ struct ore_io_state *ios;
struct page_collect *pcol_copy = NULL;
int ret;
if (!pcol->pages)
return 0;
+ if (!pcol->ios) {
+ int ret = ore_get_rw_state(&pcol->sbi->layout, &oi->comps, true,
+ pcol->pg_first << PAGE_CACHE_SHIFT,
+ pcol->length, &pcol->ios);
+
+ if (ret)
+ return ret;
+ }
+
+ ios = pcol->ios;
ios->pages = pcol->pages;
ios->nr_pages = pcol->nr_pages;
- ios->length = pcol->length;
- ios->offset = pcol->pg_first << PAGE_CACHE_SHIFT;
if (pcol->read_4_write) {
- exofs_oi_read(oi, pcol->ios);
+ ore_read(pcol->ios);
return __readpages_done(pcol);
}
@@ -295,14 +296,14 @@
*pcol_copy = *pcol;
ios->done = readpages_done;
ios->private = pcol_copy;
- ret = exofs_oi_read(oi, ios);
+ ret = ore_read(ios);
if (unlikely(ret))
goto err;
atomic_inc(&pcol->sbi->s_curr_pending);
EXOFS_DBGMSG2("read_exec obj=0x%llx start=0x%llx length=0x%lx\n",
- ios->obj.id, _LLU(ios->offset), pcol->length);
+ oi->one_comp.obj.id, _LLU(ios->offset), pcol->length);
/* pages ownership was passed to pcol_copy */
_pcol_reset(pcol);
@@ -457,14 +458,14 @@
}
/* Callback for osd_write. All writes are asynchronous */
-static void writepages_done(struct exofs_io_state *ios, void *p)
+static void writepages_done(struct ore_io_state *ios, void *p)
{
struct page_collect *pcol = p;
int i;
u64 resid;
u64 good_bytes;
u64 length = 0;
- int ret = exofs_check_io(ios, &resid);
+ int ret = ore_check_io(ios, &resid);
atomic_dec(&pcol->sbi->s_curr_pending);
@@ -507,13 +508,21 @@
static int write_exec(struct page_collect *pcol)
{
struct exofs_i_info *oi = exofs_i(pcol->inode);
- struct exofs_io_state *ios = pcol->ios;
+ struct ore_io_state *ios;
struct page_collect *pcol_copy = NULL;
int ret;
if (!pcol->pages)
return 0;
+ BUG_ON(pcol->ios);
+ ret = ore_get_rw_state(&pcol->sbi->layout, &oi->comps, false,
+ pcol->pg_first << PAGE_CACHE_SHIFT,
+ pcol->length, &pcol->ios);
+
+ if (unlikely(ret))
+ goto err;
+
pcol_copy = kmalloc(sizeof(*pcol_copy), GFP_KERNEL);
if (!pcol_copy) {
EXOFS_ERR("write_exec: Failed to kmalloc(pcol)\n");
@@ -523,16 +532,15 @@
*pcol_copy = *pcol;
+ ios = pcol->ios;
ios->pages = pcol_copy->pages;
ios->nr_pages = pcol_copy->nr_pages;
- ios->offset = pcol_copy->pg_first << PAGE_CACHE_SHIFT;
- ios->length = pcol_copy->length;
ios->done = writepages_done;
ios->private = pcol_copy;
- ret = exofs_oi_write(oi, ios);
+ ret = ore_write(ios);
if (unlikely(ret)) {
- EXOFS_ERR("write_exec: exofs_oi_write() Failed\n");
+ EXOFS_ERR("write_exec: ore_write() Failed\n");
goto err;
}
@@ -844,17 +852,15 @@
return S_ISLNK(inode->i_mode) && (oi->i_data[0] != 0);
}
-const struct osd_attr g_attr_logical_length = ATTR_DEF(
- OSD_APAGE_OBJECT_INFORMATION, OSD_ATTR_OI_LOGICAL_LENGTH, 8);
-
static int _do_truncate(struct inode *inode, loff_t newsize)
{
struct exofs_i_info *oi = exofs_i(inode);
+ struct exofs_sb_info *sbi = inode->i_sb->s_fs_info;
int ret;
inode->i_mtime = inode->i_ctime = CURRENT_TIME;
- ret = exofs_oi_truncate(oi, (u64)newsize);
+ ret = ore_truncate(&sbi->layout, &oi->comps, (u64)newsize);
if (likely(!ret))
truncate_setsize(inode, newsize);
@@ -917,30 +923,26 @@
[1] = g_attr_inode_file_layout,
[2] = g_attr_inode_dir_layout,
};
- struct exofs_io_state *ios;
+ struct ore_io_state *ios;
struct exofs_on_disk_inode_layout *layout;
int ret;
- ret = exofs_get_io_state(&sbi->layout, &ios);
+ ret = ore_get_io_state(&sbi->layout, &oi->comps, &ios);
if (unlikely(ret)) {
- EXOFS_ERR("%s: exofs_get_io_state failed.\n", __func__);
+ EXOFS_ERR("%s: ore_get_io_state failed.\n", __func__);
return ret;
}
- ios->obj.id = exofs_oi_objno(oi);
- exofs_make_credential(oi->i_cred, &ios->obj);
- ios->cred = oi->i_cred;
-
- attrs[1].len = exofs_on_disk_inode_layout_size(sbi->layout.s_numdevs);
- attrs[2].len = exofs_on_disk_inode_layout_size(sbi->layout.s_numdevs);
+ attrs[1].len = exofs_on_disk_inode_layout_size(sbi->comps.numdevs);
+ attrs[2].len = exofs_on_disk_inode_layout_size(sbi->comps.numdevs);
ios->in_attr = attrs;
ios->in_attr_len = ARRAY_SIZE(attrs);
- ret = exofs_sbi_read(ios);
+ ret = ore_read(ios);
if (unlikely(ret)) {
EXOFS_ERR("object(0x%llx) corrupted, return empty file=>%d\n",
- _LLU(ios->obj.id), ret);
+ _LLU(oi->one_comp.obj.id), ret);
memset(inode, 0, sizeof(*inode));
inode->i_mode = 0040000 | (0777 & ~022);
/* If object is lost on target we might as well enable it's
@@ -990,7 +992,7 @@
}
out:
- exofs_put_io_state(ios);
+ ore_put_io_state(ios);
return ret;
}
@@ -1016,6 +1018,8 @@
return inode;
oi = exofs_i(inode);
__oi_init(oi);
+ exofs_init_comps(&oi->comps, &oi->one_comp, sb->s_fs_info,
+ exofs_oi_objno(oi));
/* read the inode from the osd */
ret = exofs_get_inode(sb, oi, &fcb);
@@ -1107,21 +1111,22 @@
* set the obj_created flag so that other methods know that the object exists on
* the OSD.
*/
-static void create_done(struct exofs_io_state *ios, void *p)
+static void create_done(struct ore_io_state *ios, void *p)
{
struct inode *inode = p;
struct exofs_i_info *oi = exofs_i(inode);
struct exofs_sb_info *sbi = inode->i_sb->s_fs_info;
int ret;
- ret = exofs_check_io(ios, NULL);
- exofs_put_io_state(ios);
+ ret = ore_check_io(ios, NULL);
+ ore_put_io_state(ios);
atomic_dec(&sbi->s_curr_pending);
if (unlikely(ret)) {
EXOFS_ERR("object=0x%llx creation failed in pid=0x%llx",
- _LLU(exofs_oi_objno(oi)), _LLU(sbi->layout.s_pid));
+ _LLU(exofs_oi_objno(oi)),
+ _LLU(oi->one_comp.obj.partition));
/*TODO: When FS is corrupted creation can fail, object already
* exist. Get rid of this asynchronous creation, if exist
* increment the obj counter and try the next object. Until we
@@ -1140,14 +1145,13 @@
*/
struct inode *exofs_new_inode(struct inode *dir, int mode)
{
- struct super_block *sb;
+ struct super_block *sb = dir->i_sb;
+ struct exofs_sb_info *sbi = sb->s_fs_info;
struct inode *inode;
struct exofs_i_info *oi;
- struct exofs_sb_info *sbi;
- struct exofs_io_state *ios;
+ struct ore_io_state *ios;
int ret;
- sb = dir->i_sb;
inode = new_inode(sb);
if (!inode)
return ERR_PTR(-ENOMEM);
@@ -1157,8 +1161,6 @@
set_obj_2bcreated(oi);
- sbi = sb->s_fs_info;
-
inode->i_mapping->backing_dev_info = sb->s_bdi;
inode_init_owner(inode, dir, mode);
inode->i_ino = sbi->s_nextid++;
@@ -1170,25 +1172,24 @@
spin_unlock(&sbi->s_next_gen_lock);
insert_inode_hash(inode);
+ exofs_init_comps(&oi->comps, &oi->one_comp, sb->s_fs_info,
+ exofs_oi_objno(oi));
exofs_sbi_write_stats(sbi); /* Make sure new sbi->s_nextid is on disk */
mark_inode_dirty(inode);
- ret = exofs_get_io_state(&sbi->layout, &ios);
+ ret = ore_get_io_state(&sbi->layout, &oi->comps, &ios);
if (unlikely(ret)) {
- EXOFS_ERR("exofs_new_inode: exofs_get_io_state failed\n");
+ EXOFS_ERR("exofs_new_inode: ore_get_io_state failed\n");
return ERR_PTR(ret);
}
- ios->obj.id = exofs_oi_objno(oi);
- exofs_make_credential(oi->i_cred, &ios->obj);
-
ios->done = create_done;
ios->private = inode;
- ios->cred = oi->i_cred;
- ret = exofs_sbi_create(ios);
+
+ ret = ore_create(ios);
if (ret) {
- exofs_put_io_state(ios);
+ ore_put_io_state(ios);
return ERR_PTR(ret);
}
atomic_inc(&sbi->s_curr_pending);
@@ -1207,11 +1208,11 @@
/*
* Callback function from exofs_update_inode().
*/
-static void updatei_done(struct exofs_io_state *ios, void *p)
+static void updatei_done(struct ore_io_state *ios, void *p)
{
struct updatei_args *args = p;
- exofs_put_io_state(ios);
+ ore_put_io_state(ios);
atomic_dec(&args->sbi->s_curr_pending);
@@ -1227,7 +1228,7 @@
struct exofs_i_info *oi = exofs_i(inode);
struct super_block *sb = inode->i_sb;
struct exofs_sb_info *sbi = sb->s_fs_info;
- struct exofs_io_state *ios;
+ struct ore_io_state *ios;
struct osd_attr attr;
struct exofs_fcb *fcb;
struct updatei_args *args;
@@ -1266,9 +1267,9 @@
} else
memcpy(fcb->i_data, oi->i_data, sizeof(fcb->i_data));
- ret = exofs_get_io_state(&sbi->layout, &ios);
+ ret = ore_get_io_state(&sbi->layout, &oi->comps, &ios);
if (unlikely(ret)) {
- EXOFS_ERR("%s: exofs_get_io_state failed.\n", __func__);
+ EXOFS_ERR("%s: ore_get_io_state failed.\n", __func__);
goto free_args;
}
@@ -1285,13 +1286,13 @@
ios->private = args;
}
- ret = exofs_oi_write(oi, ios);
+ ret = ore_write(ios);
if (!do_sync && !ret) {
atomic_inc(&sbi->s_curr_pending);
goto out; /* deallocation in updatei_done */
}
- exofs_put_io_state(ios);
+ ore_put_io_state(ios);
free_args:
kfree(args);
out:
@@ -1310,11 +1311,11 @@
* Callback function from exofs_delete_inode() - don't have much cleaning up to
* do.
*/
-static void delete_done(struct exofs_io_state *ios, void *p)
+static void delete_done(struct ore_io_state *ios, void *p)
{
struct exofs_sb_info *sbi = p;
- exofs_put_io_state(ios);
+ ore_put_io_state(ios);
atomic_dec(&sbi->s_curr_pending);
}
@@ -1329,7 +1330,7 @@
struct exofs_i_info *oi = exofs_i(inode);
struct super_block *sb = inode->i_sb;
struct exofs_sb_info *sbi = sb->s_fs_info;
- struct exofs_io_state *ios;
+ struct ore_io_state *ios;
int ret;
truncate_inode_pages(&inode->i_data, 0);
@@ -1349,20 +1350,19 @@
/* ignore the error, attempt a remove anyway */
/* Now Remove the OSD objects */
- ret = exofs_get_io_state(&sbi->layout, &ios);
+ ret = ore_get_io_state(&sbi->layout, &oi->comps, &ios);
if (unlikely(ret)) {
- EXOFS_ERR("%s: exofs_get_io_state failed\n", __func__);
+ EXOFS_ERR("%s: ore_get_io_state failed\n", __func__);
return;
}
- ios->obj.id = exofs_oi_objno(oi);
ios->done = delete_done;
ios->private = sbi;
- ios->cred = oi->i_cred;
- ret = exofs_sbi_remove(ios);
+
+ ret = ore_remove(ios);
if (ret) {
- EXOFS_ERR("%s: exofs_sbi_remove failed\n", __func__);
- exofs_put_io_state(ios);
+ EXOFS_ERR("%s: ore_remove failed\n", __func__);
+ ore_put_io_state(ios);
return;
}
atomic_inc(&sbi->s_curr_pending);
diff --git a/fs/exofs/ios.c b/fs/exofs/ore.c
similarity index 61%
rename from fs/exofs/ios.c
rename to fs/exofs/ore.c
index f74a2ec..25305af 100644
--- a/fs/exofs/ios.c
+++ b/fs/exofs/ore.c
@@ -23,81 +23,87 @@
*/
#include <linux/slab.h>
-#include <scsi/scsi_device.h>
#include <asm/div64.h>
-#include "exofs.h"
+#include <scsi/osd_ore.h>
-#define EXOFS_DBGMSG2(M...) do {} while (0)
-/* #define EXOFS_DBGMSG2 EXOFS_DBGMSG */
+#define ORE_ERR(fmt, a...) printk(KERN_ERR "ore: " fmt, ##a)
-void exofs_make_credential(u8 cred_a[OSD_CAP_LEN], const struct osd_obj_id *obj)
+#ifdef CONFIG_EXOFS_DEBUG
+#define ORE_DBGMSG(fmt, a...) \
+ printk(KERN_NOTICE "ore @%s:%d: " fmt, __func__, __LINE__, ##a)
+#else
+#define ORE_DBGMSG(fmt, a...) \
+ do { if (0) printk(fmt, ##a); } while (0)
+#endif
+
+/* u64 has problems with printk this will cast it to unsigned long long */
+#define _LLU(x) (unsigned long long)(x)
+
+#define ORE_DBGMSG2(M...) do {} while (0)
+/* #define ORE_DBGMSG2 ORE_DBGMSG */
+
+MODULE_AUTHOR("Boaz Harrosh <bharrosh@panasas.com>");
+MODULE_DESCRIPTION("Objects Raid Engine ore.ko");
+MODULE_LICENSE("GPL");
+
+static u8 *_ios_cred(struct ore_io_state *ios, unsigned index)
{
- osd_sec_init_nosec_doall_caps(cred_a, obj, false, true);
+ return ios->comps->comps[index & ios->comps->single_comp].cred;
}
-int exofs_read_kern(struct osd_dev *od, u8 *cred, struct osd_obj_id *obj,
- u64 offset, void *p, unsigned length)
+static struct osd_obj_id *_ios_obj(struct ore_io_state *ios, unsigned index)
{
- struct osd_request *or = osd_start_request(od, GFP_KERNEL);
-/* struct osd_sense_info osi = {.key = 0};*/
- int ret;
-
- if (unlikely(!or)) {
- EXOFS_DBGMSG("%s: osd_start_request failed.\n", __func__);
- return -ENOMEM;
- }
- ret = osd_req_read_kern(or, obj, offset, p, length);
- if (unlikely(ret)) {
- EXOFS_DBGMSG("%s: osd_req_read_kern failed.\n", __func__);
- goto out;
- }
-
- ret = osd_finalize_request(or, 0, cred, NULL);
- if (unlikely(ret)) {
- EXOFS_DBGMSG("Failed to osd_finalize_request() => %d\n", ret);
- goto out;
- }
-
- ret = osd_execute_request(or);
- if (unlikely(ret))
- EXOFS_DBGMSG("osd_execute_request() => %d\n", ret);
- /* osd_req_decode_sense(or, ret); */
-
-out:
- osd_end_request(or);
- return ret;
+ return &ios->comps->comps[index & ios->comps->single_comp].obj;
}
-int exofs_get_io_state(struct exofs_layout *layout,
- struct exofs_io_state **pios)
+static struct osd_dev *_ios_od(struct ore_io_state *ios, unsigned index)
{
- struct exofs_io_state *ios;
+ return ios->comps->ods[index];
+}
+
+int ore_get_rw_state(struct ore_layout *layout, struct ore_components *comps,
+ bool is_reading, u64 offset, u64 length,
+ struct ore_io_state **pios)
+{
+ struct ore_io_state *ios;
/*TODO: Maybe use kmem_cach per sbi of size
* exofs_io_state_size(layout->s_numdevs)
*/
- ios = kzalloc(exofs_io_state_size(layout->s_numdevs), GFP_KERNEL);
+ ios = kzalloc(ore_io_state_size(comps->numdevs), GFP_KERNEL);
if (unlikely(!ios)) {
- EXOFS_DBGMSG("Failed kzalloc bytes=%d\n",
- exofs_io_state_size(layout->s_numdevs));
+ ORE_DBGMSG("Failed kzalloc bytes=%d\n",
+ ore_io_state_size(comps->numdevs));
*pios = NULL;
return -ENOMEM;
}
ios->layout = layout;
- ios->obj.partition = layout->s_pid;
+ ios->comps = comps;
+ ios->offset = offset;
+ ios->length = length;
+ ios->reading = is_reading;
+
*pios = ios;
return 0;
}
+EXPORT_SYMBOL(ore_get_rw_state);
-void exofs_put_io_state(struct exofs_io_state *ios)
+int ore_get_io_state(struct ore_layout *layout, struct ore_components *comps,
+ struct ore_io_state **ios)
+{
+ return ore_get_rw_state(layout, comps, true, 0, 0, ios);
+}
+EXPORT_SYMBOL(ore_get_io_state);
+
+void ore_put_io_state(struct ore_io_state *ios)
{
if (ios) {
unsigned i;
for (i = 0; i < ios->numdevs; i++) {
- struct exofs_per_dev_state *per_dev = &ios->per_dev[i];
+ struct ore_per_dev_state *per_dev = &ios->per_dev[i];
if (per_dev->or)
osd_end_request(per_dev->or);
@@ -108,31 +114,9 @@
kfree(ios);
}
}
+EXPORT_SYMBOL(ore_put_io_state);
-unsigned exofs_layout_od_id(struct exofs_layout *layout,
- osd_id obj_no, unsigned layout_index)
-{
-/* switch (layout->lay_func) {
- case LAYOUT_MOVING_WINDOW:
- {*/
- unsigned dev_mod = obj_no;
-
- return (layout_index + dev_mod * layout->mirrors_p1) %
- layout->s_numdevs;
-/* }
- case LAYOUT_FUNC_IMPLICT:
- return layout->devs[layout_index];
- }*/
-}
-
-static inline struct osd_dev *exofs_ios_od(struct exofs_io_state *ios,
- unsigned layout_index)
-{
- return ios->layout->s_ods[
- exofs_layout_od_id(ios->layout, ios->obj.id, layout_index)];
-}
-
-static void _sync_done(struct exofs_io_state *ios, void *p)
+static void _sync_done(struct ore_io_state *ios, void *p)
{
struct completion *waiting = p;
@@ -141,20 +125,20 @@
static void _last_io(struct kref *kref)
{
- struct exofs_io_state *ios = container_of(
- kref, struct exofs_io_state, kref);
+ struct ore_io_state *ios = container_of(
+ kref, struct ore_io_state, kref);
ios->done(ios, ios->private);
}
static void _done_io(struct osd_request *or, void *p)
{
- struct exofs_io_state *ios = p;
+ struct ore_io_state *ios = p;
kref_put(&ios->kref, _last_io);
}
-static int exofs_io_execute(struct exofs_io_state *ios)
+static int ore_io_execute(struct ore_io_state *ios)
{
DECLARE_COMPLETION_ONSTACK(wait);
bool sync = (ios->done == NULL);
@@ -170,9 +154,9 @@
if (unlikely(!or))
continue;
- ret = osd_finalize_request(or, 0, ios->cred, NULL);
+ ret = osd_finalize_request(or, 0, _ios_cred(ios, i), NULL);
if (unlikely(ret)) {
- EXOFS_DBGMSG("Failed to osd_finalize_request() => %d\n",
+ ORE_DBGMSG("Failed to osd_finalize_request() => %d\n",
ret);
return ret;
}
@@ -194,7 +178,7 @@
if (sync) {
wait_for_completion(&wait);
- ret = exofs_check_io(ios, NULL);
+ ret = ore_check_io(ios, NULL);
}
return ret;
}
@@ -214,7 +198,7 @@
}
}
-int exofs_check_io(struct exofs_io_state *ios, u64 *resid)
+int ore_check_io(struct ore_io_state *ios, u64 *resid)
{
enum osd_err_priority acumulated_osd_err = 0;
int acumulated_lin_err = 0;
@@ -235,7 +219,7 @@
if (OSD_ERR_PRI_CLEAR_PAGES == osi.osd_err_pri) {
/* start read offset passed endof file */
_clear_bio(ios->per_dev[i].bio);
- EXOFS_DBGMSG("start read offset passed end of file "
+ ORE_DBGMSG("start read offset passed end of file "
"offset=0x%llx, length=0x%llx\n",
_LLU(ios->per_dev[i].offset),
_LLU(ios->per_dev[i].length));
@@ -259,6 +243,7 @@
return acumulated_lin_err;
}
+EXPORT_SYMBOL(ore_check_io);
/*
* L - logical offset into the file
@@ -305,20 +290,21 @@
struct _striping_info {
u64 obj_offset;
u64 group_length;
+ u64 M; /* for truncate */
unsigned dev;
unsigned unit_off;
};
-static void _calc_stripe_info(struct exofs_io_state *ios, u64 file_offset,
+static void _calc_stripe_info(struct ore_layout *layout, u64 file_offset,
struct _striping_info *si)
{
- u32 stripe_unit = ios->layout->stripe_unit;
- u32 group_width = ios->layout->group_width;
- u64 group_depth = ios->layout->group_depth;
+ u32 stripe_unit = layout->stripe_unit;
+ u32 group_width = layout->group_width;
+ u64 group_depth = layout->group_depth;
u32 U = stripe_unit * group_width;
u64 T = U * group_depth;
- u64 S = T * ios->layout->group_count;
+ u64 S = T * layout->group_count;
u64 M = div64_u64(file_offset, S);
/*
@@ -333,7 +319,7 @@
/* "H - (N * U)" is just "H % U" so it's bound to u32 */
si->dev = (u32)(H - (N * U)) / stripe_unit + G * group_width;
- si->dev *= ios->layout->mirrors_p1;
+ si->dev *= layout->mirrors_p1;
div_u64_rem(file_offset, stripe_unit, &si->unit_off);
@@ -341,15 +327,16 @@
(M * group_depth * stripe_unit);
si->group_length = T - H;
+ si->M = M;
}
-static int _add_stripe_unit(struct exofs_io_state *ios, unsigned *cur_pg,
- unsigned pgbase, struct exofs_per_dev_state *per_dev,
+static int _add_stripe_unit(struct ore_io_state *ios, unsigned *cur_pg,
+ unsigned pgbase, struct ore_per_dev_state *per_dev,
int cur_len)
{
unsigned pg = *cur_pg;
struct request_queue *q =
- osd_request_queue(exofs_ios_od(ios, per_dev->dev));
+ osd_request_queue(_ios_od(ios, per_dev->dev));
per_dev->length += cur_len;
@@ -361,7 +348,7 @@
per_dev->bio = bio_kmalloc(GFP_KERNEL, bio_size);
if (unlikely(!per_dev->bio)) {
- EXOFS_DBGMSG("Failed to allocate BIO size=%u\n",
+ ORE_DBGMSG("Failed to allocate BIO size=%u\n",
bio_size);
return -ENOMEM;
}
@@ -387,7 +374,7 @@
return 0;
}
-static int _prepare_one_group(struct exofs_io_state *ios, u64 length,
+static int _prepare_one_group(struct ore_io_state *ios, u64 length,
struct _striping_info *si)
{
unsigned stripe_unit = ios->layout->stripe_unit;
@@ -400,7 +387,7 @@
int ret = 0;
while (length) {
- struct exofs_per_dev_state *per_dev = &ios->per_dev[dev];
+ struct ore_per_dev_state *per_dev = &ios->per_dev[dev];
unsigned cur_len, page_off = 0;
if (!per_dev->length) {
@@ -443,7 +430,7 @@
return ret;
}
-static int _prepare_for_striping(struct exofs_io_state *ios)
+static int _prepare_for_striping(struct ore_io_state *ios)
{
u64 length = ios->length;
u64 offset = ios->offset;
@@ -452,9 +439,9 @@
if (!ios->pages) {
if (ios->kern_buff) {
- struct exofs_per_dev_state *per_dev = &ios->per_dev[0];
+ struct ore_per_dev_state *per_dev = &ios->per_dev[0];
- _calc_stripe_info(ios, ios->offset, &si);
+ _calc_stripe_info(ios->layout, ios->offset, &si);
per_dev->offset = si.obj_offset;
per_dev->dev = si.dev;
@@ -468,7 +455,7 @@
}
while (length) {
- _calc_stripe_info(ios, offset, &si);
+ _calc_stripe_info(ios->layout, offset, &si);
if (length < si.group_length)
si.group_length = length;
@@ -485,57 +472,59 @@
return ret;
}
-int exofs_sbi_create(struct exofs_io_state *ios)
+int ore_create(struct ore_io_state *ios)
{
int i, ret;
- for (i = 0; i < ios->layout->s_numdevs; i++) {
+ for (i = 0; i < ios->comps->numdevs; i++) {
struct osd_request *or;
- or = osd_start_request(exofs_ios_od(ios, i), GFP_KERNEL);
+ or = osd_start_request(_ios_od(ios, i), GFP_KERNEL);
if (unlikely(!or)) {
- EXOFS_ERR("%s: osd_start_request failed\n", __func__);
+ ORE_ERR("%s: osd_start_request failed\n", __func__);
ret = -ENOMEM;
goto out;
}
ios->per_dev[i].or = or;
ios->numdevs++;
- osd_req_create_object(or, &ios->obj);
+ osd_req_create_object(or, _ios_obj(ios, i));
}
- ret = exofs_io_execute(ios);
+ ret = ore_io_execute(ios);
out:
return ret;
}
+EXPORT_SYMBOL(ore_create);
-int exofs_sbi_remove(struct exofs_io_state *ios)
+int ore_remove(struct ore_io_state *ios)
{
int i, ret;
- for (i = 0; i < ios->layout->s_numdevs; i++) {
+ for (i = 0; i < ios->comps->numdevs; i++) {
struct osd_request *or;
- or = osd_start_request(exofs_ios_od(ios, i), GFP_KERNEL);
+ or = osd_start_request(_ios_od(ios, i), GFP_KERNEL);
if (unlikely(!or)) {
- EXOFS_ERR("%s: osd_start_request failed\n", __func__);
+ ORE_ERR("%s: osd_start_request failed\n", __func__);
ret = -ENOMEM;
goto out;
}
ios->per_dev[i].or = or;
ios->numdevs++;
- osd_req_remove_object(or, &ios->obj);
+ osd_req_remove_object(or, _ios_obj(ios, i));
}
- ret = exofs_io_execute(ios);
+ ret = ore_io_execute(ios);
out:
return ret;
}
+EXPORT_SYMBOL(ore_remove);
-static int _sbi_write_mirror(struct exofs_io_state *ios, int cur_comp)
+static int _write_mirror(struct ore_io_state *ios, int cur_comp)
{
- struct exofs_per_dev_state *master_dev = &ios->per_dev[cur_comp];
+ struct ore_per_dev_state *master_dev = &ios->per_dev[cur_comp];
unsigned dev = ios->per_dev[cur_comp].dev;
unsigned last_comp = cur_comp + ios->layout->mirrors_p1;
int ret = 0;
@@ -544,12 +533,12 @@
return 0; /* Just an empty slot */
for (; cur_comp < last_comp; ++cur_comp, ++dev) {
- struct exofs_per_dev_state *per_dev = &ios->per_dev[cur_comp];
+ struct ore_per_dev_state *per_dev = &ios->per_dev[cur_comp];
struct osd_request *or;
- or = osd_start_request(exofs_ios_od(ios, dev), GFP_KERNEL);
+ or = osd_start_request(_ios_od(ios, dev), GFP_KERNEL);
if (unlikely(!or)) {
- EXOFS_ERR("%s: osd_start_request failed\n", __func__);
+ ORE_ERR("%s: osd_start_request failed\n", __func__);
ret = -ENOMEM;
goto out;
}
@@ -563,7 +552,7 @@
bio = bio_kmalloc(GFP_KERNEL,
master_dev->bio->bi_max_vecs);
if (unlikely(!bio)) {
- EXOFS_DBGMSG(
+ ORE_DBGMSG(
"Failed to allocate BIO size=%u\n",
master_dev->bio->bi_max_vecs);
ret = -ENOMEM;
@@ -582,25 +571,29 @@
bio->bi_rw |= REQ_WRITE;
}
- osd_req_write(or, &ios->obj, per_dev->offset, bio,
- per_dev->length);
- EXOFS_DBGMSG("write(0x%llx) offset=0x%llx "
+ osd_req_write(or, _ios_obj(ios, dev), per_dev->offset,
+ bio, per_dev->length);
+ ORE_DBGMSG("write(0x%llx) offset=0x%llx "
"length=0x%llx dev=%d\n",
- _LLU(ios->obj.id), _LLU(per_dev->offset),
+ _LLU(_ios_obj(ios, dev)->id),
+ _LLU(per_dev->offset),
_LLU(per_dev->length), dev);
} else if (ios->kern_buff) {
- ret = osd_req_write_kern(or, &ios->obj, per_dev->offset,
- ios->kern_buff, ios->length);
+ ret = osd_req_write_kern(or, _ios_obj(ios, dev),
+ per_dev->offset,
+ ios->kern_buff, ios->length);
if (unlikely(ret))
goto out;
- EXOFS_DBGMSG2("write_kern(0x%llx) offset=0x%llx "
+ ORE_DBGMSG2("write_kern(0x%llx) offset=0x%llx "
"length=0x%llx dev=%d\n",
- _LLU(ios->obj.id), _LLU(per_dev->offset),
+ _LLU(_ios_obj(ios, dev)->id),
+ _LLU(per_dev->offset),
_LLU(ios->length), dev);
} else {
- osd_req_set_attributes(or, &ios->obj);
- EXOFS_DBGMSG2("obj(0x%llx) set_attributes=%d dev=%d\n",
- _LLU(ios->obj.id), ios->out_attr_len, dev);
+ osd_req_set_attributes(or, _ios_obj(ios, dev));
+ ORE_DBGMSG2("obj(0x%llx) set_attributes=%d dev=%d\n",
+ _LLU(_ios_obj(ios, dev)->id),
+ ios->out_attr_len, dev);
}
if (ios->out_attr)
@@ -616,7 +609,7 @@
return ret;
}
-int exofs_sbi_write(struct exofs_io_state *ios)
+int ore_write(struct ore_io_state *ios)
{
int i;
int ret;
@@ -626,52 +619,55 @@
return ret;
for (i = 0; i < ios->numdevs; i += ios->layout->mirrors_p1) {
- ret = _sbi_write_mirror(ios, i);
+ ret = _write_mirror(ios, i);
if (unlikely(ret))
return ret;
}
- ret = exofs_io_execute(ios);
+ ret = ore_io_execute(ios);
return ret;
}
+EXPORT_SYMBOL(ore_write);
-static int _sbi_read_mirror(struct exofs_io_state *ios, unsigned cur_comp)
+static int _read_mirror(struct ore_io_state *ios, unsigned cur_comp)
{
struct osd_request *or;
- struct exofs_per_dev_state *per_dev = &ios->per_dev[cur_comp];
- unsigned first_dev = (unsigned)ios->obj.id;
+ struct ore_per_dev_state *per_dev = &ios->per_dev[cur_comp];
+ struct osd_obj_id *obj = _ios_obj(ios, cur_comp);
+ unsigned first_dev = (unsigned)obj->id;
if (ios->pages && !per_dev->length)
return 0; /* Just an empty slot */
first_dev = per_dev->dev + first_dev % ios->layout->mirrors_p1;
- or = osd_start_request(exofs_ios_od(ios, first_dev), GFP_KERNEL);
+ or = osd_start_request(_ios_od(ios, first_dev), GFP_KERNEL);
if (unlikely(!or)) {
- EXOFS_ERR("%s: osd_start_request failed\n", __func__);
+ ORE_ERR("%s: osd_start_request failed\n", __func__);
return -ENOMEM;
}
per_dev->or = or;
if (ios->pages) {
- osd_req_read(or, &ios->obj, per_dev->offset,
+ osd_req_read(or, obj, per_dev->offset,
per_dev->bio, per_dev->length);
- EXOFS_DBGMSG("read(0x%llx) offset=0x%llx length=0x%llx"
- " dev=%d\n", _LLU(ios->obj.id),
+ ORE_DBGMSG("read(0x%llx) offset=0x%llx length=0x%llx"
+ " dev=%d\n", _LLU(obj->id),
_LLU(per_dev->offset), _LLU(per_dev->length),
first_dev);
} else if (ios->kern_buff) {
- int ret = osd_req_read_kern(or, &ios->obj, per_dev->offset,
+ int ret = osd_req_read_kern(or, obj, per_dev->offset,
ios->kern_buff, ios->length);
- EXOFS_DBGMSG2("read_kern(0x%llx) offset=0x%llx "
+ ORE_DBGMSG2("read_kern(0x%llx) offset=0x%llx "
"length=0x%llx dev=%d ret=>%d\n",
- _LLU(ios->obj.id), _LLU(per_dev->offset),
+ _LLU(obj->id), _LLU(per_dev->offset),
_LLU(ios->length), first_dev, ret);
if (unlikely(ret))
return ret;
} else {
- osd_req_get_attributes(or, &ios->obj);
- EXOFS_DBGMSG2("obj(0x%llx) get_attributes=%d dev=%d\n",
- _LLU(ios->obj.id), ios->in_attr_len, first_dev);
+ osd_req_get_attributes(or, obj);
+ ORE_DBGMSG2("obj(0x%llx) get_attributes=%d dev=%d\n",
+ _LLU(obj->id),
+ ios->in_attr_len, first_dev);
}
if (ios->out_attr)
osd_req_add_set_attr_list(or, ios->out_attr, ios->out_attr_len);
@@ -682,7 +678,7 @@
return 0;
}
-int exofs_sbi_read(struct exofs_io_state *ios)
+int ore_read(struct ore_io_state *ios)
{
int i;
int ret;
@@ -692,16 +688,17 @@
return ret;
for (i = 0; i < ios->numdevs; i += ios->layout->mirrors_p1) {
- ret = _sbi_read_mirror(ios, i);
+ ret = _read_mirror(ios, i);
if (unlikely(ret))
return ret;
}
- ret = exofs_io_execute(ios);
+ ret = ore_io_execute(ios);
return ret;
}
+EXPORT_SYMBOL(ore_read);
-int extract_attr_from_ios(struct exofs_io_state *ios, struct osd_attr *attr)
+int extract_attr_from_ios(struct ore_io_state *ios, struct osd_attr *attr)
{
struct osd_attr cur_attr = {.attr_page = 0}; /* start with zeros */
void *iter = NULL;
@@ -721,83 +718,118 @@
return -EIO;
}
+EXPORT_SYMBOL(extract_attr_from_ios);
-static int _truncate_mirrors(struct exofs_io_state *ios, unsigned cur_comp,
+static int _truncate_mirrors(struct ore_io_state *ios, unsigned cur_comp,
struct osd_attr *attr)
{
int last_comp = cur_comp + ios->layout->mirrors_p1;
for (; cur_comp < last_comp; ++cur_comp) {
- struct exofs_per_dev_state *per_dev = &ios->per_dev[cur_comp];
+ struct ore_per_dev_state *per_dev = &ios->per_dev[cur_comp];
struct osd_request *or;
- or = osd_start_request(exofs_ios_od(ios, cur_comp), GFP_KERNEL);
+ or = osd_start_request(_ios_od(ios, cur_comp), GFP_KERNEL);
if (unlikely(!or)) {
- EXOFS_ERR("%s: osd_start_request failed\n", __func__);
+ ORE_ERR("%s: osd_start_request failed\n", __func__);
return -ENOMEM;
}
per_dev->or = or;
- osd_req_set_attributes(or, &ios->obj);
+ osd_req_set_attributes(or, _ios_obj(ios, cur_comp));
osd_req_add_set_attr_list(or, attr, 1);
}
return 0;
}
-int exofs_oi_truncate(struct exofs_i_info *oi, u64 size)
+struct _trunc_info {
+ struct _striping_info si;
+ u64 prev_group_obj_off;
+ u64 next_group_obj_off;
+
+ unsigned first_group_dev;
+ unsigned nex_group_dev;
+ unsigned max_devs;
+};
+
+void _calc_trunk_info(struct ore_layout *layout, u64 file_offset,
+ struct _trunc_info *ti)
{
- struct exofs_sb_info *sbi = oi->vfs_inode.i_sb->s_fs_info;
- struct exofs_io_state *ios;
+ unsigned stripe_unit = layout->stripe_unit;
+
+ _calc_stripe_info(layout, file_offset, &ti->si);
+
+ ti->prev_group_obj_off = ti->si.M * stripe_unit;
+ ti->next_group_obj_off = ti->si.M ? (ti->si.M - 1) * stripe_unit : 0;
+
+ ti->first_group_dev = ti->si.dev - (ti->si.dev % layout->group_width);
+ ti->nex_group_dev = ti->first_group_dev + layout->group_width;
+ ti->max_devs = layout->group_width * layout->group_count;
+}
+
+int ore_truncate(struct ore_layout *layout, struct ore_components *comps,
+ u64 size)
+{
+ struct ore_io_state *ios;
struct exofs_trunc_attr {
struct osd_attr attr;
__be64 newsize;
} *size_attrs;
- struct _striping_info si;
+ struct _trunc_info ti;
int i, ret;
- ret = exofs_get_io_state(&sbi->layout, &ios);
+ ret = ore_get_io_state(layout, comps, &ios);
if (unlikely(ret))
return ret;
- size_attrs = kcalloc(ios->layout->group_width, sizeof(*size_attrs),
+ _calc_trunk_info(ios->layout, size, &ti);
+
+ size_attrs = kcalloc(ti.max_devs, sizeof(*size_attrs),
GFP_KERNEL);
if (unlikely(!size_attrs)) {
ret = -ENOMEM;
goto out;
}
- ios->obj.id = exofs_oi_objno(oi);
- ios->cred = oi->i_cred;
+ ios->numdevs = ios->comps->numdevs;
- ios->numdevs = ios->layout->s_numdevs;
- _calc_stripe_info(ios, size, &si);
-
- for (i = 0; i < ios->layout->group_width; ++i) {
+ for (i = 0; i < ti.max_devs; ++i) {
struct exofs_trunc_attr *size_attr = &size_attrs[i];
u64 obj_size;
- if (i < si.dev)
- obj_size = si.obj_offset +
- ios->layout->stripe_unit - si.unit_off;
- else if (i == si.dev)
- obj_size = si.obj_offset;
- else /* i > si.dev */
- obj_size = si.obj_offset - si.unit_off;
+ if (i < ti.first_group_dev)
+ obj_size = ti.prev_group_obj_off;
+ else if (i >= ti.nex_group_dev)
+ obj_size = ti.next_group_obj_off;
+ else if (i < ti.si.dev) /* dev within this group */
+ obj_size = ti.si.obj_offset +
+ ios->layout->stripe_unit - ti.si.unit_off;
+ else if (i == ti.si.dev)
+ obj_size = ti.si.obj_offset;
+ else /* i > ti.dev */
+ obj_size = ti.si.obj_offset - ti.si.unit_off;
size_attr->newsize = cpu_to_be64(obj_size);
size_attr->attr = g_attr_logical_length;
size_attr->attr.val_ptr = &size_attr->newsize;
+ ORE_DBGMSG("trunc(0x%llx) obj_offset=0x%llx dev=%d\n",
+ _LLU(comps->comps->obj.id), _LLU(obj_size), i);
ret = _truncate_mirrors(ios, i * ios->layout->mirrors_p1,
&size_attr->attr);
if (unlikely(ret))
goto out;
}
- ret = exofs_io_execute(ios);
+ ret = ore_io_execute(ios);
out:
kfree(size_attrs);
- exofs_put_io_state(ios);
+ ore_put_io_state(ios);
return ret;
}
+EXPORT_SYMBOL(ore_truncate);
+
+const struct osd_attr g_attr_logical_length = ATTR_DEF(
+ OSD_APAGE_OBJECT_INFORMATION, OSD_ATTR_OI_LOGICAL_LENGTH, 8);
+EXPORT_SYMBOL(g_attr_logical_length);
diff --git a/fs/exofs/pnfs.h b/fs/exofs/pnfs.h
deleted file mode 100644
index c52e988..0000000
--- a/fs/exofs/pnfs.h
+++ /dev/null
@@ -1,45 +0,0 @@
-/*
- * Copyright (C) 2008, 2009
- * Boaz Harrosh <bharrosh@panasas.com>
- *
- * This file is part of exofs.
- *
- * exofs 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.
- *
- */
-
-/* FIXME: Remove this file once pnfs hits mainline */
-
-#ifndef __EXOFS_PNFS_H__
-#define __EXOFS_PNFS_H__
-
-#if ! defined(__PNFS_OSD_XDR_H__)
-
-enum pnfs_iomode {
- IOMODE_READ = 1,
- IOMODE_RW = 2,
- IOMODE_ANY = 3,
-};
-
-/* Layout Structure */
-enum pnfs_osd_raid_algorithm4 {
- PNFS_OSD_RAID_0 = 1,
- PNFS_OSD_RAID_4 = 2,
- PNFS_OSD_RAID_5 = 3,
- PNFS_OSD_RAID_PQ = 4 /* Reed-Solomon P+Q */
-};
-
-struct pnfs_osd_data_map {
- u32 odm_num_comps;
- u64 odm_stripe_unit;
- u32 odm_group_width;
- u32 odm_group_depth;
- u32 odm_mirror_cnt;
- u32 odm_raid_algorithm;
-};
-
-#endif /* ! defined(__PNFS_OSD_XDR_H__) */
-
-#endif /* __EXOFS_PNFS_H__ */
diff --git a/fs/exofs/super.c b/fs/exofs/super.c
index c57bedd..2748940 100644
--- a/fs/exofs/super.c
+++ b/fs/exofs/super.c
@@ -40,6 +40,8 @@
#include "exofs.h"
+#define EXOFS_DBGMSG2(M...) do {} while (0)
+
/******************************************************************************
* MOUNT OPTIONS
*****************************************************************************/
@@ -208,10 +210,48 @@
}
/******************************************************************************
- * SUPERBLOCK FUNCTIONS
+ * Some osd helpers
*****************************************************************************/
-static const struct super_operations exofs_sops;
-static const struct export_operations exofs_export_ops;
+void exofs_make_credential(u8 cred_a[OSD_CAP_LEN], const struct osd_obj_id *obj)
+{
+ osd_sec_init_nosec_doall_caps(cred_a, obj, false, true);
+}
+
+static int exofs_read_kern(struct osd_dev *od, u8 *cred, struct osd_obj_id *obj,
+ u64 offset, void *p, unsigned length)
+{
+ struct osd_request *or = osd_start_request(od, GFP_KERNEL);
+/* struct osd_sense_info osi = {.key = 0};*/
+ int ret;
+
+ if (unlikely(!or)) {
+ EXOFS_DBGMSG("%s: osd_start_request failed.\n", __func__);
+ return -ENOMEM;
+ }
+ ret = osd_req_read_kern(or, obj, offset, p, length);
+ if (unlikely(ret)) {
+ EXOFS_DBGMSG("%s: osd_req_read_kern failed.\n", __func__);
+ goto out;
+ }
+
+ ret = osd_finalize_request(or, 0, cred, NULL);
+ if (unlikely(ret)) {
+ EXOFS_DBGMSG("Failed to osd_finalize_request() => %d\n", ret);
+ goto out;
+ }
+
+ ret = osd_execute_request(or);
+ if (unlikely(ret))
+ EXOFS_DBGMSG("osd_execute_request() => %d\n", ret);
+ /* osd_req_decode_sense(or, ret); */
+
+out:
+ osd_end_request(or);
+ EXOFS_DBGMSG2("read_kern(0x%llx) offset=0x%llx "
+ "length=0x%llx dev=%p ret=>%d\n",
+ _LLU(obj->id), _LLU(offset), _LLU(length), od, ret);
+ return ret;
+}
static const struct osd_attr g_attr_sb_stats = ATTR_DEF(
EXOFS_APAGE_SB_DATA,
@@ -223,21 +263,19 @@
struct osd_attr attrs[] = {
[0] = g_attr_sb_stats,
};
- struct exofs_io_state *ios;
+ struct ore_io_state *ios;
int ret;
- ret = exofs_get_io_state(&sbi->layout, &ios);
+ ret = ore_get_io_state(&sbi->layout, &sbi->comps, &ios);
if (unlikely(ret)) {
- EXOFS_ERR("%s: exofs_get_io_state failed.\n", __func__);
+ EXOFS_ERR("%s: ore_get_io_state failed.\n", __func__);
return ret;
}
- ios->cred = sbi->s_cred;
-
ios->in_attr = attrs;
ios->in_attr_len = ARRAY_SIZE(attrs);
- ret = exofs_sbi_read(ios);
+ ret = ore_read(ios);
if (unlikely(ret)) {
EXOFS_ERR("Error reading super_block stats => %d\n", ret);
goto out;
@@ -264,13 +302,13 @@
}
out:
- exofs_put_io_state(ios);
+ ore_put_io_state(ios);
return ret;
}
-static void stats_done(struct exofs_io_state *ios, void *p)
+static void stats_done(struct ore_io_state *ios, void *p)
{
- exofs_put_io_state(ios);
+ ore_put_io_state(ios);
/* Good thanks nothing to do anymore */
}
@@ -280,12 +318,12 @@
struct osd_attr attrs[] = {
[0] = g_attr_sb_stats,
};
- struct exofs_io_state *ios;
+ struct ore_io_state *ios;
int ret;
- ret = exofs_get_io_state(&sbi->layout, &ios);
+ ret = ore_get_io_state(&sbi->layout, &sbi->comps, &ios);
if (unlikely(ret)) {
- EXOFS_ERR("%s: exofs_get_io_state failed.\n", __func__);
+ EXOFS_ERR("%s: ore_get_io_state failed.\n", __func__);
return ret;
}
@@ -293,21 +331,27 @@
sbi->s_ess.s_numfiles = cpu_to_le64(sbi->s_numfiles);
attrs[0].val_ptr = &sbi->s_ess;
- ios->cred = sbi->s_cred;
+
ios->done = stats_done;
ios->private = sbi;
ios->out_attr = attrs;
ios->out_attr_len = ARRAY_SIZE(attrs);
- ret = exofs_sbi_write(ios);
+ ret = ore_write(ios);
if (unlikely(ret)) {
- EXOFS_ERR("%s: exofs_sbi_write failed.\n", __func__);
- exofs_put_io_state(ios);
+ EXOFS_ERR("%s: ore_write failed.\n", __func__);
+ ore_put_io_state(ios);
}
return ret;
}
+/******************************************************************************
+ * SUPERBLOCK FUNCTIONS
+ *****************************************************************************/
+static const struct super_operations exofs_sops;
+static const struct export_operations exofs_export_ops;
+
/*
* Write the superblock to the OSD
*/
@@ -315,7 +359,9 @@
{
struct exofs_sb_info *sbi;
struct exofs_fscb *fscb;
- struct exofs_io_state *ios;
+ struct ore_comp one_comp;
+ struct ore_components comps;
+ struct ore_io_state *ios;
int ret = -ENOMEM;
fscb = kmalloc(sizeof(*fscb), GFP_KERNEL);
@@ -331,7 +377,10 @@
* version). Otherwise the exofs_fscb is read-only from mkfs time. All
* the writeable info is set in exofs_sbi_write_stats() above.
*/
- ret = exofs_get_io_state(&sbi->layout, &ios);
+
+ exofs_init_comps(&comps, &one_comp, sbi, EXOFS_SUPER_ID);
+
+ ret = ore_get_io_state(&sbi->layout, &comps, &ios);
if (unlikely(ret))
goto out;
@@ -345,14 +394,12 @@
fscb->s_newfs = 0;
fscb->s_version = EXOFS_FSCB_VER;
- ios->obj.id = EXOFS_SUPER_ID;
ios->offset = 0;
ios->kern_buff = fscb;
- ios->cred = sbi->s_cred;
- ret = exofs_sbi_write(ios);
+ ret = ore_write(ios);
if (unlikely(ret))
- EXOFS_ERR("%s: exofs_sbi_write failed.\n", __func__);
+ EXOFS_ERR("%s: ore_write failed.\n", __func__);
else
sb->s_dirt = 0;
@@ -360,7 +407,7 @@
unlock_super(sb);
out:
EXOFS_DBGMSG("s_nextid=0x%llx ret=%d\n", _LLU(sbi->s_nextid), ret);
- exofs_put_io_state(ios);
+ ore_put_io_state(ios);
kfree(fscb);
return ret;
}
@@ -384,15 +431,17 @@
void exofs_free_sbi(struct exofs_sb_info *sbi)
{
- while (sbi->layout.s_numdevs) {
- int i = --sbi->layout.s_numdevs;
- struct osd_dev *od = sbi->layout.s_ods[i];
+ while (sbi->comps.numdevs) {
+ int i = --sbi->comps.numdevs;
+ struct osd_dev *od = sbi->comps.ods[i];
if (od) {
- sbi->layout.s_ods[i] = NULL;
+ sbi->comps.ods[i] = NULL;
osduld_put_device(od);
}
}
+ if (sbi->comps.ods != sbi->_min_one_dev)
+ kfree(sbi->comps.ods);
kfree(sbi);
}
@@ -419,8 +468,8 @@
msecs_to_jiffies(100));
}
- _exofs_print_device("Unmounting", NULL, sbi->layout.s_ods[0],
- sbi->layout.s_pid);
+ _exofs_print_device("Unmounting", NULL, sbi->comps.ods[0],
+ sbi->one_comp.obj.partition);
bdi_destroy(&sbi->bdi);
exofs_free_sbi(sbi);
@@ -501,10 +550,19 @@
return -EINVAL;
}
+ EXOFS_DBGMSG("exofs: layout: "
+ "num_comps=%u stripe_unit=0x%x group_width=%u "
+ "group_depth=0x%llx mirrors_p1=%u raid_algorithm=%u\n",
+ numdevs,
+ sbi->layout.stripe_unit,
+ sbi->layout.group_width,
+ _LLU(sbi->layout.group_depth),
+ sbi->layout.mirrors_p1,
+ sbi->data_map.odm_raid_algorithm);
return 0;
}
-static unsigned __ra_pages(struct exofs_layout *layout)
+static unsigned __ra_pages(struct ore_layout *layout)
{
const unsigned _MIN_RA = 32; /* min 128K read-ahead */
unsigned ra_pages = layout->group_width * layout->stripe_unit /
@@ -547,13 +605,11 @@
return !(odi->systemid_len || odi->osdname_len);
}
-static int exofs_read_lookup_dev_table(struct exofs_sb_info **psbi,
+static int exofs_read_lookup_dev_table(struct exofs_sb_info *sbi,
+ struct osd_dev *fscb_od,
unsigned table_count)
{
- struct exofs_sb_info *sbi = *psbi;
- struct osd_dev *fscb_od;
- struct osd_obj_id obj = {.partition = sbi->layout.s_pid,
- .id = EXOFS_DEVTABLE_ID};
+ struct ore_comp comp;
struct exofs_device_table *dt;
unsigned table_bytes = table_count * sizeof(dt->dt_dev_table[0]) +
sizeof(*dt);
@@ -567,10 +623,14 @@
return -ENOMEM;
}
- fscb_od = sbi->layout.s_ods[0];
- sbi->layout.s_ods[0] = NULL;
- sbi->layout.s_numdevs = 0;
- ret = exofs_read_kern(fscb_od, sbi->s_cred, &obj, 0, dt, table_bytes);
+ sbi->comps.numdevs = 0;
+
+ comp.obj.partition = sbi->one_comp.obj.partition;
+ comp.obj.id = EXOFS_DEVTABLE_ID;
+ exofs_make_credential(comp.cred, &comp.obj);
+
+ ret = exofs_read_kern(fscb_od, comp.cred, &comp.obj, 0, dt,
+ table_bytes);
if (unlikely(ret)) {
EXOFS_ERR("ERROR: reading device table\n");
goto out;
@@ -588,16 +648,18 @@
goto out;
if (likely(numdevs > 1)) {
- unsigned size = numdevs * sizeof(sbi->layout.s_ods[0]);
+ unsigned size = numdevs * sizeof(sbi->comps.ods[0]);
- sbi = krealloc(sbi, sizeof(*sbi) + size, GFP_KERNEL);
- if (unlikely(!sbi)) {
+ /* Twice bigger table: See exofs_init_comps() and below
+ * comment
+ */
+ sbi->comps.ods = kzalloc(size + size - 1, GFP_KERNEL);
+ if (unlikely(!sbi->comps.ods)) {
+ EXOFS_ERR("ERROR: faild allocating Device array[%d]\n",
+ numdevs);
ret = -ENOMEM;
goto out;
}
- memset(&sbi->layout.s_ods[1], 0,
- size - sizeof(sbi->layout.s_ods[0]));
- *psbi = sbi;
}
for (i = 0; i < numdevs; i++) {
@@ -619,8 +681,8 @@
* line. We always keep them in device-table order.
*/
if (fscb_od && osduld_device_same(fscb_od, &odi)) {
- sbi->layout.s_ods[i] = fscb_od;
- ++sbi->layout.s_numdevs;
+ sbi->comps.ods[i] = fscb_od;
+ ++sbi->comps.numdevs;
fscb_od = NULL;
continue;
}
@@ -633,13 +695,13 @@
goto out;
}
- sbi->layout.s_ods[i] = od;
- ++sbi->layout.s_numdevs;
+ sbi->comps.ods[i] = od;
+ ++sbi->comps.numdevs;
/* Read the fscb of the other devices to make sure the FS
* partition is there.
*/
- ret = exofs_read_kern(od, sbi->s_cred, &obj, 0, &fscb,
+ ret = exofs_read_kern(od, comp.cred, &comp.obj, 0, &fscb,
sizeof(fscb));
if (unlikely(ret)) {
EXOFS_ERR("ERROR: Malformed participating device "
@@ -656,13 +718,22 @@
out:
kfree(dt);
- if (unlikely(!ret && fscb_od)) {
- EXOFS_ERR(
- "ERROR: Bad device-table container device not present\n");
- osduld_put_device(fscb_od);
- ret = -EINVAL;
- }
+ if (likely(!ret)) {
+ unsigned numdevs = sbi->comps.numdevs;
+ if (unlikely(fscb_od)) {
+ EXOFS_ERR("ERROR: Bad device-table container device not present\n");
+ osduld_put_device(fscb_od);
+ return -EINVAL;
+ }
+ /* exofs round-robins the device table view according to inode
+ * number. We hold a: twice bigger table hence inodes can point
+ * to any device and have a sequential view of the table
+ * starting at this device. See exofs_init_comps()
+ */
+ for (i = 0; i < numdevs - 1; ++i)
+ sbi->comps.ods[i + numdevs] = sbi->comps.ods[i];
+ }
return ret;
}
@@ -676,7 +747,7 @@
struct exofs_sb_info *sbi; /*extended info */
struct osd_dev *od; /* Master device */
struct exofs_fscb fscb; /*on-disk superblock info */
- struct osd_obj_id obj;
+ struct ore_comp comp;
unsigned table_count;
int ret;
@@ -684,10 +755,6 @@
if (!sbi)
return -ENOMEM;
- ret = bdi_setup_and_register(&sbi->bdi, "exofs", BDI_CAP_MAP_COPY);
- if (ret)
- goto free_bdi;
-
/* use mount options to fill superblock */
if (opts->is_osdname) {
struct osd_dev_info odi = {.systemid_len = 0};
@@ -695,6 +762,8 @@
odi.osdname_len = strlen(opts->dev_name);
odi.osdname = (u8 *)opts->dev_name;
od = osduld_info_lookup(&odi);
+ kfree(opts->dev_name);
+ opts->dev_name = NULL;
} else {
od = osduld_path_lookup(opts->dev_name);
}
@@ -709,11 +778,16 @@
sbi->layout.group_width = 1;
sbi->layout.group_depth = -1;
sbi->layout.group_count = 1;
- sbi->layout.s_ods[0] = od;
- sbi->layout.s_numdevs = 1;
- sbi->layout.s_pid = opts->pid;
sbi->s_timeout = opts->timeout;
+ sbi->one_comp.obj.partition = opts->pid;
+ sbi->one_comp.obj.id = 0;
+ exofs_make_credential(sbi->one_comp.cred, &sbi->one_comp.obj);
+ sbi->comps.numdevs = 1;
+ sbi->comps.single_comp = EC_SINGLE_COMP;
+ sbi->comps.comps = &sbi->one_comp;
+ sbi->comps.ods = sbi->_min_one_dev;
+
/* fill in some other data by hand */
memset(sb->s_id, 0, sizeof(sb->s_id));
strcpy(sb->s_id, "exofs");
@@ -724,11 +798,11 @@
sb->s_bdev = NULL;
sb->s_dev = 0;
- obj.partition = sbi->layout.s_pid;
- obj.id = EXOFS_SUPER_ID;
- exofs_make_credential(sbi->s_cred, &obj);
+ comp.obj.partition = sbi->one_comp.obj.partition;
+ comp.obj.id = EXOFS_SUPER_ID;
+ exofs_make_credential(comp.cred, &comp.obj);
- ret = exofs_read_kern(od, sbi->s_cred, &obj, 0, &fscb, sizeof(fscb));
+ ret = exofs_read_kern(od, comp.cred, &comp.obj, 0, &fscb, sizeof(fscb));
if (unlikely(ret))
goto free_sbi;
@@ -757,9 +831,11 @@
table_count = le64_to_cpu(fscb.s_dev_table_count);
if (table_count) {
- ret = exofs_read_lookup_dev_table(&sbi, table_count);
+ ret = exofs_read_lookup_dev_table(sbi, od, table_count);
if (unlikely(ret))
goto free_sbi;
+ } else {
+ sbi->comps.ods[0] = od;
}
__sbi_read_stats(sbi);
@@ -793,20 +869,20 @@
goto free_sbi;
}
- _exofs_print_device("Mounting", opts->dev_name, sbi->layout.s_ods[0],
- sbi->layout.s_pid);
- if (opts->is_osdname)
- kfree(opts->dev_name);
+ ret = bdi_setup_and_register(&sbi->bdi, "exofs", BDI_CAP_MAP_COPY);
+ if (ret) {
+ EXOFS_DBGMSG("Failed to bdi_setup_and_register\n");
+ goto free_sbi;
+ }
+
+ _exofs_print_device("Mounting", opts->dev_name, sbi->comps.ods[0],
+ sbi->one_comp.obj.partition);
return 0;
free_sbi:
- bdi_destroy(&sbi->bdi);
-free_bdi:
EXOFS_ERR("Unable to mount exofs on %s pid=0x%llx err=%d\n",
- opts->dev_name, sbi->layout.s_pid, ret);
+ opts->dev_name, sbi->one_comp.obj.partition, ret);
exofs_free_sbi(sbi);
- if (opts->is_osdname)
- kfree(opts->dev_name);
return ret;
}
@@ -837,7 +913,7 @@
{
struct super_block *sb = dentry->d_sb;
struct exofs_sb_info *sbi = sb->s_fs_info;
- struct exofs_io_state *ios;
+ struct ore_io_state *ios;
struct osd_attr attrs[] = {
ATTR_DEF(OSD_APAGE_PARTITION_QUOTAS,
OSD_ATTR_PQ_CAPACITY_QUOTA, sizeof(__be64)),
@@ -846,21 +922,18 @@
};
uint64_t capacity = ULLONG_MAX;
uint64_t used = ULLONG_MAX;
- uint8_t cred_a[OSD_CAP_LEN];
int ret;
- ret = exofs_get_io_state(&sbi->layout, &ios);
+ ret = ore_get_io_state(&sbi->layout, &sbi->comps, &ios);
if (ret) {
- EXOFS_DBGMSG("exofs_get_io_state failed.\n");
+ EXOFS_DBGMSG("ore_get_io_state failed.\n");
return ret;
}
- exofs_make_credential(cred_a, &ios->obj);
- ios->cred = sbi->s_cred;
ios->in_attr = attrs;
ios->in_attr_len = ARRAY_SIZE(attrs);
- ret = exofs_sbi_read(ios);
+ ret = ore_read(ios);
if (unlikely(ret))
goto out;
@@ -889,7 +962,7 @@
buf->f_namelen = EXOFS_NAME_LEN;
out:
- exofs_put_io_state(ios);
+ ore_put_io_state(ios);
return ret;
}
diff --git a/fs/ext2/acl.c b/fs/ext2/acl.c
index 52c0537..35d6a3c 100644
--- a/fs/ext2/acl.c
+++ b/fs/ext2/acl.c
@@ -194,12 +194,10 @@
case ACL_TYPE_ACCESS:
name_index = EXT2_XATTR_INDEX_POSIX_ACL_ACCESS;
if (acl) {
- mode_t mode = inode->i_mode;
- error = posix_acl_equiv_mode(acl, &mode);
+ error = posix_acl_equiv_mode(acl, &inode->i_mode);
if (error < 0)
return error;
else {
- inode->i_mode = mode;
inode->i_ctime = CURRENT_TIME_SEC;
mark_inode_dirty(inode);
if (error == 0)
@@ -253,16 +251,14 @@
inode->i_mode &= ~current_umask();
}
if (test_opt(inode->i_sb, POSIX_ACL) && acl) {
- mode_t mode = inode->i_mode;
if (S_ISDIR(inode->i_mode)) {
error = ext2_set_acl(inode, ACL_TYPE_DEFAULT, acl);
if (error)
goto cleanup;
}
- error = posix_acl_create(&acl, GFP_KERNEL, &mode);
+ error = posix_acl_create(&acl, GFP_KERNEL, &inode->i_mode);
if (error < 0)
return error;
- inode->i_mode = mode;
if (error > 0) {
/* This is an extended ACL */
error = ext2_set_acl(inode, ACL_TYPE_ACCESS, acl);
diff --git a/fs/ext3/acl.c b/fs/ext3/acl.c
index 6c29bf0..3091f62 100644
--- a/fs/ext3/acl.c
+++ b/fs/ext3/acl.c
@@ -199,12 +199,10 @@
case ACL_TYPE_ACCESS:
name_index = EXT3_XATTR_INDEX_POSIX_ACL_ACCESS;
if (acl) {
- mode_t mode = inode->i_mode;
- error = posix_acl_equiv_mode(acl, &mode);
+ error = posix_acl_equiv_mode(acl, &inode->i_mode);
if (error < 0)
return error;
else {
- inode->i_mode = mode;
inode->i_ctime = CURRENT_TIME_SEC;
ext3_mark_inode_dirty(handle, inode);
if (error == 0)
@@ -261,19 +259,16 @@
inode->i_mode &= ~current_umask();
}
if (test_opt(inode->i_sb, POSIX_ACL) && acl) {
- mode_t mode = inode->i_mode;
-
if (S_ISDIR(inode->i_mode)) {
error = ext3_set_acl(handle, inode,
ACL_TYPE_DEFAULT, acl);
if (error)
goto cleanup;
}
- error = posix_acl_create(&acl, GFP_NOFS, &mode);
+ error = posix_acl_create(&acl, GFP_NOFS, &inode->i_mode);
if (error < 0)
return error;
- inode->i_mode = mode;
if (error > 0) {
/* This is an extended ACL */
error = ext3_set_acl(handle, inode, ACL_TYPE_ACCESS, acl);
diff --git a/fs/ext3/namei.c b/fs/ext3/namei.c
index 6e18a0b..5571708 100644
--- a/fs/ext3/namei.c
+++ b/fs/ext3/namei.c
@@ -2209,9 +2209,11 @@
/*
* For non-fast symlinks, we just allocate inode and put it on
* orphan list in the first transaction => we need bitmap,
- * group descriptor, sb, inode block, quota blocks.
+ * group descriptor, sb, inode block, quota blocks, and
+ * possibly selinux xattr blocks.
*/
- credits = 4 + EXT3_MAXQUOTAS_INIT_BLOCKS(dir->i_sb);
+ credits = 4 + EXT3_MAXQUOTAS_INIT_BLOCKS(dir->i_sb) +
+ EXT3_XATTR_TRANS_BLOCKS;
} else {
/*
* Fast symlink. We have to add entry to directory
diff --git a/fs/ext4/Makefile b/fs/ext4/Makefile
index 0410946..56fd8f86 100644
--- a/fs/ext4/Makefile
+++ b/fs/ext4/Makefile
@@ -7,7 +7,7 @@
ext4-y := balloc.o bitmap.o dir.o file.o fsync.o ialloc.o inode.o page-io.o \
ioctl.o namei.o super.o symlink.o hash.o resize.o extents.o \
ext4_jbd2.o migrate.o mballoc.o block_validity.o move_extent.o \
- mmp.o
+ mmp.o indirect.o
ext4-$(CONFIG_EXT4_FS_XATTR) += xattr.o xattr_user.o xattr_trusted.o
ext4-$(CONFIG_EXT4_FS_POSIX_ACL) += acl.o
diff --git a/fs/ext4/acl.c b/fs/ext4/acl.c
index dca2d1d..a5c29bb 100644
--- a/fs/ext4/acl.c
+++ b/fs/ext4/acl.c
@@ -198,12 +198,10 @@
case ACL_TYPE_ACCESS:
name_index = EXT4_XATTR_INDEX_POSIX_ACL_ACCESS;
if (acl) {
- mode_t mode = inode->i_mode;
- error = posix_acl_equiv_mode(acl, &mode);
+ error = posix_acl_equiv_mode(acl, &inode->i_mode);
if (error < 0)
return error;
else {
- inode->i_mode = mode;
inode->i_ctime = ext4_current_time(inode);
ext4_mark_inode_dirty(handle, inode);
if (error == 0)
@@ -259,19 +257,16 @@
inode->i_mode &= ~current_umask();
}
if (test_opt(inode->i_sb, POSIX_ACL) && acl) {
- mode_t mode = inode->i_mode;
-
if (S_ISDIR(inode->i_mode)) {
error = ext4_set_acl(handle, inode,
ACL_TYPE_DEFAULT, acl);
if (error)
goto cleanup;
}
- error = posix_acl_create(&acl, GFP_NOFS, &mode);
+ error = posix_acl_create(&acl, GFP_NOFS, &inode->i_mode);
if (error < 0)
return error;
- inode->i_mode = mode;
if (error > 0) {
/* This is an extended ACL */
error = ext4_set_acl(handle, inode, ACL_TYPE_ACCESS, acl);
diff --git a/fs/ext4/balloc.c b/fs/ext4/balloc.c
index 264f694..f8224ad 100644
--- a/fs/ext4/balloc.c
+++ b/fs/ext4/balloc.c
@@ -620,3 +620,51 @@
}
+/**
+ * ext4_inode_to_goal_block - return a hint for block allocation
+ * @inode: inode for block allocation
+ *
+ * Return the ideal location to start allocating blocks for a
+ * newly created inode.
+ */
+ext4_fsblk_t ext4_inode_to_goal_block(struct inode *inode)
+{
+ struct ext4_inode_info *ei = EXT4_I(inode);
+ ext4_group_t block_group;
+ ext4_grpblk_t colour;
+ int flex_size = ext4_flex_bg_size(EXT4_SB(inode->i_sb));
+ ext4_fsblk_t bg_start;
+ ext4_fsblk_t last_block;
+
+ block_group = ei->i_block_group;
+ if (flex_size >= EXT4_FLEX_SIZE_DIR_ALLOC_SCHEME) {
+ /*
+ * If there are at least EXT4_FLEX_SIZE_DIR_ALLOC_SCHEME
+ * block groups per flexgroup, reserve the first block
+ * group for directories and special files. Regular
+ * files will start at the second block group. This
+ * tends to speed up directory access and improves
+ * fsck times.
+ */
+ block_group &= ~(flex_size-1);
+ if (S_ISREG(inode->i_mode))
+ block_group++;
+ }
+ bg_start = ext4_group_first_block_no(inode->i_sb, block_group);
+ last_block = ext4_blocks_count(EXT4_SB(inode->i_sb)->s_es) - 1;
+
+ /*
+ * If we are doing delayed allocation, we don't need take
+ * colour into account.
+ */
+ if (test_opt(inode->i_sb, DELALLOC))
+ return bg_start;
+
+ if (bg_start + EXT4_BLOCKS_PER_GROUP(inode->i_sb) <= last_block)
+ colour = (current->pid % 16) *
+ (EXT4_BLOCKS_PER_GROUP(inode->i_sb) / 16);
+ else
+ colour = (current->pid % 16) * ((last_block - bg_start) / 16);
+ return bg_start + colour;
+}
+
diff --git a/fs/ext4/block_validity.c b/fs/ext4/block_validity.c
index fac90f3..8efb2f0 100644
--- a/fs/ext4/block_validity.c
+++ b/fs/ext4/block_validity.c
@@ -246,3 +246,24 @@
return 1;
}
+int ext4_check_blockref(const char *function, unsigned int line,
+ struct inode *inode, __le32 *p, unsigned int max)
+{
+ struct ext4_super_block *es = EXT4_SB(inode->i_sb)->s_es;
+ __le32 *bref = p;
+ unsigned int blk;
+
+ while (bref < p+max) {
+ blk = le32_to_cpu(*bref++);
+ if (blk &&
+ unlikely(!ext4_data_block_valid(EXT4_SB(inode->i_sb),
+ blk, 1))) {
+ es->s_last_error_block = cpu_to_le64(blk);
+ ext4_error_inode(inode, function, line, blk,
+ "invalid block");
+ return -EIO;
+ }
+ }
+ return 0;
+}
+
diff --git a/fs/ext4/ext4.h b/fs/ext4/ext4.h
index fa44df8..e717dfd 100644
--- a/fs/ext4/ext4.h
+++ b/fs/ext4/ext4.h
@@ -526,6 +526,7 @@
#define EXT4_FREE_BLOCKS_METADATA 0x0001
#define EXT4_FREE_BLOCKS_FORGET 0x0002
#define EXT4_FREE_BLOCKS_VALIDATED 0x0004
+#define EXT4_FREE_BLOCKS_NO_QUOT_UPDATE 0x0008
/*
* ioctl commands
@@ -939,6 +940,8 @@
#define ext4_find_next_zero_bit find_next_zero_bit_le
#define ext4_find_next_bit find_next_bit_le
+extern void ext4_set_bits(void *bm, int cur, int len);
+
/*
* Maximal mount counts between two filesystem checks
*/
@@ -1126,7 +1129,8 @@
struct journal_s *s_journal;
struct list_head s_orphan;
struct mutex s_orphan_lock;
- struct mutex s_resize_lock;
+ unsigned long s_resize_flags; /* Flags indicating if there
+ is a resizer */
unsigned long s_commit_interval;
u32 s_max_batch_time;
u32 s_min_batch_time;
@@ -1214,6 +1218,9 @@
/* Kernel thread for multiple mount protection */
struct task_struct *s_mmp_tsk;
+
+ /* record the last minlen when FITRIM is called. */
+ atomic_t s_last_trim_minblks;
};
static inline struct ext4_sb_info *EXT4_SB(struct super_block *sb)
@@ -1743,6 +1750,7 @@
struct ext4_group_desc *desc);
#define ext4_free_blocks_after_init(sb, group, desc) \
ext4_init_block_bitmap(sb, NULL, group, desc)
+ext4_fsblk_t ext4_inode_to_goal_block(struct inode *);
/* dir.c */
extern int __ext4_check_dir_entry(const char *, unsigned int, struct inode *,
@@ -1793,7 +1801,7 @@
unsigned long count, int flags);
extern int ext4_mb_add_groupinfo(struct super_block *sb,
ext4_group_t i, struct ext4_group_desc *desc);
-extern void ext4_add_groupblocks(handle_t *handle, struct super_block *sb,
+extern int ext4_group_add_blocks(handle_t *handle, struct super_block *sb,
ext4_fsblk_t block, unsigned long count);
extern int ext4_trim_fs(struct super_block *, struct fstrim_range *);
@@ -1834,6 +1842,17 @@
extern qsize_t *ext4_get_reserved_space(struct inode *inode);
extern void ext4_da_update_reserve_space(struct inode *inode,
int used, int quota_claim);
+
+/* indirect.c */
+extern int ext4_ind_map_blocks(handle_t *handle, struct inode *inode,
+ struct ext4_map_blocks *map, int flags);
+extern ssize_t ext4_ind_direct_IO(int rw, struct kiocb *iocb,
+ const struct iovec *iov, loff_t offset,
+ unsigned long nr_segs);
+extern int ext4_ind_calc_metadata_amount(struct inode *inode, sector_t lblock);
+extern int ext4_ind_trans_blocks(struct inode *inode, int nrblocks, int chunk);
+extern void ext4_ind_truncate(struct inode *inode);
+
/* ioctl.c */
extern long ext4_ioctl(struct file *, unsigned int, unsigned long);
extern long ext4_compat_ioctl(struct file *, unsigned int, unsigned long);
@@ -1855,6 +1874,9 @@
ext4_fsblk_t n_blocks_count);
/* super.c */
+extern void *ext4_kvmalloc(size_t size, gfp_t flags);
+extern void *ext4_kvzalloc(size_t size, gfp_t flags);
+extern void ext4_kvfree(void *ptr);
extern void __ext4_error(struct super_block *, const char *, unsigned int,
const char *, ...)
__attribute__ ((format (printf, 4, 5)));
@@ -2067,11 +2089,19 @@
* 5 free 8-block regions. */
};
-#define EXT4_GROUP_INFO_NEED_INIT_BIT 0
+#define EXT4_GROUP_INFO_NEED_INIT_BIT 0
+#define EXT4_GROUP_INFO_WAS_TRIMMED_BIT 1
#define EXT4_MB_GRP_NEED_INIT(grp) \
(test_bit(EXT4_GROUP_INFO_NEED_INIT_BIT, &((grp)->bb_state)))
+#define EXT4_MB_GRP_WAS_TRIMMED(grp) \
+ (test_bit(EXT4_GROUP_INFO_WAS_TRIMMED_BIT, &((grp)->bb_state)))
+#define EXT4_MB_GRP_SET_TRIMMED(grp) \
+ (set_bit(EXT4_GROUP_INFO_WAS_TRIMMED_BIT, &((grp)->bb_state)))
+#define EXT4_MB_GRP_CLEAR_TRIMMED(grp) \
+ (clear_bit(EXT4_GROUP_INFO_WAS_TRIMMED_BIT, &((grp)->bb_state)))
+
#define EXT4_MAX_CONTENTION 8
#define EXT4_CONTENTION_THRESHOLD 2
@@ -2123,6 +2153,19 @@
}
/*
+ * Block validity checking
+ */
+#define ext4_check_indirect_blockref(inode, bh) \
+ ext4_check_blockref(__func__, __LINE__, inode, \
+ (__le32 *)(bh)->b_data, \
+ EXT4_ADDR_PER_BLOCK((inode)->i_sb))
+
+#define ext4_ind_check_inode(inode) \
+ ext4_check_blockref(__func__, __LINE__, inode, \
+ EXT4_I(inode)->i_data, \
+ EXT4_NDIR_BLOCKS)
+
+/*
* Inodes and files operations
*/
@@ -2151,6 +2194,8 @@
extern int ext4_data_block_valid(struct ext4_sb_info *sbi,
ext4_fsblk_t start_blk,
unsigned int count);
+extern int ext4_check_blockref(const char *, unsigned int,
+ struct inode *, __le32 *, unsigned int);
/* extents.c */
extern int ext4_ext_tree_init(handle_t *handle, struct inode *);
@@ -2230,6 +2275,10 @@
extern wait_queue_head_t ext4__ioend_wq[EXT4_WQ_HASH_SZ];
extern struct mutex ext4__aio_mutex[EXT4_WQ_HASH_SZ];
+#define EXT4_RESIZING 0
+extern int ext4_resize_begin(struct super_block *sb);
+extern void ext4_resize_end(struct super_block *sb);
+
#endif /* __KERNEL__ */
#endif /* _EXT4_H */
diff --git a/fs/ext4/extents.c b/fs/ext4/extents.c
index f815cc8..57cf568 100644
--- a/fs/ext4/extents.c
+++ b/fs/ext4/extents.c
@@ -114,12 +114,6 @@
struct ext4_ext_path *path,
ext4_lblk_t block)
{
- struct ext4_inode_info *ei = EXT4_I(inode);
- ext4_fsblk_t bg_start;
- ext4_fsblk_t last_block;
- ext4_grpblk_t colour;
- ext4_group_t block_group;
- int flex_size = ext4_flex_bg_size(EXT4_SB(inode->i_sb));
int depth;
if (path) {
@@ -161,36 +155,7 @@
}
/* OK. use inode's group */
- block_group = ei->i_block_group;
- if (flex_size >= EXT4_FLEX_SIZE_DIR_ALLOC_SCHEME) {
- /*
- * If there are at least EXT4_FLEX_SIZE_DIR_ALLOC_SCHEME
- * block groups per flexgroup, reserve the first block
- * group for directories and special files. Regular
- * files will start at the second block group. This
- * tends to speed up directory access and improves
- * fsck times.
- */
- block_group &= ~(flex_size-1);
- if (S_ISREG(inode->i_mode))
- block_group++;
- }
- bg_start = ext4_group_first_block_no(inode->i_sb, block_group);
- last_block = ext4_blocks_count(EXT4_SB(inode->i_sb)->s_es) - 1;
-
- /*
- * If we are doing delayed allocation, we don't need take
- * colour into account.
- */
- if (test_opt(inode->i_sb, DELALLOC))
- return bg_start;
-
- if (bg_start + EXT4_BLOCKS_PER_GROUP(inode->i_sb) <= last_block)
- colour = (current->pid % 16) *
- (EXT4_BLOCKS_PER_GROUP(inode->i_sb) / 16);
- else
- colour = (current->pid % 16) * ((last_block - bg_start) / 16);
- return bg_start + colour + block;
+ return ext4_inode_to_goal_block(inode);
}
/*
@@ -776,6 +741,16 @@
logical, le32_to_cpu(curp->p_idx->ei_block));
return -EIO;
}
+
+ if (unlikely(le16_to_cpu(curp->p_hdr->eh_entries)
+ >= le16_to_cpu(curp->p_hdr->eh_max))) {
+ EXT4_ERROR_INODE(inode,
+ "eh_entries %d >= eh_max %d!",
+ le16_to_cpu(curp->p_hdr->eh_entries),
+ le16_to_cpu(curp->p_hdr->eh_max));
+ return -EIO;
+ }
+
len = EXT_MAX_INDEX(curp->p_hdr) - curp->p_idx;
if (logical > le32_to_cpu(curp->p_idx->ei_block)) {
/* insert after */
@@ -805,13 +780,6 @@
ext4_idx_store_pblock(ix, ptr);
le16_add_cpu(&curp->p_hdr->eh_entries, 1);
- if (unlikely(le16_to_cpu(curp->p_hdr->eh_entries)
- > le16_to_cpu(curp->p_hdr->eh_max))) {
- EXT4_ERROR_INODE(inode,
- "logical %d == ei_block %d!",
- logical, le32_to_cpu(curp->p_idx->ei_block));
- return -EIO;
- }
if (unlikely(ix > EXT_LAST_INDEX(curp->p_hdr))) {
EXT4_ERROR_INODE(inode, "ix > EXT_LAST_INDEX!");
return -EIO;
@@ -1446,8 +1414,7 @@
* ext4_ext_next_leaf_block:
* returns first allocated block from next leaf or EXT_MAX_BLOCKS
*/
-static ext4_lblk_t ext4_ext_next_leaf_block(struct inode *inode,
- struct ext4_ext_path *path)
+static ext4_lblk_t ext4_ext_next_leaf_block(struct ext4_ext_path *path)
{
int depth;
@@ -1757,7 +1724,6 @@
goto merge;
}
-repeat:
depth = ext_depth(inode);
eh = path[depth].p_hdr;
if (le16_to_cpu(eh->eh_entries) < le16_to_cpu(eh->eh_max))
@@ -1765,9 +1731,10 @@
/* probably next leaf has space for us? */
fex = EXT_LAST_EXTENT(eh);
- next = ext4_ext_next_leaf_block(inode, path);
- if (le32_to_cpu(newext->ee_block) > le32_to_cpu(fex->ee_block)
- && next != EXT_MAX_BLOCKS) {
+ next = EXT_MAX_BLOCKS;
+ if (le32_to_cpu(newext->ee_block) > le32_to_cpu(fex->ee_block))
+ next = ext4_ext_next_leaf_block(path);
+ if (next != EXT_MAX_BLOCKS) {
ext_debug("next leaf block - %d\n", next);
BUG_ON(npath != NULL);
npath = ext4_ext_find_extent(inode, next, NULL);
@@ -1779,7 +1746,7 @@
ext_debug("next leaf isn't full(%d)\n",
le16_to_cpu(eh->eh_entries));
path = npath;
- goto repeat;
+ goto has_space;
}
ext_debug("next leaf has no free space(%d,%d)\n",
le16_to_cpu(eh->eh_entries), le16_to_cpu(eh->eh_max));
@@ -1839,7 +1806,7 @@
ext4_ext_pblock(newext),
ext4_ext_is_uninitialized(newext),
ext4_ext_get_actual_len(newext),
- nearex, len, nearex + 1, nearex + 2);
+ nearex, len, nearex, nearex + 1);
memmove(nearex + 1, nearex, len);
path[depth].p_ext = nearex;
}
@@ -2052,7 +2019,7 @@
}
/*
- * ext4_ext_in_cache()
+ * ext4_ext_check_cache()
* Checks to see if the given block is in the cache.
* If it is, the cached extent is stored in the given
* cache extent pointer. If the cached extent is a hole,
@@ -2134,8 +2101,6 @@
/*
* ext4_ext_rm_idx:
* removes index from the index block.
- * It's used in truncate case only, thus all requests are for
- * last index in the block only.
*/
static int ext4_ext_rm_idx(handle_t *handle, struct inode *inode,
struct ext4_ext_path *path)
@@ -2153,6 +2118,13 @@
err = ext4_ext_get_access(handle, inode, path);
if (err)
return err;
+
+ if (path->p_idx != EXT_LAST_INDEX(path->p_hdr)) {
+ int len = EXT_LAST_INDEX(path->p_hdr) - path->p_idx;
+ len *= sizeof(struct ext4_extent_idx);
+ memmove(path->p_idx, path->p_idx + 1, len);
+ }
+
le16_add_cpu(&path->p_hdr->eh_entries, -1);
err = ext4_ext_dirty(handle, inode, path);
if (err)
@@ -2534,8 +2506,7 @@
return 1;
}
-static int ext4_ext_remove_space(struct inode *inode, ext4_lblk_t start,
- ext4_lblk_t end)
+static int ext4_ext_remove_space(struct inode *inode, ext4_lblk_t start)
{
struct super_block *sb = inode->i_sb;
int depth = ext_depth(inode);
@@ -2575,7 +2546,7 @@
if (i == depth) {
/* this is leaf block */
err = ext4_ext_rm_leaf(handle, inode, path,
- start, end);
+ start, EXT_MAX_BLOCKS - 1);
/* root level has p_bh == NULL, brelse() eats this */
brelse(path[i].p_bh);
path[i].p_bh = NULL;
@@ -3107,12 +3078,10 @@
struct ext4_ext_path *path)
{
struct ext4_extent *ex;
- struct ext4_extent_header *eh;
int depth;
int err = 0;
depth = ext_depth(inode);
- eh = path[depth].p_hdr;
ex = path[depth].p_ext;
ext_debug("ext4_convert_unwritten_extents_endio: inode %lu, logical"
@@ -3357,8 +3326,8 @@
trace_ext4_ext_map_blocks_enter(inode, map->m_lblk, map->m_len, flags);
/* check in cache */
- if (ext4_ext_in_cache(inode, map->m_lblk, &newex) &&
- ((flags & EXT4_GET_BLOCKS_PUNCH_OUT_EXT) == 0)) {
+ if (!(flags & EXT4_GET_BLOCKS_PUNCH_OUT_EXT) &&
+ ext4_ext_in_cache(inode, map->m_lblk, &newex)) {
if (!newex.ee_start_lo && !newex.ee_start_hi) {
if ((flags & EXT4_GET_BLOCKS_CREATE) == 0) {
/*
@@ -3497,8 +3466,27 @@
ext4_ext_mark_uninitialized(ex);
- err = ext4_ext_remove_space(inode, map->m_lblk,
- map->m_lblk + punched_out);
+ ext4_ext_invalidate_cache(inode);
+
+ err = ext4_ext_rm_leaf(handle, inode, path,
+ map->m_lblk, map->m_lblk + punched_out);
+
+ if (!err && path->p_hdr->eh_entries == 0) {
+ /*
+ * Punch hole freed all of this sub tree,
+ * so we need to correct eh_depth
+ */
+ err = ext4_ext_get_access(handle, inode, path);
+ if (err == 0) {
+ ext_inode_hdr(inode)->eh_depth = 0;
+ ext_inode_hdr(inode)->eh_max =
+ cpu_to_le16(ext4_ext_space_root(
+ inode, 0));
+
+ err = ext4_ext_dirty(
+ handle, inode, path);
+ }
+ }
goto out2;
}
@@ -3596,17 +3584,18 @@
}
err = check_eofblocks_fl(handle, inode, map->m_lblk, path, ar.len);
- if (err)
- goto out2;
-
- err = ext4_ext_insert_extent(handle, inode, path, &newex, flags);
+ if (!err)
+ err = ext4_ext_insert_extent(handle, inode, path,
+ &newex, flags);
if (err) {
+ int fb_flags = flags & EXT4_GET_BLOCKS_DELALLOC_RESERVE ?
+ EXT4_FREE_BLOCKS_NO_QUOT_UPDATE : 0;
/* free data blocks we just allocated */
/* not a good idea to call discard here directly,
* but otherwise we'd need to call it every free() */
ext4_discard_preallocations(inode);
ext4_free_blocks(handle, inode, NULL, ext4_ext_pblock(&newex),
- ext4_ext_get_actual_len(&newex), 0);
+ ext4_ext_get_actual_len(&newex), fb_flags);
goto out2;
}
@@ -3699,7 +3688,7 @@
last_block = (inode->i_size + sb->s_blocksize - 1)
>> EXT4_BLOCK_SIZE_BITS(sb);
- err = ext4_ext_remove_space(inode, last_block, EXT_MAX_BLOCKS - 1);
+ err = ext4_ext_remove_space(inode, last_block);
/* In a multi-transaction truncate, we only make the final
* transaction synchronous.
@@ -3835,7 +3824,7 @@
blkbits) >> blkbits))
new_size = offset + len;
else
- new_size = (map.m_lblk + ret) << blkbits;
+ new_size = ((loff_t) map.m_lblk + ret) << blkbits;
ext4_falloc_update_inode(inode, mode, new_size,
(map.m_flags & EXT4_MAP_NEW));
diff --git a/fs/ext4/fsync.c b/fs/ext4/fsync.c
index da3bed3..036f78f 100644
--- a/fs/ext4/fsync.c
+++ b/fs/ext4/fsync.c
@@ -129,15 +129,30 @@
{
struct writeback_control wbc;
struct dentry *dentry = NULL;
+ struct inode *next;
int ret = 0;
- while (inode && ext4_test_inode_state(inode, EXT4_STATE_NEWENTRY)) {
+ if (!ext4_test_inode_state(inode, EXT4_STATE_NEWENTRY))
+ return 0;
+ inode = igrab(inode);
+ while (ext4_test_inode_state(inode, EXT4_STATE_NEWENTRY)) {
ext4_clear_inode_state(inode, EXT4_STATE_NEWENTRY);
- dentry = list_entry(inode->i_dentry.next,
- struct dentry, d_alias);
- if (!dentry || !dentry->d_parent || !dentry->d_parent->d_inode)
+ dentry = NULL;
+ spin_lock(&inode->i_lock);
+ if (!list_empty(&inode->i_dentry)) {
+ dentry = list_first_entry(&inode->i_dentry,
+ struct dentry, d_alias);
+ dget(dentry);
+ }
+ spin_unlock(&inode->i_lock);
+ if (!dentry)
break;
- inode = dentry->d_parent->d_inode;
+ next = igrab(dentry->d_parent->d_inode);
+ dput(dentry);
+ if (!next)
+ break;
+ iput(inode);
+ inode = next;
ret = sync_mapping_buffers(inode->i_mapping);
if (ret)
break;
@@ -148,6 +163,7 @@
if (ret)
break;
}
+ iput(inode);
return ret;
}
diff --git a/fs/ext4/ialloc.c b/fs/ext4/ialloc.c
index 21bb2f6..9c63f27 100644
--- a/fs/ext4/ialloc.c
+++ b/fs/ext4/ialloc.c
@@ -1287,7 +1287,7 @@
group, used_blks,
ext4_itable_unused_count(sb, gdp));
ret = 1;
- goto out;
+ goto err_out;
}
blk = ext4_inode_table(sb, gdp) + used_blks;
diff --git a/fs/ext4/indirect.c b/fs/ext4/indirect.c
new file mode 100644
index 0000000..b8602cd
--- /dev/null
+++ b/fs/ext4/indirect.c
@@ -0,0 +1,1482 @@
+/*
+ * linux/fs/ext4/indirect.c
+ *
+ * from
+ *
+ * linux/fs/ext4/inode.c
+ *
+ * Copyright (C) 1992, 1993, 1994, 1995
+ * Remy Card (card@masi.ibp.fr)
+ * Laboratoire MASI - Institut Blaise Pascal
+ * Universite Pierre et Marie Curie (Paris VI)
+ *
+ * from
+ *
+ * linux/fs/minix/inode.c
+ *
+ * Copyright (C) 1991, 1992 Linus Torvalds
+ *
+ * Goal-directed block allocation by Stephen Tweedie
+ * (sct@redhat.com), 1993, 1998
+ */
+
+#include <linux/module.h>
+#include "ext4_jbd2.h"
+#include "truncate.h"
+
+#include <trace/events/ext4.h>
+
+typedef struct {
+ __le32 *p;
+ __le32 key;
+ struct buffer_head *bh;
+} Indirect;
+
+static inline void add_chain(Indirect *p, struct buffer_head *bh, __le32 *v)
+{
+ p->key = *(p->p = v);
+ p->bh = bh;
+}
+
+/**
+ * ext4_block_to_path - parse the block number into array of offsets
+ * @inode: inode in question (we are only interested in its superblock)
+ * @i_block: block number to be parsed
+ * @offsets: array to store the offsets in
+ * @boundary: set this non-zero if the referred-to block is likely to be
+ * followed (on disk) by an indirect block.
+ *
+ * To store the locations of file's data ext4 uses a data structure common
+ * for UNIX filesystems - tree of pointers anchored in the inode, with
+ * data blocks at leaves and indirect blocks in intermediate nodes.
+ * This function translates the block number into path in that tree -
+ * return value is the path length and @offsets[n] is the offset of
+ * pointer to (n+1)th node in the nth one. If @block is out of range
+ * (negative or too large) warning is printed and zero returned.
+ *
+ * Note: function doesn't find node addresses, so no IO is needed. All
+ * we need to know is the capacity of indirect blocks (taken from the
+ * inode->i_sb).
+ */
+
+/*
+ * Portability note: the last comparison (check that we fit into triple
+ * indirect block) is spelled differently, because otherwise on an
+ * architecture with 32-bit longs and 8Kb pages we might get into trouble
+ * if our filesystem had 8Kb blocks. We might use long long, but that would
+ * kill us on x86. Oh, well, at least the sign propagation does not matter -
+ * i_block would have to be negative in the very beginning, so we would not
+ * get there at all.
+ */
+
+static int ext4_block_to_path(struct inode *inode,
+ ext4_lblk_t i_block,
+ ext4_lblk_t offsets[4], int *boundary)
+{
+ int ptrs = EXT4_ADDR_PER_BLOCK(inode->i_sb);
+ int ptrs_bits = EXT4_ADDR_PER_BLOCK_BITS(inode->i_sb);
+ const long direct_blocks = EXT4_NDIR_BLOCKS,
+ indirect_blocks = ptrs,
+ double_blocks = (1 << (ptrs_bits * 2));
+ int n = 0;
+ int final = 0;
+
+ if (i_block < direct_blocks) {
+ offsets[n++] = i_block;
+ final = direct_blocks;
+ } else if ((i_block -= direct_blocks) < indirect_blocks) {
+ offsets[n++] = EXT4_IND_BLOCK;
+ offsets[n++] = i_block;
+ final = ptrs;
+ } else if ((i_block -= indirect_blocks) < double_blocks) {
+ offsets[n++] = EXT4_DIND_BLOCK;
+ offsets[n++] = i_block >> ptrs_bits;
+ offsets[n++] = i_block & (ptrs - 1);
+ final = ptrs;
+ } else if (((i_block -= double_blocks) >> (ptrs_bits * 2)) < ptrs) {
+ offsets[n++] = EXT4_TIND_BLOCK;
+ offsets[n++] = i_block >> (ptrs_bits * 2);
+ offsets[n++] = (i_block >> ptrs_bits) & (ptrs - 1);
+ offsets[n++] = i_block & (ptrs - 1);
+ final = ptrs;
+ } else {
+ ext4_warning(inode->i_sb, "block %lu > max in inode %lu",
+ i_block + direct_blocks +
+ indirect_blocks + double_blocks, inode->i_ino);
+ }
+ if (boundary)
+ *boundary = final - 1 - (i_block & (ptrs - 1));
+ return n;
+}
+
+/**
+ * ext4_get_branch - read the chain of indirect blocks leading to data
+ * @inode: inode in question
+ * @depth: depth of the chain (1 - direct pointer, etc.)
+ * @offsets: offsets of pointers in inode/indirect blocks
+ * @chain: place to store the result
+ * @err: here we store the error value
+ *
+ * Function fills the array of triples <key, p, bh> and returns %NULL
+ * if everything went OK or the pointer to the last filled triple
+ * (incomplete one) otherwise. Upon the return chain[i].key contains
+ * the number of (i+1)-th block in the chain (as it is stored in memory,
+ * i.e. little-endian 32-bit), chain[i].p contains the address of that
+ * number (it points into struct inode for i==0 and into the bh->b_data
+ * for i>0) and chain[i].bh points to the buffer_head of i-th indirect
+ * block for i>0 and NULL for i==0. In other words, it holds the block
+ * numbers of the chain, addresses they were taken from (and where we can
+ * verify that chain did not change) and buffer_heads hosting these
+ * numbers.
+ *
+ * Function stops when it stumbles upon zero pointer (absent block)
+ * (pointer to last triple returned, *@err == 0)
+ * or when it gets an IO error reading an indirect block
+ * (ditto, *@err == -EIO)
+ * or when it reads all @depth-1 indirect blocks successfully and finds
+ * the whole chain, all way to the data (returns %NULL, *err == 0).
+ *
+ * Need to be called with
+ * down_read(&EXT4_I(inode)->i_data_sem)
+ */
+static Indirect *ext4_get_branch(struct inode *inode, int depth,
+ ext4_lblk_t *offsets,
+ Indirect chain[4], int *err)
+{
+ struct super_block *sb = inode->i_sb;
+ Indirect *p = chain;
+ struct buffer_head *bh;
+
+ *err = 0;
+ /* i_data is not going away, no lock needed */
+ add_chain(chain, NULL, EXT4_I(inode)->i_data + *offsets);
+ if (!p->key)
+ goto no_block;
+ while (--depth) {
+ bh = sb_getblk(sb, le32_to_cpu(p->key));
+ if (unlikely(!bh))
+ goto failure;
+
+ if (!bh_uptodate_or_lock(bh)) {
+ if (bh_submit_read(bh) < 0) {
+ put_bh(bh);
+ goto failure;
+ }
+ /* validate block references */
+ if (ext4_check_indirect_blockref(inode, bh)) {
+ put_bh(bh);
+ goto failure;
+ }
+ }
+
+ add_chain(++p, bh, (__le32 *)bh->b_data + *++offsets);
+ /* Reader: end */
+ if (!p->key)
+ goto no_block;
+ }
+ return NULL;
+
+failure:
+ *err = -EIO;
+no_block:
+ return p;
+}
+
+/**
+ * ext4_find_near - find a place for allocation with sufficient locality
+ * @inode: owner
+ * @ind: descriptor of indirect block.
+ *
+ * This function returns the preferred place for block allocation.
+ * It is used when heuristic for sequential allocation fails.
+ * Rules are:
+ * + if there is a block to the left of our position - allocate near it.
+ * + if pointer will live in indirect block - allocate near that block.
+ * + if pointer will live in inode - allocate in the same
+ * cylinder group.
+ *
+ * In the latter case we colour the starting block by the callers PID to
+ * prevent it from clashing with concurrent allocations for a different inode
+ * in the same block group. The PID is used here so that functionally related
+ * files will be close-by on-disk.
+ *
+ * Caller must make sure that @ind is valid and will stay that way.
+ */
+static ext4_fsblk_t ext4_find_near(struct inode *inode, Indirect *ind)
+{
+ struct ext4_inode_info *ei = EXT4_I(inode);
+ __le32 *start = ind->bh ? (__le32 *) ind->bh->b_data : ei->i_data;
+ __le32 *p;
+
+ /* Try to find previous block */
+ for (p = ind->p - 1; p >= start; p--) {
+ if (*p)
+ return le32_to_cpu(*p);
+ }
+
+ /* No such thing, so let's try location of indirect block */
+ if (ind->bh)
+ return ind->bh->b_blocknr;
+
+ /*
+ * It is going to be referred to from the inode itself? OK, just put it
+ * into the same cylinder group then.
+ */
+ return ext4_inode_to_goal_block(inode);
+}
+
+/**
+ * ext4_find_goal - find a preferred place for allocation.
+ * @inode: owner
+ * @block: block we want
+ * @partial: pointer to the last triple within a chain
+ *
+ * Normally this function find the preferred place for block allocation,
+ * returns it.
+ * Because this is only used for non-extent files, we limit the block nr
+ * to 32 bits.
+ */
+static ext4_fsblk_t ext4_find_goal(struct inode *inode, ext4_lblk_t block,
+ Indirect *partial)
+{
+ ext4_fsblk_t goal;
+
+ /*
+ * XXX need to get goal block from mballoc's data structures
+ */
+
+ goal = ext4_find_near(inode, partial);
+ goal = goal & EXT4_MAX_BLOCK_FILE_PHYS;
+ return goal;
+}
+
+/**
+ * ext4_blks_to_allocate - Look up the block map and count the number
+ * of direct blocks need to be allocated for the given branch.
+ *
+ * @branch: chain of indirect blocks
+ * @k: number of blocks need for indirect blocks
+ * @blks: number of data blocks to be mapped.
+ * @blocks_to_boundary: the offset in the indirect block
+ *
+ * return the total number of blocks to be allocate, including the
+ * direct and indirect blocks.
+ */
+static int ext4_blks_to_allocate(Indirect *branch, int k, unsigned int blks,
+ int blocks_to_boundary)
+{
+ unsigned int count = 0;
+
+ /*
+ * Simple case, [t,d]Indirect block(s) has not allocated yet
+ * then it's clear blocks on that path have not allocated
+ */
+ if (k > 0) {
+ /* right now we don't handle cross boundary allocation */
+ if (blks < blocks_to_boundary + 1)
+ count += blks;
+ else
+ count += blocks_to_boundary + 1;
+ return count;
+ }
+
+ count++;
+ while (count < blks && count <= blocks_to_boundary &&
+ le32_to_cpu(*(branch[0].p + count)) == 0) {
+ count++;
+ }
+ return count;
+}
+
+/**
+ * ext4_alloc_blocks: multiple allocate blocks needed for a branch
+ * @handle: handle for this transaction
+ * @inode: inode which needs allocated blocks
+ * @iblock: the logical block to start allocated at
+ * @goal: preferred physical block of allocation
+ * @indirect_blks: the number of blocks need to allocate for indirect
+ * blocks
+ * @blks: number of desired blocks
+ * @new_blocks: on return it will store the new block numbers for
+ * the indirect blocks(if needed) and the first direct block,
+ * @err: on return it will store the error code
+ *
+ * This function will return the number of blocks allocated as
+ * requested by the passed-in parameters.
+ */
+static int ext4_alloc_blocks(handle_t *handle, struct inode *inode,
+ ext4_lblk_t iblock, ext4_fsblk_t goal,
+ int indirect_blks, int blks,
+ ext4_fsblk_t new_blocks[4], int *err)
+{
+ struct ext4_allocation_request ar;
+ int target, i;
+ unsigned long count = 0, blk_allocated = 0;
+ int index = 0;
+ ext4_fsblk_t current_block = 0;
+ int ret = 0;
+
+ /*
+ * Here we try to allocate the requested multiple blocks at once,
+ * on a best-effort basis.
+ * To build a branch, we should allocate blocks for
+ * the indirect blocks(if not allocated yet), and at least
+ * the first direct block of this branch. That's the
+ * minimum number of blocks need to allocate(required)
+ */
+ /* first we try to allocate the indirect blocks */
+ target = indirect_blks;
+ while (target > 0) {
+ count = target;
+ /* allocating blocks for indirect blocks and direct blocks */
+ current_block = ext4_new_meta_blocks(handle, inode, goal,
+ 0, &count, err);
+ if (*err)
+ goto failed_out;
+
+ if (unlikely(current_block + count > EXT4_MAX_BLOCK_FILE_PHYS)) {
+ EXT4_ERROR_INODE(inode,
+ "current_block %llu + count %lu > %d!",
+ current_block, count,
+ EXT4_MAX_BLOCK_FILE_PHYS);
+ *err = -EIO;
+ goto failed_out;
+ }
+
+ target -= count;
+ /* allocate blocks for indirect blocks */
+ while (index < indirect_blks && count) {
+ new_blocks[index++] = current_block++;
+ count--;
+ }
+ if (count > 0) {
+ /*
+ * save the new block number
+ * for the first direct block
+ */
+ new_blocks[index] = current_block;
+ printk(KERN_INFO "%s returned more blocks than "
+ "requested\n", __func__);
+ WARN_ON(1);
+ break;
+ }
+ }
+
+ target = blks - count ;
+ blk_allocated = count;
+ if (!target)
+ goto allocated;
+ /* Now allocate data blocks */
+ memset(&ar, 0, sizeof(ar));
+ ar.inode = inode;
+ ar.goal = goal;
+ ar.len = target;
+ ar.logical = iblock;
+ if (S_ISREG(inode->i_mode))
+ /* enable in-core preallocation only for regular files */
+ ar.flags = EXT4_MB_HINT_DATA;
+
+ current_block = ext4_mb_new_blocks(handle, &ar, err);
+ if (unlikely(current_block + ar.len > EXT4_MAX_BLOCK_FILE_PHYS)) {
+ EXT4_ERROR_INODE(inode,
+ "current_block %llu + ar.len %d > %d!",
+ current_block, ar.len,
+ EXT4_MAX_BLOCK_FILE_PHYS);
+ *err = -EIO;
+ goto failed_out;
+ }
+
+ if (*err && (target == blks)) {
+ /*
+ * if the allocation failed and we didn't allocate
+ * any blocks before
+ */
+ goto failed_out;
+ }
+ if (!*err) {
+ if (target == blks) {
+ /*
+ * save the new block number
+ * for the first direct block
+ */
+ new_blocks[index] = current_block;
+ }
+ blk_allocated += ar.len;
+ }
+allocated:
+ /* total number of blocks allocated for direct blocks */
+ ret = blk_allocated;
+ *err = 0;
+ return ret;
+failed_out:
+ for (i = 0; i < index; i++)
+ ext4_free_blocks(handle, inode, NULL, new_blocks[i], 1, 0);
+ return ret;
+}
+
+/**
+ * ext4_alloc_branch - allocate and set up a chain of blocks.
+ * @handle: handle for this transaction
+ * @inode: owner
+ * @indirect_blks: number of allocated indirect blocks
+ * @blks: number of allocated direct blocks
+ * @goal: preferred place for allocation
+ * @offsets: offsets (in the blocks) to store the pointers to next.
+ * @branch: place to store the chain in.
+ *
+ * This function allocates blocks, zeroes out all but the last one,
+ * links them into chain and (if we are synchronous) writes them to disk.
+ * In other words, it prepares a branch that can be spliced onto the
+ * inode. It stores the information about that chain in the branch[], in
+ * the same format as ext4_get_branch() would do. We are calling it after
+ * we had read the existing part of chain and partial points to the last
+ * triple of that (one with zero ->key). Upon the exit we have the same
+ * picture as after the successful ext4_get_block(), except that in one
+ * place chain is disconnected - *branch->p is still zero (we did not
+ * set the last link), but branch->key contains the number that should
+ * be placed into *branch->p to fill that gap.
+ *
+ * If allocation fails we free all blocks we've allocated (and forget
+ * their buffer_heads) and return the error value the from failed
+ * ext4_alloc_block() (normally -ENOSPC). Otherwise we set the chain
+ * as described above and return 0.
+ */
+static int ext4_alloc_branch(handle_t *handle, struct inode *inode,
+ ext4_lblk_t iblock, int indirect_blks,
+ int *blks, ext4_fsblk_t goal,
+ ext4_lblk_t *offsets, Indirect *branch)
+{
+ int blocksize = inode->i_sb->s_blocksize;
+ int i, n = 0;
+ int err = 0;
+ struct buffer_head *bh;
+ int num;
+ ext4_fsblk_t new_blocks[4];
+ ext4_fsblk_t current_block;
+
+ num = ext4_alloc_blocks(handle, inode, iblock, goal, indirect_blks,
+ *blks, new_blocks, &err);
+ if (err)
+ return err;
+
+ branch[0].key = cpu_to_le32(new_blocks[0]);
+ /*
+ * metadata blocks and data blocks are allocated.
+ */
+ for (n = 1; n <= indirect_blks; n++) {
+ /*
+ * Get buffer_head for parent block, zero it out
+ * and set the pointer to new one, then send
+ * parent to disk.
+ */
+ bh = sb_getblk(inode->i_sb, new_blocks[n-1]);
+ if (unlikely(!bh)) {
+ err = -EIO;
+ goto failed;
+ }
+
+ branch[n].bh = bh;
+ lock_buffer(bh);
+ BUFFER_TRACE(bh, "call get_create_access");
+ err = ext4_journal_get_create_access(handle, bh);
+ if (err) {
+ /* Don't brelse(bh) here; it's done in
+ * ext4_journal_forget() below */
+ unlock_buffer(bh);
+ goto failed;
+ }
+
+ memset(bh->b_data, 0, blocksize);
+ branch[n].p = (__le32 *) bh->b_data + offsets[n];
+ branch[n].key = cpu_to_le32(new_blocks[n]);
+ *branch[n].p = branch[n].key;
+ if (n == indirect_blks) {
+ current_block = new_blocks[n];
+ /*
+ * End of chain, update the last new metablock of
+ * the chain to point to the new allocated
+ * data blocks numbers
+ */
+ for (i = 1; i < num; i++)
+ *(branch[n].p + i) = cpu_to_le32(++current_block);
+ }
+ BUFFER_TRACE(bh, "marking uptodate");
+ set_buffer_uptodate(bh);
+ unlock_buffer(bh);
+
+ BUFFER_TRACE(bh, "call ext4_handle_dirty_metadata");
+ err = ext4_handle_dirty_metadata(handle, inode, bh);
+ if (err)
+ goto failed;
+ }
+ *blks = num;
+ return err;
+failed:
+ /* Allocation failed, free what we already allocated */
+ ext4_free_blocks(handle, inode, NULL, new_blocks[0], 1, 0);
+ for (i = 1; i <= n ; i++) {
+ /*
+ * branch[i].bh is newly allocated, so there is no
+ * need to revoke the block, which is why we don't
+ * need to set EXT4_FREE_BLOCKS_METADATA.
+ */
+ ext4_free_blocks(handle, inode, NULL, new_blocks[i], 1,
+ EXT4_FREE_BLOCKS_FORGET);
+ }
+ for (i = n+1; i < indirect_blks; i++)
+ ext4_free_blocks(handle, inode, NULL, new_blocks[i], 1, 0);
+
+ ext4_free_blocks(handle, inode, NULL, new_blocks[i], num, 0);
+
+ return err;
+}
+
+/**
+ * ext4_splice_branch - splice the allocated branch onto inode.
+ * @handle: handle for this transaction
+ * @inode: owner
+ * @block: (logical) number of block we are adding
+ * @chain: chain of indirect blocks (with a missing link - see
+ * ext4_alloc_branch)
+ * @where: location of missing link
+ * @num: number of indirect blocks we are adding
+ * @blks: number of direct blocks we are adding
+ *
+ * This function fills the missing link and does all housekeeping needed in
+ * inode (->i_blocks, etc.). In case of success we end up with the full
+ * chain to new block and return 0.
+ */
+static int ext4_splice_branch(handle_t *handle, struct inode *inode,
+ ext4_lblk_t block, Indirect *where, int num,
+ int blks)
+{
+ int i;
+ int err = 0;
+ ext4_fsblk_t current_block;
+
+ /*
+ * If we're splicing into a [td]indirect block (as opposed to the
+ * inode) then we need to get write access to the [td]indirect block
+ * before the splice.
+ */
+ if (where->bh) {
+ BUFFER_TRACE(where->bh, "get_write_access");
+ err = ext4_journal_get_write_access(handle, where->bh);
+ if (err)
+ goto err_out;
+ }
+ /* That's it */
+
+ *where->p = where->key;
+
+ /*
+ * Update the host buffer_head or inode to point to more just allocated
+ * direct blocks blocks
+ */
+ if (num == 0 && blks > 1) {
+ current_block = le32_to_cpu(where->key) + 1;
+ for (i = 1; i < blks; i++)
+ *(where->p + i) = cpu_to_le32(current_block++);
+ }
+
+ /* We are done with atomic stuff, now do the rest of housekeeping */
+ /* had we spliced it onto indirect block? */
+ if (where->bh) {
+ /*
+ * If we spliced it onto an indirect block, we haven't
+ * altered the inode. Note however that if it is being spliced
+ * onto an indirect block at the very end of the file (the
+ * file is growing) then we *will* alter the inode to reflect
+ * the new i_size. But that is not done here - it is done in
+ * generic_commit_write->__mark_inode_dirty->ext4_dirty_inode.
+ */
+ jbd_debug(5, "splicing indirect only\n");
+ BUFFER_TRACE(where->bh, "call ext4_handle_dirty_metadata");
+ err = ext4_handle_dirty_metadata(handle, inode, where->bh);
+ if (err)
+ goto err_out;
+ } else {
+ /*
+ * OK, we spliced it into the inode itself on a direct block.
+ */
+ ext4_mark_inode_dirty(handle, inode);
+ jbd_debug(5, "splicing direct\n");
+ }
+ return err;
+
+err_out:
+ for (i = 1; i <= num; i++) {
+ /*
+ * branch[i].bh is newly allocated, so there is no
+ * need to revoke the block, which is why we don't
+ * need to set EXT4_FREE_BLOCKS_METADATA.
+ */
+ ext4_free_blocks(handle, inode, where[i].bh, 0, 1,
+ EXT4_FREE_BLOCKS_FORGET);
+ }
+ ext4_free_blocks(handle, inode, NULL, le32_to_cpu(where[num].key),
+ blks, 0);
+
+ return err;
+}
+
+/*
+ * The ext4_ind_map_blocks() function handles non-extents inodes
+ * (i.e., using the traditional indirect/double-indirect i_blocks
+ * scheme) for ext4_map_blocks().
+ *
+ * Allocation strategy is simple: if we have to allocate something, we will
+ * have to go the whole way to leaf. So let's do it before attaching anything
+ * to tree, set linkage between the newborn blocks, write them if sync is
+ * required, recheck the path, free and repeat if check fails, otherwise
+ * set the last missing link (that will protect us from any truncate-generated
+ * removals - all blocks on the path are immune now) and possibly force the
+ * write on the parent block.
+ * That has a nice additional property: no special recovery from the failed
+ * allocations is needed - we simply release blocks and do not touch anything
+ * reachable from inode.
+ *
+ * `handle' can be NULL if create == 0.
+ *
+ * return > 0, # of blocks mapped or allocated.
+ * return = 0, if plain lookup failed.
+ * return < 0, error case.
+ *
+ * The ext4_ind_get_blocks() function should be called with
+ * down_write(&EXT4_I(inode)->i_data_sem) if allocating filesystem
+ * blocks (i.e., flags has EXT4_GET_BLOCKS_CREATE set) or
+ * down_read(&EXT4_I(inode)->i_data_sem) if not allocating file system
+ * blocks.
+ */
+int ext4_ind_map_blocks(handle_t *handle, struct inode *inode,
+ struct ext4_map_blocks *map,
+ int flags)
+{
+ int err = -EIO;
+ ext4_lblk_t offsets[4];
+ Indirect chain[4];
+ Indirect *partial;
+ ext4_fsblk_t goal;
+ int indirect_blks;
+ int blocks_to_boundary = 0;
+ int depth;
+ int count = 0;
+ ext4_fsblk_t first_block = 0;
+
+ trace_ext4_ind_map_blocks_enter(inode, map->m_lblk, map->m_len, flags);
+ J_ASSERT(!(ext4_test_inode_flag(inode, EXT4_INODE_EXTENTS)));
+ J_ASSERT(handle != NULL || (flags & EXT4_GET_BLOCKS_CREATE) == 0);
+ depth = ext4_block_to_path(inode, map->m_lblk, offsets,
+ &blocks_to_boundary);
+
+ if (depth == 0)
+ goto out;
+
+ partial = ext4_get_branch(inode, depth, offsets, chain, &err);
+
+ /* Simplest case - block found, no allocation needed */
+ if (!partial) {
+ first_block = le32_to_cpu(chain[depth - 1].key);
+ count++;
+ /*map more blocks*/
+ while (count < map->m_len && count <= blocks_to_boundary) {
+ ext4_fsblk_t blk;
+
+ blk = le32_to_cpu(*(chain[depth-1].p + count));
+
+ if (blk == first_block + count)
+ count++;
+ else
+ break;
+ }
+ goto got_it;
+ }
+
+ /* Next simple case - plain lookup or failed read of indirect block */
+ if ((flags & EXT4_GET_BLOCKS_CREATE) == 0 || err == -EIO)
+ goto cleanup;
+
+ /*
+ * Okay, we need to do block allocation.
+ */
+ goal = ext4_find_goal(inode, map->m_lblk, partial);
+
+ /* the number of blocks need to allocate for [d,t]indirect blocks */
+ indirect_blks = (chain + depth) - partial - 1;
+
+ /*
+ * Next look up the indirect map to count the totoal number of
+ * direct blocks to allocate for this branch.
+ */
+ count = ext4_blks_to_allocate(partial, indirect_blks,
+ map->m_len, blocks_to_boundary);
+ /*
+ * Block out ext4_truncate while we alter the tree
+ */
+ err = ext4_alloc_branch(handle, inode, map->m_lblk, indirect_blks,
+ &count, goal,
+ offsets + (partial - chain), partial);
+
+ /*
+ * The ext4_splice_branch call will free and forget any buffers
+ * on the new chain if there is a failure, but that risks using
+ * up transaction credits, especially for bitmaps where the
+ * credits cannot be returned. Can we handle this somehow? We
+ * may need to return -EAGAIN upwards in the worst case. --sct
+ */
+ if (!err)
+ err = ext4_splice_branch(handle, inode, map->m_lblk,
+ partial, indirect_blks, count);
+ if (err)
+ goto cleanup;
+
+ map->m_flags |= EXT4_MAP_NEW;
+
+ ext4_update_inode_fsync_trans(handle, inode, 1);
+got_it:
+ map->m_flags |= EXT4_MAP_MAPPED;
+ map->m_pblk = le32_to_cpu(chain[depth-1].key);
+ map->m_len = count;
+ if (count > blocks_to_boundary)
+ map->m_flags |= EXT4_MAP_BOUNDARY;
+ err = count;
+ /* Clean up and exit */
+ partial = chain + depth - 1; /* the whole chain */
+cleanup:
+ while (partial > chain) {
+ BUFFER_TRACE(partial->bh, "call brelse");
+ brelse(partial->bh);
+ partial--;
+ }
+out:
+ trace_ext4_ind_map_blocks_exit(inode, map->m_lblk,
+ map->m_pblk, map->m_len, err);
+ return err;
+}
+
+/*
+ * O_DIRECT for ext3 (or indirect map) based files
+ *
+ * If the O_DIRECT write will extend the file then add this inode to the
+ * orphan list. So recovery will truncate it back to the original size
+ * if the machine crashes during the write.
+ *
+ * If the O_DIRECT write is intantiating holes inside i_size and the machine
+ * crashes then stale disk data _may_ be exposed inside the file. But current
+ * VFS code falls back into buffered path in that case so we are safe.
+ */
+ssize_t ext4_ind_direct_IO(int rw, struct kiocb *iocb,
+ const struct iovec *iov, loff_t offset,
+ unsigned long nr_segs)
+{
+ struct file *file = iocb->ki_filp;
+ struct inode *inode = file->f_mapping->host;
+ struct ext4_inode_info *ei = EXT4_I(inode);
+ handle_t *handle;
+ ssize_t ret;
+ int orphan = 0;
+ size_t count = iov_length(iov, nr_segs);
+ int retries = 0;
+
+ if (rw == WRITE) {
+ loff_t final_size = offset + count;
+
+ if (final_size > inode->i_size) {
+ /* Credits for sb + inode write */
+ handle = ext4_journal_start(inode, 2);
+ if (IS_ERR(handle)) {
+ ret = PTR_ERR(handle);
+ goto out;
+ }
+ ret = ext4_orphan_add(handle, inode);
+ if (ret) {
+ ext4_journal_stop(handle);
+ goto out;
+ }
+ orphan = 1;
+ ei->i_disksize = inode->i_size;
+ ext4_journal_stop(handle);
+ }
+ }
+
+retry:
+ if (rw == READ && ext4_should_dioread_nolock(inode))
+ ret = __blockdev_direct_IO(rw, iocb, inode,
+ inode->i_sb->s_bdev, iov,
+ offset, nr_segs,
+ ext4_get_block, NULL, NULL, 0);
+ else {
+ ret = blockdev_direct_IO(rw, iocb, inode, iov,
+ offset, nr_segs, ext4_get_block);
+
+ if (unlikely((rw & WRITE) && ret < 0)) {
+ loff_t isize = i_size_read(inode);
+ loff_t end = offset + iov_length(iov, nr_segs);
+
+ if (end > isize)
+ ext4_truncate_failed_write(inode);
+ }
+ }
+ if (ret == -ENOSPC && ext4_should_retry_alloc(inode->i_sb, &retries))
+ goto retry;
+
+ if (orphan) {
+ int err;
+
+ /* Credits for sb + inode write */
+ handle = ext4_journal_start(inode, 2);
+ if (IS_ERR(handle)) {
+ /* This is really bad luck. We've written the data
+ * but cannot extend i_size. Bail out and pretend
+ * the write failed... */
+ ret = PTR_ERR(handle);
+ if (inode->i_nlink)
+ ext4_orphan_del(NULL, inode);
+
+ goto out;
+ }
+ if (inode->i_nlink)
+ ext4_orphan_del(handle, inode);
+ if (ret > 0) {
+ loff_t end = offset + ret;
+ if (end > inode->i_size) {
+ ei->i_disksize = end;
+ i_size_write(inode, end);
+ /*
+ * We're going to return a positive `ret'
+ * here due to non-zero-length I/O, so there's
+ * no way of reporting error returns from
+ * ext4_mark_inode_dirty() to userspace. So
+ * ignore it.
+ */
+ ext4_mark_inode_dirty(handle, inode);
+ }
+ }
+ err = ext4_journal_stop(handle);
+ if (ret == 0)
+ ret = err;
+ }
+out:
+ return ret;
+}
+
+/*
+ * Calculate the number of metadata blocks need to reserve
+ * to allocate a new block at @lblocks for non extent file based file
+ */
+int ext4_ind_calc_metadata_amount(struct inode *inode, sector_t lblock)
+{
+ struct ext4_inode_info *ei = EXT4_I(inode);
+ sector_t dind_mask = ~((sector_t)EXT4_ADDR_PER_BLOCK(inode->i_sb) - 1);
+ int blk_bits;
+
+ if (lblock < EXT4_NDIR_BLOCKS)
+ return 0;
+
+ lblock -= EXT4_NDIR_BLOCKS;
+
+ if (ei->i_da_metadata_calc_len &&
+ (lblock & dind_mask) == ei->i_da_metadata_calc_last_lblock) {
+ ei->i_da_metadata_calc_len++;
+ return 0;
+ }
+ ei->i_da_metadata_calc_last_lblock = lblock & dind_mask;
+ ei->i_da_metadata_calc_len = 1;
+ blk_bits = order_base_2(lblock);
+ return (blk_bits / EXT4_ADDR_PER_BLOCK_BITS(inode->i_sb)) + 1;
+}
+
+int ext4_ind_trans_blocks(struct inode *inode, int nrblocks, int chunk)
+{
+ int indirects;
+
+ /* if nrblocks are contiguous */
+ if (chunk) {
+ /*
+ * With N contiguous data blocks, we need at most
+ * N/EXT4_ADDR_PER_BLOCK(inode->i_sb) + 1 indirect blocks,
+ * 2 dindirect blocks, and 1 tindirect block
+ */
+ return DIV_ROUND_UP(nrblocks,
+ EXT4_ADDR_PER_BLOCK(inode->i_sb)) + 4;
+ }
+ /*
+ * if nrblocks are not contiguous, worse case, each block touch
+ * a indirect block, and each indirect block touch a double indirect
+ * block, plus a triple indirect block
+ */
+ indirects = nrblocks * 2 + 1;
+ return indirects;
+}
+
+/*
+ * Truncate transactions can be complex and absolutely huge. So we need to
+ * be able to restart the transaction at a conventient checkpoint to make
+ * sure we don't overflow the journal.
+ *
+ * start_transaction gets us a new handle for a truncate transaction,
+ * and extend_transaction tries to extend the existing one a bit. If
+ * extend fails, we need to propagate the failure up and restart the
+ * transaction in the top-level truncate loop. --sct
+ */
+static handle_t *start_transaction(struct inode *inode)
+{
+ handle_t *result;
+
+ result = ext4_journal_start(inode, ext4_blocks_for_truncate(inode));
+ if (!IS_ERR(result))
+ return result;
+
+ ext4_std_error(inode->i_sb, PTR_ERR(result));
+ return result;
+}
+
+/*
+ * Try to extend this transaction for the purposes of truncation.
+ *
+ * Returns 0 if we managed to create more room. If we can't create more
+ * room, and the transaction must be restarted we return 1.
+ */
+static int try_to_extend_transaction(handle_t *handle, struct inode *inode)
+{
+ if (!ext4_handle_valid(handle))
+ return 0;
+ if (ext4_handle_has_enough_credits(handle, EXT4_RESERVE_TRANS_BLOCKS+1))
+ return 0;
+ if (!ext4_journal_extend(handle, ext4_blocks_for_truncate(inode)))
+ return 0;
+ return 1;
+}
+
+/*
+ * Probably it should be a library function... search for first non-zero word
+ * or memcmp with zero_page, whatever is better for particular architecture.
+ * Linus?
+ */
+static inline int all_zeroes(__le32 *p, __le32 *q)
+{
+ while (p < q)
+ if (*p++)
+ return 0;
+ return 1;
+}
+
+/**
+ * ext4_find_shared - find the indirect blocks for partial truncation.
+ * @inode: inode in question
+ * @depth: depth of the affected branch
+ * @offsets: offsets of pointers in that branch (see ext4_block_to_path)
+ * @chain: place to store the pointers to partial indirect blocks
+ * @top: place to the (detached) top of branch
+ *
+ * This is a helper function used by ext4_truncate().
+ *
+ * When we do truncate() we may have to clean the ends of several
+ * indirect blocks but leave the blocks themselves alive. Block is
+ * partially truncated if some data below the new i_size is referred
+ * from it (and it is on the path to the first completely truncated
+ * data block, indeed). We have to free the top of that path along
+ * with everything to the right of the path. Since no allocation
+ * past the truncation point is possible until ext4_truncate()
+ * finishes, we may safely do the latter, but top of branch may
+ * require special attention - pageout below the truncation point
+ * might try to populate it.
+ *
+ * We atomically detach the top of branch from the tree, store the
+ * block number of its root in *@top, pointers to buffer_heads of
+ * partially truncated blocks - in @chain[].bh and pointers to
+ * their last elements that should not be removed - in
+ * @chain[].p. Return value is the pointer to last filled element
+ * of @chain.
+ *
+ * The work left to caller to do the actual freeing of subtrees:
+ * a) free the subtree starting from *@top
+ * b) free the subtrees whose roots are stored in
+ * (@chain[i].p+1 .. end of @chain[i].bh->b_data)
+ * c) free the subtrees growing from the inode past the @chain[0].
+ * (no partially truncated stuff there). */
+
+static Indirect *ext4_find_shared(struct inode *inode, int depth,
+ ext4_lblk_t offsets[4], Indirect chain[4],
+ __le32 *top)
+{
+ Indirect *partial, *p;
+ int k, err;
+
+ *top = 0;
+ /* Make k index the deepest non-null offset + 1 */
+ for (k = depth; k > 1 && !offsets[k-1]; k--)
+ ;
+ partial = ext4_get_branch(inode, k, offsets, chain, &err);
+ /* Writer: pointers */
+ if (!partial)
+ partial = chain + k-1;
+ /*
+ * If the branch acquired continuation since we've looked at it -
+ * fine, it should all survive and (new) top doesn't belong to us.
+ */
+ if (!partial->key && *partial->p)
+ /* Writer: end */
+ goto no_top;
+ for (p = partial; (p > chain) && all_zeroes((__le32 *) p->bh->b_data, p->p); p--)
+ ;
+ /*
+ * OK, we've found the last block that must survive. The rest of our
+ * branch should be detached before unlocking. However, if that rest
+ * of branch is all ours and does not grow immediately from the inode
+ * it's easier to cheat and just decrement partial->p.
+ */
+ if (p == chain + k - 1 && p > chain) {
+ p->p--;
+ } else {
+ *top = *p->p;
+ /* Nope, don't do this in ext4. Must leave the tree intact */
+#if 0
+ *p->p = 0;
+#endif
+ }
+ /* Writer: end */
+
+ while (partial > p) {
+ brelse(partial->bh);
+ partial--;
+ }
+no_top:
+ return partial;
+}
+
+/*
+ * Zero a number of block pointers in either an inode or an indirect block.
+ * If we restart the transaction we must again get write access to the
+ * indirect block for further modification.
+ *
+ * We release `count' blocks on disk, but (last - first) may be greater
+ * than `count' because there can be holes in there.
+ *
+ * Return 0 on success, 1 on invalid block range
+ * and < 0 on fatal error.
+ */
+static int ext4_clear_blocks(handle_t *handle, struct inode *inode,
+ struct buffer_head *bh,
+ ext4_fsblk_t block_to_free,
+ unsigned long count, __le32 *first,
+ __le32 *last)
+{
+ __le32 *p;
+ int flags = EXT4_FREE_BLOCKS_FORGET | EXT4_FREE_BLOCKS_VALIDATED;
+ int err;
+
+ if (S_ISDIR(inode->i_mode) || S_ISLNK(inode->i_mode))
+ flags |= EXT4_FREE_BLOCKS_METADATA;
+
+ if (!ext4_data_block_valid(EXT4_SB(inode->i_sb), block_to_free,
+ count)) {
+ EXT4_ERROR_INODE(inode, "attempt to clear invalid "
+ "blocks %llu len %lu",
+ (unsigned long long) block_to_free, count);
+ return 1;
+ }
+
+ if (try_to_extend_transaction(handle, inode)) {
+ if (bh) {
+ BUFFER_TRACE(bh, "call ext4_handle_dirty_metadata");
+ err = ext4_handle_dirty_metadata(handle, inode, bh);
+ if (unlikely(err))
+ goto out_err;
+ }
+ err = ext4_mark_inode_dirty(handle, inode);
+ if (unlikely(err))
+ goto out_err;
+ err = ext4_truncate_restart_trans(handle, inode,
+ ext4_blocks_for_truncate(inode));
+ if (unlikely(err))
+ goto out_err;
+ if (bh) {
+ BUFFER_TRACE(bh, "retaking write access");
+ err = ext4_journal_get_write_access(handle, bh);
+ if (unlikely(err))
+ goto out_err;
+ }
+ }
+
+ for (p = first; p < last; p++)
+ *p = 0;
+
+ ext4_free_blocks(handle, inode, NULL, block_to_free, count, flags);
+ return 0;
+out_err:
+ ext4_std_error(inode->i_sb, err);
+ return err;
+}
+
+/**
+ * ext4_free_data - free a list of data blocks
+ * @handle: handle for this transaction
+ * @inode: inode we are dealing with
+ * @this_bh: indirect buffer_head which contains *@first and *@last
+ * @first: array of block numbers
+ * @last: points immediately past the end of array
+ *
+ * We are freeing all blocks referred from that array (numbers are stored as
+ * little-endian 32-bit) and updating @inode->i_blocks appropriately.
+ *
+ * We accumulate contiguous runs of blocks to free. Conveniently, if these
+ * blocks are contiguous then releasing them at one time will only affect one
+ * or two bitmap blocks (+ group descriptor(s) and superblock) and we won't
+ * actually use a lot of journal space.
+ *
+ * @this_bh will be %NULL if @first and @last point into the inode's direct
+ * block pointers.
+ */
+static void ext4_free_data(handle_t *handle, struct inode *inode,
+ struct buffer_head *this_bh,
+ __le32 *first, __le32 *last)
+{
+ ext4_fsblk_t block_to_free = 0; /* Starting block # of a run */
+ unsigned long count = 0; /* Number of blocks in the run */
+ __le32 *block_to_free_p = NULL; /* Pointer into inode/ind
+ corresponding to
+ block_to_free */
+ ext4_fsblk_t nr; /* Current block # */
+ __le32 *p; /* Pointer into inode/ind
+ for current block */
+ int err = 0;
+
+ if (this_bh) { /* For indirect block */
+ BUFFER_TRACE(this_bh, "get_write_access");
+ err = ext4_journal_get_write_access(handle, this_bh);
+ /* Important: if we can't update the indirect pointers
+ * to the blocks, we can't free them. */
+ if (err)
+ return;
+ }
+
+ for (p = first; p < last; p++) {
+ nr = le32_to_cpu(*p);
+ if (nr) {
+ /* accumulate blocks to free if they're contiguous */
+ if (count == 0) {
+ block_to_free = nr;
+ block_to_free_p = p;
+ count = 1;
+ } else if (nr == block_to_free + count) {
+ count++;
+ } else {
+ err = ext4_clear_blocks(handle, inode, this_bh,
+ block_to_free, count,
+ block_to_free_p, p);
+ if (err)
+ break;
+ block_to_free = nr;
+ block_to_free_p = p;
+ count = 1;
+ }
+ }
+ }
+
+ if (!err && count > 0)
+ err = ext4_clear_blocks(handle, inode, this_bh, block_to_free,
+ count, block_to_free_p, p);
+ if (err < 0)
+ /* fatal error */
+ return;
+
+ if (this_bh) {
+ BUFFER_TRACE(this_bh, "call ext4_handle_dirty_metadata");
+
+ /*
+ * The buffer head should have an attached journal head at this
+ * point. However, if the data is corrupted and an indirect
+ * block pointed to itself, it would have been detached when
+ * the block was cleared. Check for this instead of OOPSing.
+ */
+ if ((EXT4_JOURNAL(inode) == NULL) || bh2jh(this_bh))
+ ext4_handle_dirty_metadata(handle, inode, this_bh);
+ else
+ EXT4_ERROR_INODE(inode,
+ "circular indirect block detected at "
+ "block %llu",
+ (unsigned long long) this_bh->b_blocknr);
+ }
+}
+
+/**
+ * ext4_free_branches - free an array of branches
+ * @handle: JBD handle for this transaction
+ * @inode: inode we are dealing with
+ * @parent_bh: the buffer_head which contains *@first and *@last
+ * @first: array of block numbers
+ * @last: pointer immediately past the end of array
+ * @depth: depth of the branches to free
+ *
+ * We are freeing all blocks referred from these branches (numbers are
+ * stored as little-endian 32-bit) and updating @inode->i_blocks
+ * appropriately.
+ */
+static void ext4_free_branches(handle_t *handle, struct inode *inode,
+ struct buffer_head *parent_bh,
+ __le32 *first, __le32 *last, int depth)
+{
+ ext4_fsblk_t nr;
+ __le32 *p;
+
+ if (ext4_handle_is_aborted(handle))
+ return;
+
+ if (depth--) {
+ struct buffer_head *bh;
+ int addr_per_block = EXT4_ADDR_PER_BLOCK(inode->i_sb);
+ p = last;
+ while (--p >= first) {
+ nr = le32_to_cpu(*p);
+ if (!nr)
+ continue; /* A hole */
+
+ if (!ext4_data_block_valid(EXT4_SB(inode->i_sb),
+ nr, 1)) {
+ EXT4_ERROR_INODE(inode,
+ "invalid indirect mapped "
+ "block %lu (level %d)",
+ (unsigned long) nr, depth);
+ break;
+ }
+
+ /* Go read the buffer for the next level down */
+ bh = sb_bread(inode->i_sb, nr);
+
+ /*
+ * A read failure? Report error and clear slot
+ * (should be rare).
+ */
+ if (!bh) {
+ EXT4_ERROR_INODE_BLOCK(inode, nr,
+ "Read failure");
+ continue;
+ }
+
+ /* This zaps the entire block. Bottom up. */
+ BUFFER_TRACE(bh, "free child branches");
+ ext4_free_branches(handle, inode, bh,
+ (__le32 *) bh->b_data,
+ (__le32 *) bh->b_data + addr_per_block,
+ depth);
+ brelse(bh);
+
+ /*
+ * Everything below this this pointer has been
+ * released. Now let this top-of-subtree go.
+ *
+ * We want the freeing of this indirect block to be
+ * atomic in the journal with the updating of the
+ * bitmap block which owns it. So make some room in
+ * the journal.
+ *
+ * We zero the parent pointer *after* freeing its
+ * pointee in the bitmaps, so if extend_transaction()
+ * for some reason fails to put the bitmap changes and
+ * the release into the same transaction, recovery
+ * will merely complain about releasing a free block,
+ * rather than leaking blocks.
+ */
+ if (ext4_handle_is_aborted(handle))
+ return;
+ if (try_to_extend_transaction(handle, inode)) {
+ ext4_mark_inode_dirty(handle, inode);
+ ext4_truncate_restart_trans(handle, inode,
+ ext4_blocks_for_truncate(inode));
+ }
+
+ /*
+ * The forget flag here is critical because if
+ * we are journaling (and not doing data
+ * journaling), we have to make sure a revoke
+ * record is written to prevent the journal
+ * replay from overwriting the (former)
+ * indirect block if it gets reallocated as a
+ * data block. This must happen in the same
+ * transaction where the data blocks are
+ * actually freed.
+ */
+ ext4_free_blocks(handle, inode, NULL, nr, 1,
+ EXT4_FREE_BLOCKS_METADATA|
+ EXT4_FREE_BLOCKS_FORGET);
+
+ if (parent_bh) {
+ /*
+ * The block which we have just freed is
+ * pointed to by an indirect block: journal it
+ */
+ BUFFER_TRACE(parent_bh, "get_write_access");
+ if (!ext4_journal_get_write_access(handle,
+ parent_bh)){
+ *p = 0;
+ BUFFER_TRACE(parent_bh,
+ "call ext4_handle_dirty_metadata");
+ ext4_handle_dirty_metadata(handle,
+ inode,
+ parent_bh);
+ }
+ }
+ }
+ } else {
+ /* We have reached the bottom of the tree. */
+ BUFFER_TRACE(parent_bh, "free data blocks");
+ ext4_free_data(handle, inode, parent_bh, first, last);
+ }
+}
+
+void ext4_ind_truncate(struct inode *inode)
+{
+ handle_t *handle;
+ struct ext4_inode_info *ei = EXT4_I(inode);
+ __le32 *i_data = ei->i_data;
+ int addr_per_block = EXT4_ADDR_PER_BLOCK(inode->i_sb);
+ struct address_space *mapping = inode->i_mapping;
+ ext4_lblk_t offsets[4];
+ Indirect chain[4];
+ Indirect *partial;
+ __le32 nr = 0;
+ int n = 0;
+ ext4_lblk_t last_block, max_block;
+ unsigned blocksize = inode->i_sb->s_blocksize;
+
+ handle = start_transaction(inode);
+ if (IS_ERR(handle))
+ return; /* AKPM: return what? */
+
+ last_block = (inode->i_size + blocksize-1)
+ >> EXT4_BLOCK_SIZE_BITS(inode->i_sb);
+ max_block = (EXT4_SB(inode->i_sb)->s_bitmap_maxbytes + blocksize-1)
+ >> EXT4_BLOCK_SIZE_BITS(inode->i_sb);
+
+ if (inode->i_size & (blocksize - 1))
+ if (ext4_block_truncate_page(handle, mapping, inode->i_size))
+ goto out_stop;
+
+ if (last_block != max_block) {
+ n = ext4_block_to_path(inode, last_block, offsets, NULL);
+ if (n == 0)
+ goto out_stop; /* error */
+ }
+
+ /*
+ * OK. This truncate is going to happen. We add the inode to the
+ * orphan list, so that if this truncate spans multiple transactions,
+ * and we crash, we will resume the truncate when the filesystem
+ * recovers. It also marks the inode dirty, to catch the new size.
+ *
+ * Implication: the file must always be in a sane, consistent
+ * truncatable state while each transaction commits.
+ */
+ if (ext4_orphan_add(handle, inode))
+ goto out_stop;
+
+ /*
+ * From here we block out all ext4_get_block() callers who want to
+ * modify the block allocation tree.
+ */
+ down_write(&ei->i_data_sem);
+
+ ext4_discard_preallocations(inode);
+
+ /*
+ * The orphan list entry will now protect us from any crash which
+ * occurs before the truncate completes, so it is now safe to propagate
+ * the new, shorter inode size (held for now in i_size) into the
+ * on-disk inode. We do this via i_disksize, which is the value which
+ * ext4 *really* writes onto the disk inode.
+ */
+ ei->i_disksize = inode->i_size;
+
+ if (last_block == max_block) {
+ /*
+ * It is unnecessary to free any data blocks if last_block is
+ * equal to the indirect block limit.
+ */
+ goto out_unlock;
+ } else if (n == 1) { /* direct blocks */
+ ext4_free_data(handle, inode, NULL, i_data+offsets[0],
+ i_data + EXT4_NDIR_BLOCKS);
+ goto do_indirects;
+ }
+
+ partial = ext4_find_shared(inode, n, offsets, chain, &nr);
+ /* Kill the top of shared branch (not detached) */
+ if (nr) {
+ if (partial == chain) {
+ /* Shared branch grows from the inode */
+ ext4_free_branches(handle, inode, NULL,
+ &nr, &nr+1, (chain+n-1) - partial);
+ *partial->p = 0;
+ /*
+ * We mark the inode dirty prior to restart,
+ * and prior to stop. No need for it here.
+ */
+ } else {
+ /* Shared branch grows from an indirect block */
+ BUFFER_TRACE(partial->bh, "get_write_access");
+ ext4_free_branches(handle, inode, partial->bh,
+ partial->p,
+ partial->p+1, (chain+n-1) - partial);
+ }
+ }
+ /* Clear the ends of indirect blocks on the shared branch */
+ while (partial > chain) {
+ ext4_free_branches(handle, inode, partial->bh, partial->p + 1,
+ (__le32*)partial->bh->b_data+addr_per_block,
+ (chain+n-1) - partial);
+ BUFFER_TRACE(partial->bh, "call brelse");
+ brelse(partial->bh);
+ partial--;
+ }
+do_indirects:
+ /* Kill the remaining (whole) subtrees */
+ switch (offsets[0]) {
+ default:
+ nr = i_data[EXT4_IND_BLOCK];
+ if (nr) {
+ ext4_free_branches(handle, inode, NULL, &nr, &nr+1, 1);
+ i_data[EXT4_IND_BLOCK] = 0;
+ }
+ case EXT4_IND_BLOCK:
+ nr = i_data[EXT4_DIND_BLOCK];
+ if (nr) {
+ ext4_free_branches(handle, inode, NULL, &nr, &nr+1, 2);
+ i_data[EXT4_DIND_BLOCK] = 0;
+ }
+ case EXT4_DIND_BLOCK:
+ nr = i_data[EXT4_TIND_BLOCK];
+ if (nr) {
+ ext4_free_branches(handle, inode, NULL, &nr, &nr+1, 3);
+ i_data[EXT4_TIND_BLOCK] = 0;
+ }
+ case EXT4_TIND_BLOCK:
+ ;
+ }
+
+out_unlock:
+ up_write(&ei->i_data_sem);
+ inode->i_mtime = inode->i_ctime = ext4_current_time(inode);
+ ext4_mark_inode_dirty(handle, inode);
+
+ /*
+ * In a multi-transaction truncate, we only make the final transaction
+ * synchronous
+ */
+ if (IS_SYNC(inode))
+ ext4_handle_sync(handle);
+out_stop:
+ /*
+ * If this was a simple ftruncate(), and the file will remain alive
+ * then we need to clear up the orphan record which we created above.
+ * However, if this was a real unlink then we were called by
+ * ext4_delete_inode(), and we allow that function to clean up the
+ * orphan info for us.
+ */
+ if (inode->i_nlink)
+ ext4_orphan_del(handle, inode);
+
+ ext4_journal_stop(handle);
+ trace_ext4_truncate_exit(inode);
+}
+
diff --git a/fs/ext4/inode.c b/fs/ext4/inode.c
index 3e5191f..d47264c 100644
--- a/fs/ext4/inode.c
+++ b/fs/ext4/inode.c
@@ -12,10 +12,6 @@
*
* Copyright (C) 1991, 1992 Linus Torvalds
*
- * Goal-directed block allocation by Stephen Tweedie
- * (sct@redhat.com), 1993, 1998
- * Big-endian to little-endian byte-swapping/bitmaps by
- * David S. Miller (davem@caip.rutgers.edu), 1995
* 64-bit file support on 64-bit platforms by Jakub Jelinek
* (jj@sunsite.ms.mff.cuni.cz)
*
@@ -47,6 +43,7 @@
#include "xattr.h"
#include "acl.h"
#include "ext4_extents.h"
+#include "truncate.h"
#include <trace/events/ext4.h>
@@ -89,72 +86,6 @@
}
/*
- * Work out how many blocks we need to proceed with the next chunk of a
- * truncate transaction.
- */
-static unsigned long blocks_for_truncate(struct inode *inode)
-{
- ext4_lblk_t needed;
-
- needed = inode->i_blocks >> (inode->i_sb->s_blocksize_bits - 9);
-
- /* Give ourselves just enough room to cope with inodes in which
- * i_blocks is corrupt: we've seen disk corruptions in the past
- * which resulted in random data in an inode which looked enough
- * like a regular file for ext4 to try to delete it. Things
- * will go a bit crazy if that happens, but at least we should
- * try not to panic the whole kernel. */
- if (needed < 2)
- needed = 2;
-
- /* But we need to bound the transaction so we don't overflow the
- * journal. */
- if (needed > EXT4_MAX_TRANS_DATA)
- needed = EXT4_MAX_TRANS_DATA;
-
- return EXT4_DATA_TRANS_BLOCKS(inode->i_sb) + needed;
-}
-
-/*
- * Truncate transactions can be complex and absolutely huge. So we need to
- * be able to restart the transaction at a conventient checkpoint to make
- * sure we don't overflow the journal.
- *
- * start_transaction gets us a new handle for a truncate transaction,
- * and extend_transaction tries to extend the existing one a bit. If
- * extend fails, we need to propagate the failure up and restart the
- * transaction in the top-level truncate loop. --sct
- */
-static handle_t *start_transaction(struct inode *inode)
-{
- handle_t *result;
-
- result = ext4_journal_start(inode, blocks_for_truncate(inode));
- if (!IS_ERR(result))
- return result;
-
- ext4_std_error(inode->i_sb, PTR_ERR(result));
- return result;
-}
-
-/*
- * Try to extend this transaction for the purposes of truncation.
- *
- * Returns 0 if we managed to create more room. If we can't create more
- * room, and the transaction must be restarted we return 1.
- */
-static int try_to_extend_transaction(handle_t *handle, struct inode *inode)
-{
- if (!ext4_handle_valid(handle))
- return 0;
- if (ext4_handle_has_enough_credits(handle, EXT4_RESERVE_TRANS_BLOCKS+1))
- return 0;
- if (!ext4_journal_extend(handle, blocks_for_truncate(inode)))
- return 0;
- return 1;
-}
-
-/*
* Restart the transaction associated with *handle. This does a commit,
* so before we call here everything must be consistently dirtied against
* this transaction.
@@ -190,6 +121,33 @@
trace_ext4_evict_inode(inode);
if (inode->i_nlink) {
+ /*
+ * When journalling data dirty buffers are tracked only in the
+ * journal. So although mm thinks everything is clean and
+ * ready for reaping the inode might still have some pages to
+ * write in the running transaction or waiting to be
+ * checkpointed. Thus calling jbd2_journal_invalidatepage()
+ * (via truncate_inode_pages()) to discard these buffers can
+ * cause data loss. Also even if we did not discard these
+ * buffers, we would have no way to find them after the inode
+ * is reaped and thus user could see stale data if he tries to
+ * read them before the transaction is checkpointed. So be
+ * careful and force everything to disk here... We use
+ * ei->i_datasync_tid to store the newest transaction
+ * containing inode's data.
+ *
+ * Note that directories do not have this problem because they
+ * don't use page cache.
+ */
+ if (ext4_should_journal_data(inode) &&
+ (S_ISLNK(inode->i_mode) || S_ISREG(inode->i_mode))) {
+ journal_t *journal = EXT4_SB(inode->i_sb)->s_journal;
+ tid_t commit_tid = EXT4_I(inode)->i_datasync_tid;
+
+ jbd2_log_start_commit(journal, commit_tid);
+ jbd2_log_wait_commit(journal, commit_tid);
+ filemap_write_and_wait(&inode->i_data);
+ }
truncate_inode_pages(&inode->i_data, 0);
goto no_delete;
}
@@ -204,7 +162,7 @@
if (is_bad_inode(inode))
goto no_delete;
- handle = ext4_journal_start(inode, blocks_for_truncate(inode)+3);
+ handle = ext4_journal_start(inode, ext4_blocks_for_truncate(inode)+3);
if (IS_ERR(handle)) {
ext4_std_error(inode->i_sb, PTR_ERR(handle));
/*
@@ -277,793 +235,6 @@
ext4_clear_inode(inode); /* We must guarantee clearing of inode... */
}
-typedef struct {
- __le32 *p;
- __le32 key;
- struct buffer_head *bh;
-} Indirect;
-
-static inline void add_chain(Indirect *p, struct buffer_head *bh, __le32 *v)
-{
- p->key = *(p->p = v);
- p->bh = bh;
-}
-
-/**
- * ext4_block_to_path - parse the block number into array of offsets
- * @inode: inode in question (we are only interested in its superblock)
- * @i_block: block number to be parsed
- * @offsets: array to store the offsets in
- * @boundary: set this non-zero if the referred-to block is likely to be
- * followed (on disk) by an indirect block.
- *
- * To store the locations of file's data ext4 uses a data structure common
- * for UNIX filesystems - tree of pointers anchored in the inode, with
- * data blocks at leaves and indirect blocks in intermediate nodes.
- * This function translates the block number into path in that tree -
- * return value is the path length and @offsets[n] is the offset of
- * pointer to (n+1)th node in the nth one. If @block is out of range
- * (negative or too large) warning is printed and zero returned.
- *
- * Note: function doesn't find node addresses, so no IO is needed. All
- * we need to know is the capacity of indirect blocks (taken from the
- * inode->i_sb).
- */
-
-/*
- * Portability note: the last comparison (check that we fit into triple
- * indirect block) is spelled differently, because otherwise on an
- * architecture with 32-bit longs and 8Kb pages we might get into trouble
- * if our filesystem had 8Kb blocks. We might use long long, but that would
- * kill us on x86. Oh, well, at least the sign propagation does not matter -
- * i_block would have to be negative in the very beginning, so we would not
- * get there at all.
- */
-
-static int ext4_block_to_path(struct inode *inode,
- ext4_lblk_t i_block,
- ext4_lblk_t offsets[4], int *boundary)
-{
- int ptrs = EXT4_ADDR_PER_BLOCK(inode->i_sb);
- int ptrs_bits = EXT4_ADDR_PER_BLOCK_BITS(inode->i_sb);
- const long direct_blocks = EXT4_NDIR_BLOCKS,
- indirect_blocks = ptrs,
- double_blocks = (1 << (ptrs_bits * 2));
- int n = 0;
- int final = 0;
-
- if (i_block < direct_blocks) {
- offsets[n++] = i_block;
- final = direct_blocks;
- } else if ((i_block -= direct_blocks) < indirect_blocks) {
- offsets[n++] = EXT4_IND_BLOCK;
- offsets[n++] = i_block;
- final = ptrs;
- } else if ((i_block -= indirect_blocks) < double_blocks) {
- offsets[n++] = EXT4_DIND_BLOCK;
- offsets[n++] = i_block >> ptrs_bits;
- offsets[n++] = i_block & (ptrs - 1);
- final = ptrs;
- } else if (((i_block -= double_blocks) >> (ptrs_bits * 2)) < ptrs) {
- offsets[n++] = EXT4_TIND_BLOCK;
- offsets[n++] = i_block >> (ptrs_bits * 2);
- offsets[n++] = (i_block >> ptrs_bits) & (ptrs - 1);
- offsets[n++] = i_block & (ptrs - 1);
- final = ptrs;
- } else {
- ext4_warning(inode->i_sb, "block %lu > max in inode %lu",
- i_block + direct_blocks +
- indirect_blocks + double_blocks, inode->i_ino);
- }
- if (boundary)
- *boundary = final - 1 - (i_block & (ptrs - 1));
- return n;
-}
-
-static int __ext4_check_blockref(const char *function, unsigned int line,
- struct inode *inode,
- __le32 *p, unsigned int max)
-{
- struct ext4_super_block *es = EXT4_SB(inode->i_sb)->s_es;
- __le32 *bref = p;
- unsigned int blk;
-
- while (bref < p+max) {
- blk = le32_to_cpu(*bref++);
- if (blk &&
- unlikely(!ext4_data_block_valid(EXT4_SB(inode->i_sb),
- blk, 1))) {
- es->s_last_error_block = cpu_to_le64(blk);
- ext4_error_inode(inode, function, line, blk,
- "invalid block");
- return -EIO;
- }
- }
- return 0;
-}
-
-
-#define ext4_check_indirect_blockref(inode, bh) \
- __ext4_check_blockref(__func__, __LINE__, inode, \
- (__le32 *)(bh)->b_data, \
- EXT4_ADDR_PER_BLOCK((inode)->i_sb))
-
-#define ext4_check_inode_blockref(inode) \
- __ext4_check_blockref(__func__, __LINE__, inode, \
- EXT4_I(inode)->i_data, \
- EXT4_NDIR_BLOCKS)
-
-/**
- * ext4_get_branch - read the chain of indirect blocks leading to data
- * @inode: inode in question
- * @depth: depth of the chain (1 - direct pointer, etc.)
- * @offsets: offsets of pointers in inode/indirect blocks
- * @chain: place to store the result
- * @err: here we store the error value
- *
- * Function fills the array of triples <key, p, bh> and returns %NULL
- * if everything went OK or the pointer to the last filled triple
- * (incomplete one) otherwise. Upon the return chain[i].key contains
- * the number of (i+1)-th block in the chain (as it is stored in memory,
- * i.e. little-endian 32-bit), chain[i].p contains the address of that
- * number (it points into struct inode for i==0 and into the bh->b_data
- * for i>0) and chain[i].bh points to the buffer_head of i-th indirect
- * block for i>0 and NULL for i==0. In other words, it holds the block
- * numbers of the chain, addresses they were taken from (and where we can
- * verify that chain did not change) and buffer_heads hosting these
- * numbers.
- *
- * Function stops when it stumbles upon zero pointer (absent block)
- * (pointer to last triple returned, *@err == 0)
- * or when it gets an IO error reading an indirect block
- * (ditto, *@err == -EIO)
- * or when it reads all @depth-1 indirect blocks successfully and finds
- * the whole chain, all way to the data (returns %NULL, *err == 0).
- *
- * Need to be called with
- * down_read(&EXT4_I(inode)->i_data_sem)
- */
-static Indirect *ext4_get_branch(struct inode *inode, int depth,
- ext4_lblk_t *offsets,
- Indirect chain[4], int *err)
-{
- struct super_block *sb = inode->i_sb;
- Indirect *p = chain;
- struct buffer_head *bh;
-
- *err = 0;
- /* i_data is not going away, no lock needed */
- add_chain(chain, NULL, EXT4_I(inode)->i_data + *offsets);
- if (!p->key)
- goto no_block;
- while (--depth) {
- bh = sb_getblk(sb, le32_to_cpu(p->key));
- if (unlikely(!bh))
- goto failure;
-
- if (!bh_uptodate_or_lock(bh)) {
- if (bh_submit_read(bh) < 0) {
- put_bh(bh);
- goto failure;
- }
- /* validate block references */
- if (ext4_check_indirect_blockref(inode, bh)) {
- put_bh(bh);
- goto failure;
- }
- }
-
- add_chain(++p, bh, (__le32 *)bh->b_data + *++offsets);
- /* Reader: end */
- if (!p->key)
- goto no_block;
- }
- return NULL;
-
-failure:
- *err = -EIO;
-no_block:
- return p;
-}
-
-/**
- * ext4_find_near - find a place for allocation with sufficient locality
- * @inode: owner
- * @ind: descriptor of indirect block.
- *
- * This function returns the preferred place for block allocation.
- * It is used when heuristic for sequential allocation fails.
- * Rules are:
- * + if there is a block to the left of our position - allocate near it.
- * + if pointer will live in indirect block - allocate near that block.
- * + if pointer will live in inode - allocate in the same
- * cylinder group.
- *
- * In the latter case we colour the starting block by the callers PID to
- * prevent it from clashing with concurrent allocations for a different inode
- * in the same block group. The PID is used here so that functionally related
- * files will be close-by on-disk.
- *
- * Caller must make sure that @ind is valid and will stay that way.
- */
-static ext4_fsblk_t ext4_find_near(struct inode *inode, Indirect *ind)
-{
- struct ext4_inode_info *ei = EXT4_I(inode);
- __le32 *start = ind->bh ? (__le32 *) ind->bh->b_data : ei->i_data;
- __le32 *p;
- ext4_fsblk_t bg_start;
- ext4_fsblk_t last_block;
- ext4_grpblk_t colour;
- ext4_group_t block_group;
- int flex_size = ext4_flex_bg_size(EXT4_SB(inode->i_sb));
-
- /* Try to find previous block */
- for (p = ind->p - 1; p >= start; p--) {
- if (*p)
- return le32_to_cpu(*p);
- }
-
- /* No such thing, so let's try location of indirect block */
- if (ind->bh)
- return ind->bh->b_blocknr;
-
- /*
- * It is going to be referred to from the inode itself? OK, just put it
- * into the same cylinder group then.
- */
- block_group = ei->i_block_group;
- if (flex_size >= EXT4_FLEX_SIZE_DIR_ALLOC_SCHEME) {
- block_group &= ~(flex_size-1);
- if (S_ISREG(inode->i_mode))
- block_group++;
- }
- bg_start = ext4_group_first_block_no(inode->i_sb, block_group);
- last_block = ext4_blocks_count(EXT4_SB(inode->i_sb)->s_es) - 1;
-
- /*
- * If we are doing delayed allocation, we don't need take
- * colour into account.
- */
- if (test_opt(inode->i_sb, DELALLOC))
- return bg_start;
-
- if (bg_start + EXT4_BLOCKS_PER_GROUP(inode->i_sb) <= last_block)
- colour = (current->pid % 16) *
- (EXT4_BLOCKS_PER_GROUP(inode->i_sb) / 16);
- else
- colour = (current->pid % 16) * ((last_block - bg_start) / 16);
- return bg_start + colour;
-}
-
-/**
- * ext4_find_goal - find a preferred place for allocation.
- * @inode: owner
- * @block: block we want
- * @partial: pointer to the last triple within a chain
- *
- * Normally this function find the preferred place for block allocation,
- * returns it.
- * Because this is only used for non-extent files, we limit the block nr
- * to 32 bits.
- */
-static ext4_fsblk_t ext4_find_goal(struct inode *inode, ext4_lblk_t block,
- Indirect *partial)
-{
- ext4_fsblk_t goal;
-
- /*
- * XXX need to get goal block from mballoc's data structures
- */
-
- goal = ext4_find_near(inode, partial);
- goal = goal & EXT4_MAX_BLOCK_FILE_PHYS;
- return goal;
-}
-
-/**
- * ext4_blks_to_allocate - Look up the block map and count the number
- * of direct blocks need to be allocated for the given branch.
- *
- * @branch: chain of indirect blocks
- * @k: number of blocks need for indirect blocks
- * @blks: number of data blocks to be mapped.
- * @blocks_to_boundary: the offset in the indirect block
- *
- * return the total number of blocks to be allocate, including the
- * direct and indirect blocks.
- */
-static int ext4_blks_to_allocate(Indirect *branch, int k, unsigned int blks,
- int blocks_to_boundary)
-{
- unsigned int count = 0;
-
- /*
- * Simple case, [t,d]Indirect block(s) has not allocated yet
- * then it's clear blocks on that path have not allocated
- */
- if (k > 0) {
- /* right now we don't handle cross boundary allocation */
- if (blks < blocks_to_boundary + 1)
- count += blks;
- else
- count += blocks_to_boundary + 1;
- return count;
- }
-
- count++;
- while (count < blks && count <= blocks_to_boundary &&
- le32_to_cpu(*(branch[0].p + count)) == 0) {
- count++;
- }
- return count;
-}
-
-/**
- * ext4_alloc_blocks: multiple allocate blocks needed for a branch
- * @handle: handle for this transaction
- * @inode: inode which needs allocated blocks
- * @iblock: the logical block to start allocated at
- * @goal: preferred physical block of allocation
- * @indirect_blks: the number of blocks need to allocate for indirect
- * blocks
- * @blks: number of desired blocks
- * @new_blocks: on return it will store the new block numbers for
- * the indirect blocks(if needed) and the first direct block,
- * @err: on return it will store the error code
- *
- * This function will return the number of blocks allocated as
- * requested by the passed-in parameters.
- */
-static int ext4_alloc_blocks(handle_t *handle, struct inode *inode,
- ext4_lblk_t iblock, ext4_fsblk_t goal,
- int indirect_blks, int blks,
- ext4_fsblk_t new_blocks[4], int *err)
-{
- struct ext4_allocation_request ar;
- int target, i;
- unsigned long count = 0, blk_allocated = 0;
- int index = 0;
- ext4_fsblk_t current_block = 0;
- int ret = 0;
-
- /*
- * Here we try to allocate the requested multiple blocks at once,
- * on a best-effort basis.
- * To build a branch, we should allocate blocks for
- * the indirect blocks(if not allocated yet), and at least
- * the first direct block of this branch. That's the
- * minimum number of blocks need to allocate(required)
- */
- /* first we try to allocate the indirect blocks */
- target = indirect_blks;
- while (target > 0) {
- count = target;
- /* allocating blocks for indirect blocks and direct blocks */
- current_block = ext4_new_meta_blocks(handle, inode, goal,
- 0, &count, err);
- if (*err)
- goto failed_out;
-
- if (unlikely(current_block + count > EXT4_MAX_BLOCK_FILE_PHYS)) {
- EXT4_ERROR_INODE(inode,
- "current_block %llu + count %lu > %d!",
- current_block, count,
- EXT4_MAX_BLOCK_FILE_PHYS);
- *err = -EIO;
- goto failed_out;
- }
-
- target -= count;
- /* allocate blocks for indirect blocks */
- while (index < indirect_blks && count) {
- new_blocks[index++] = current_block++;
- count--;
- }
- if (count > 0) {
- /*
- * save the new block number
- * for the first direct block
- */
- new_blocks[index] = current_block;
- printk(KERN_INFO "%s returned more blocks than "
- "requested\n", __func__);
- WARN_ON(1);
- break;
- }
- }
-
- target = blks - count ;
- blk_allocated = count;
- if (!target)
- goto allocated;
- /* Now allocate data blocks */
- memset(&ar, 0, sizeof(ar));
- ar.inode = inode;
- ar.goal = goal;
- ar.len = target;
- ar.logical = iblock;
- if (S_ISREG(inode->i_mode))
- /* enable in-core preallocation only for regular files */
- ar.flags = EXT4_MB_HINT_DATA;
-
- current_block = ext4_mb_new_blocks(handle, &ar, err);
- if (unlikely(current_block + ar.len > EXT4_MAX_BLOCK_FILE_PHYS)) {
- EXT4_ERROR_INODE(inode,
- "current_block %llu + ar.len %d > %d!",
- current_block, ar.len,
- EXT4_MAX_BLOCK_FILE_PHYS);
- *err = -EIO;
- goto failed_out;
- }
-
- if (*err && (target == blks)) {
- /*
- * if the allocation failed and we didn't allocate
- * any blocks before
- */
- goto failed_out;
- }
- if (!*err) {
- if (target == blks) {
- /*
- * save the new block number
- * for the first direct block
- */
- new_blocks[index] = current_block;
- }
- blk_allocated += ar.len;
- }
-allocated:
- /* total number of blocks allocated for direct blocks */
- ret = blk_allocated;
- *err = 0;
- return ret;
-failed_out:
- for (i = 0; i < index; i++)
- ext4_free_blocks(handle, inode, NULL, new_blocks[i], 1, 0);
- return ret;
-}
-
-/**
- * ext4_alloc_branch - allocate and set up a chain of blocks.
- * @handle: handle for this transaction
- * @inode: owner
- * @indirect_blks: number of allocated indirect blocks
- * @blks: number of allocated direct blocks
- * @goal: preferred place for allocation
- * @offsets: offsets (in the blocks) to store the pointers to next.
- * @branch: place to store the chain in.
- *
- * This function allocates blocks, zeroes out all but the last one,
- * links them into chain and (if we are synchronous) writes them to disk.
- * In other words, it prepares a branch that can be spliced onto the
- * inode. It stores the information about that chain in the branch[], in
- * the same format as ext4_get_branch() would do. We are calling it after
- * we had read the existing part of chain and partial points to the last
- * triple of that (one with zero ->key). Upon the exit we have the same
- * picture as after the successful ext4_get_block(), except that in one
- * place chain is disconnected - *branch->p is still zero (we did not
- * set the last link), but branch->key contains the number that should
- * be placed into *branch->p to fill that gap.
- *
- * If allocation fails we free all blocks we've allocated (and forget
- * their buffer_heads) and return the error value the from failed
- * ext4_alloc_block() (normally -ENOSPC). Otherwise we set the chain
- * as described above and return 0.
- */
-static int ext4_alloc_branch(handle_t *handle, struct inode *inode,
- ext4_lblk_t iblock, int indirect_blks,
- int *blks, ext4_fsblk_t goal,
- ext4_lblk_t *offsets, Indirect *branch)
-{
- int blocksize = inode->i_sb->s_blocksize;
- int i, n = 0;
- int err = 0;
- struct buffer_head *bh;
- int num;
- ext4_fsblk_t new_blocks[4];
- ext4_fsblk_t current_block;
-
- num = ext4_alloc_blocks(handle, inode, iblock, goal, indirect_blks,
- *blks, new_blocks, &err);
- if (err)
- return err;
-
- branch[0].key = cpu_to_le32(new_blocks[0]);
- /*
- * metadata blocks and data blocks are allocated.
- */
- for (n = 1; n <= indirect_blks; n++) {
- /*
- * Get buffer_head for parent block, zero it out
- * and set the pointer to new one, then send
- * parent to disk.
- */
- bh = sb_getblk(inode->i_sb, new_blocks[n-1]);
- if (unlikely(!bh)) {
- err = -EIO;
- goto failed;
- }
-
- branch[n].bh = bh;
- lock_buffer(bh);
- BUFFER_TRACE(bh, "call get_create_access");
- err = ext4_journal_get_create_access(handle, bh);
- if (err) {
- /* Don't brelse(bh) here; it's done in
- * ext4_journal_forget() below */
- unlock_buffer(bh);
- goto failed;
- }
-
- memset(bh->b_data, 0, blocksize);
- branch[n].p = (__le32 *) bh->b_data + offsets[n];
- branch[n].key = cpu_to_le32(new_blocks[n]);
- *branch[n].p = branch[n].key;
- if (n == indirect_blks) {
- current_block = new_blocks[n];
- /*
- * End of chain, update the last new metablock of
- * the chain to point to the new allocated
- * data blocks numbers
- */
- for (i = 1; i < num; i++)
- *(branch[n].p + i) = cpu_to_le32(++current_block);
- }
- BUFFER_TRACE(bh, "marking uptodate");
- set_buffer_uptodate(bh);
- unlock_buffer(bh);
-
- BUFFER_TRACE(bh, "call ext4_handle_dirty_metadata");
- err = ext4_handle_dirty_metadata(handle, inode, bh);
- if (err)
- goto failed;
- }
- *blks = num;
- return err;
-failed:
- /* Allocation failed, free what we already allocated */
- ext4_free_blocks(handle, inode, NULL, new_blocks[0], 1, 0);
- for (i = 1; i <= n ; i++) {
- /*
- * branch[i].bh is newly allocated, so there is no
- * need to revoke the block, which is why we don't
- * need to set EXT4_FREE_BLOCKS_METADATA.
- */
- ext4_free_blocks(handle, inode, NULL, new_blocks[i], 1,
- EXT4_FREE_BLOCKS_FORGET);
- }
- for (i = n+1; i < indirect_blks; i++)
- ext4_free_blocks(handle, inode, NULL, new_blocks[i], 1, 0);
-
- ext4_free_blocks(handle, inode, NULL, new_blocks[i], num, 0);
-
- return err;
-}
-
-/**
- * ext4_splice_branch - splice the allocated branch onto inode.
- * @handle: handle for this transaction
- * @inode: owner
- * @block: (logical) number of block we are adding
- * @chain: chain of indirect blocks (with a missing link - see
- * ext4_alloc_branch)
- * @where: location of missing link
- * @num: number of indirect blocks we are adding
- * @blks: number of direct blocks we are adding
- *
- * This function fills the missing link and does all housekeeping needed in
- * inode (->i_blocks, etc.). In case of success we end up with the full
- * chain to new block and return 0.
- */
-static int ext4_splice_branch(handle_t *handle, struct inode *inode,
- ext4_lblk_t block, Indirect *where, int num,
- int blks)
-{
- int i;
- int err = 0;
- ext4_fsblk_t current_block;
-
- /*
- * If we're splicing into a [td]indirect block (as opposed to the
- * inode) then we need to get write access to the [td]indirect block
- * before the splice.
- */
- if (where->bh) {
- BUFFER_TRACE(where->bh, "get_write_access");
- err = ext4_journal_get_write_access(handle, where->bh);
- if (err)
- goto err_out;
- }
- /* That's it */
-
- *where->p = where->key;
-
- /*
- * Update the host buffer_head or inode to point to more just allocated
- * direct blocks blocks
- */
- if (num == 0 && blks > 1) {
- current_block = le32_to_cpu(where->key) + 1;
- for (i = 1; i < blks; i++)
- *(where->p + i) = cpu_to_le32(current_block++);
- }
-
- /* We are done with atomic stuff, now do the rest of housekeeping */
- /* had we spliced it onto indirect block? */
- if (where->bh) {
- /*
- * If we spliced it onto an indirect block, we haven't
- * altered the inode. Note however that if it is being spliced
- * onto an indirect block at the very end of the file (the
- * file is growing) then we *will* alter the inode to reflect
- * the new i_size. But that is not done here - it is done in
- * generic_commit_write->__mark_inode_dirty->ext4_dirty_inode.
- */
- jbd_debug(5, "splicing indirect only\n");
- BUFFER_TRACE(where->bh, "call ext4_handle_dirty_metadata");
- err = ext4_handle_dirty_metadata(handle, inode, where->bh);
- if (err)
- goto err_out;
- } else {
- /*
- * OK, we spliced it into the inode itself on a direct block.
- */
- ext4_mark_inode_dirty(handle, inode);
- jbd_debug(5, "splicing direct\n");
- }
- return err;
-
-err_out:
- for (i = 1; i <= num; i++) {
- /*
- * branch[i].bh is newly allocated, so there is no
- * need to revoke the block, which is why we don't
- * need to set EXT4_FREE_BLOCKS_METADATA.
- */
- ext4_free_blocks(handle, inode, where[i].bh, 0, 1,
- EXT4_FREE_BLOCKS_FORGET);
- }
- ext4_free_blocks(handle, inode, NULL, le32_to_cpu(where[num].key),
- blks, 0);
-
- return err;
-}
-
-/*
- * The ext4_ind_map_blocks() function handles non-extents inodes
- * (i.e., using the traditional indirect/double-indirect i_blocks
- * scheme) for ext4_map_blocks().
- *
- * Allocation strategy is simple: if we have to allocate something, we will
- * have to go the whole way to leaf. So let's do it before attaching anything
- * to tree, set linkage between the newborn blocks, write them if sync is
- * required, recheck the path, free and repeat if check fails, otherwise
- * set the last missing link (that will protect us from any truncate-generated
- * removals - all blocks on the path are immune now) and possibly force the
- * write on the parent block.
- * That has a nice additional property: no special recovery from the failed
- * allocations is needed - we simply release blocks and do not touch anything
- * reachable from inode.
- *
- * `handle' can be NULL if create == 0.
- *
- * return > 0, # of blocks mapped or allocated.
- * return = 0, if plain lookup failed.
- * return < 0, error case.
- *
- * The ext4_ind_get_blocks() function should be called with
- * down_write(&EXT4_I(inode)->i_data_sem) if allocating filesystem
- * blocks (i.e., flags has EXT4_GET_BLOCKS_CREATE set) or
- * down_read(&EXT4_I(inode)->i_data_sem) if not allocating file system
- * blocks.
- */
-static int ext4_ind_map_blocks(handle_t *handle, struct inode *inode,
- struct ext4_map_blocks *map,
- int flags)
-{
- int err = -EIO;
- ext4_lblk_t offsets[4];
- Indirect chain[4];
- Indirect *partial;
- ext4_fsblk_t goal;
- int indirect_blks;
- int blocks_to_boundary = 0;
- int depth;
- int count = 0;
- ext4_fsblk_t first_block = 0;
-
- trace_ext4_ind_map_blocks_enter(inode, map->m_lblk, map->m_len, flags);
- J_ASSERT(!(ext4_test_inode_flag(inode, EXT4_INODE_EXTENTS)));
- J_ASSERT(handle != NULL || (flags & EXT4_GET_BLOCKS_CREATE) == 0);
- depth = ext4_block_to_path(inode, map->m_lblk, offsets,
- &blocks_to_boundary);
-
- if (depth == 0)
- goto out;
-
- partial = ext4_get_branch(inode, depth, offsets, chain, &err);
-
- /* Simplest case - block found, no allocation needed */
- if (!partial) {
- first_block = le32_to_cpu(chain[depth - 1].key);
- count++;
- /*map more blocks*/
- while (count < map->m_len && count <= blocks_to_boundary) {
- ext4_fsblk_t blk;
-
- blk = le32_to_cpu(*(chain[depth-1].p + count));
-
- if (blk == first_block + count)
- count++;
- else
- break;
- }
- goto got_it;
- }
-
- /* Next simple case - plain lookup or failed read of indirect block */
- if ((flags & EXT4_GET_BLOCKS_CREATE) == 0 || err == -EIO)
- goto cleanup;
-
- /*
- * Okay, we need to do block allocation.
- */
- goal = ext4_find_goal(inode, map->m_lblk, partial);
-
- /* the number of blocks need to allocate for [d,t]indirect blocks */
- indirect_blks = (chain + depth) - partial - 1;
-
- /*
- * Next look up the indirect map to count the totoal number of
- * direct blocks to allocate for this branch.
- */
- count = ext4_blks_to_allocate(partial, indirect_blks,
- map->m_len, blocks_to_boundary);
- /*
- * Block out ext4_truncate while we alter the tree
- */
- err = ext4_alloc_branch(handle, inode, map->m_lblk, indirect_blks,
- &count, goal,
- offsets + (partial - chain), partial);
-
- /*
- * The ext4_splice_branch call will free and forget any buffers
- * on the new chain if there is a failure, but that risks using
- * up transaction credits, especially for bitmaps where the
- * credits cannot be returned. Can we handle this somehow? We
- * may need to return -EAGAIN upwards in the worst case. --sct
- */
- if (!err)
- err = ext4_splice_branch(handle, inode, map->m_lblk,
- partial, indirect_blks, count);
- if (err)
- goto cleanup;
-
- map->m_flags |= EXT4_MAP_NEW;
-
- ext4_update_inode_fsync_trans(handle, inode, 1);
-got_it:
- map->m_flags |= EXT4_MAP_MAPPED;
- map->m_pblk = le32_to_cpu(chain[depth-1].key);
- map->m_len = count;
- if (count > blocks_to_boundary)
- map->m_flags |= EXT4_MAP_BOUNDARY;
- err = count;
- /* Clean up and exit */
- partial = chain + depth - 1; /* the whole chain */
-cleanup:
- while (partial > chain) {
- BUFFER_TRACE(partial->bh, "call brelse");
- brelse(partial->bh);
- partial--;
- }
-out:
- trace_ext4_ind_map_blocks_exit(inode, map->m_lblk,
- map->m_pblk, map->m_len, err);
- return err;
-}
-
#ifdef CONFIG_QUOTA
qsize_t *ext4_get_reserved_space(struct inode *inode)
{
@@ -1073,33 +244,6 @@
/*
* Calculate the number of metadata blocks need to reserve
- * to allocate a new block at @lblocks for non extent file based file
- */
-static int ext4_indirect_calc_metadata_amount(struct inode *inode,
- sector_t lblock)
-{
- struct ext4_inode_info *ei = EXT4_I(inode);
- sector_t dind_mask = ~((sector_t)EXT4_ADDR_PER_BLOCK(inode->i_sb) - 1);
- int blk_bits;
-
- if (lblock < EXT4_NDIR_BLOCKS)
- return 0;
-
- lblock -= EXT4_NDIR_BLOCKS;
-
- if (ei->i_da_metadata_calc_len &&
- (lblock & dind_mask) == ei->i_da_metadata_calc_last_lblock) {
- ei->i_da_metadata_calc_len++;
- return 0;
- }
- ei->i_da_metadata_calc_last_lblock = lblock & dind_mask;
- ei->i_da_metadata_calc_len = 1;
- blk_bits = order_base_2(lblock);
- return (blk_bits / EXT4_ADDR_PER_BLOCK_BITS(inode->i_sb)) + 1;
-}
-
-/*
- * Calculate the number of metadata blocks need to reserve
* to allocate a block located at @lblock
*/
static int ext4_calc_metadata_amount(struct inode *inode, ext4_lblk_t lblock)
@@ -1107,7 +251,7 @@
if (ext4_test_inode_flag(inode, EXT4_INODE_EXTENTS))
return ext4_ext_calc_metadata_amount(inode, lblock);
- return ext4_indirect_calc_metadata_amount(inode, lblock);
+ return ext4_ind_calc_metadata_amount(inode, lblock);
}
/*
@@ -1589,16 +733,6 @@
return ret;
}
-/*
- * Truncate blocks that were not used by write. We have to truncate the
- * pagecache as well so that corresponding buffers get properly unmapped.
- */
-static void ext4_truncate_failed_write(struct inode *inode)
-{
- truncate_inode_pages(inode->i_mapping, inode->i_size);
- ext4_truncate(inode);
-}
-
static int ext4_get_block_write(struct inode *inode, sector_t iblock,
struct buffer_head *bh_result, int create);
static int ext4_write_begin(struct file *file, struct address_space *mapping,
@@ -1863,6 +997,7 @@
if (new_i_size > inode->i_size)
i_size_write(inode, pos+copied);
ext4_set_inode_state(inode, EXT4_STATE_JDATA);
+ EXT4_I(inode)->i_datasync_tid = handle->h_transaction->t_tid;
if (new_i_size > EXT4_I(inode)->i_disksize) {
ext4_update_i_disksize(inode, new_i_size);
ret2 = ext4_mark_inode_dirty(handle, inode);
@@ -2571,6 +1706,7 @@
write_end_fn);
if (ret == 0)
ret = err;
+ EXT4_I(inode)->i_datasync_tid = handle->h_transaction->t_tid;
err = ext4_journal_stop(handle);
if (!ret)
ret = err;
@@ -3450,112 +2586,6 @@
}
/*
- * O_DIRECT for ext3 (or indirect map) based files
- *
- * If the O_DIRECT write will extend the file then add this inode to the
- * orphan list. So recovery will truncate it back to the original size
- * if the machine crashes during the write.
- *
- * If the O_DIRECT write is intantiating holes inside i_size and the machine
- * crashes then stale disk data _may_ be exposed inside the file. But current
- * VFS code falls back into buffered path in that case so we are safe.
- */
-static ssize_t ext4_ind_direct_IO(int rw, struct kiocb *iocb,
- const struct iovec *iov, loff_t offset,
- unsigned long nr_segs)
-{
- struct file *file = iocb->ki_filp;
- struct inode *inode = file->f_mapping->host;
- struct ext4_inode_info *ei = EXT4_I(inode);
- handle_t *handle;
- ssize_t ret;
- int orphan = 0;
- size_t count = iov_length(iov, nr_segs);
- int retries = 0;
-
- if (rw == WRITE) {
- loff_t final_size = offset + count;
-
- if (final_size > inode->i_size) {
- /* Credits for sb + inode write */
- handle = ext4_journal_start(inode, 2);
- if (IS_ERR(handle)) {
- ret = PTR_ERR(handle);
- goto out;
- }
- ret = ext4_orphan_add(handle, inode);
- if (ret) {
- ext4_journal_stop(handle);
- goto out;
- }
- orphan = 1;
- ei->i_disksize = inode->i_size;
- ext4_journal_stop(handle);
- }
- }
-
-retry:
- if (rw == READ && ext4_should_dioread_nolock(inode))
- ret = __blockdev_direct_IO(rw, iocb, inode,
- inode->i_sb->s_bdev, iov,
- offset, nr_segs,
- ext4_get_block, NULL, NULL, 0);
- else {
- ret = blockdev_direct_IO(rw, iocb, inode, iov,
- offset, nr_segs, ext4_get_block);
-
- if (unlikely((rw & WRITE) && ret < 0)) {
- loff_t isize = i_size_read(inode);
- loff_t end = offset + iov_length(iov, nr_segs);
-
- if (end > isize)
- ext4_truncate_failed_write(inode);
- }
- }
- if (ret == -ENOSPC && ext4_should_retry_alloc(inode->i_sb, &retries))
- goto retry;
-
- if (orphan) {
- int err;
-
- /* Credits for sb + inode write */
- handle = ext4_journal_start(inode, 2);
- if (IS_ERR(handle)) {
- /* This is really bad luck. We've written the data
- * but cannot extend i_size. Bail out and pretend
- * the write failed... */
- ret = PTR_ERR(handle);
- if (inode->i_nlink)
- ext4_orphan_del(NULL, inode);
-
- goto out;
- }
- if (inode->i_nlink)
- ext4_orphan_del(handle, inode);
- if (ret > 0) {
- loff_t end = offset + ret;
- if (end > inode->i_size) {
- ei->i_disksize = end;
- i_size_write(inode, end);
- /*
- * We're going to return a positive `ret'
- * here due to non-zero-length I/O, so there's
- * no way of reporting error returns from
- * ext4_mark_inode_dirty() to userspace. So
- * ignore it.
- */
- ext4_mark_inode_dirty(handle, inode);
- }
- }
- err = ext4_journal_stop(handle);
- if (ret == 0)
- ret = err;
- }
-out:
- return ret;
-}
-
-/*
* ext4_get_block used when preparing for a DIO write or buffer write.
* We allocate an uinitialized extent if blocks haven't been allocated.
* The extent will be converted to initialized after the IO is complete.
@@ -4033,383 +3063,6 @@
return err;
}
-/*
- * Probably it should be a library function... search for first non-zero word
- * or memcmp with zero_page, whatever is better for particular architecture.
- * Linus?
- */
-static inline int all_zeroes(__le32 *p, __le32 *q)
-{
- while (p < q)
- if (*p++)
- return 0;
- return 1;
-}
-
-/**
- * ext4_find_shared - find the indirect blocks for partial truncation.
- * @inode: inode in question
- * @depth: depth of the affected branch
- * @offsets: offsets of pointers in that branch (see ext4_block_to_path)
- * @chain: place to store the pointers to partial indirect blocks
- * @top: place to the (detached) top of branch
- *
- * This is a helper function used by ext4_truncate().
- *
- * When we do truncate() we may have to clean the ends of several
- * indirect blocks but leave the blocks themselves alive. Block is
- * partially truncated if some data below the new i_size is referred
- * from it (and it is on the path to the first completely truncated
- * data block, indeed). We have to free the top of that path along
- * with everything to the right of the path. Since no allocation
- * past the truncation point is possible until ext4_truncate()
- * finishes, we may safely do the latter, but top of branch may
- * require special attention - pageout below the truncation point
- * might try to populate it.
- *
- * We atomically detach the top of branch from the tree, store the
- * block number of its root in *@top, pointers to buffer_heads of
- * partially truncated blocks - in @chain[].bh and pointers to
- * their last elements that should not be removed - in
- * @chain[].p. Return value is the pointer to last filled element
- * of @chain.
- *
- * The work left to caller to do the actual freeing of subtrees:
- * a) free the subtree starting from *@top
- * b) free the subtrees whose roots are stored in
- * (@chain[i].p+1 .. end of @chain[i].bh->b_data)
- * c) free the subtrees growing from the inode past the @chain[0].
- * (no partially truncated stuff there). */
-
-static Indirect *ext4_find_shared(struct inode *inode, int depth,
- ext4_lblk_t offsets[4], Indirect chain[4],
- __le32 *top)
-{
- Indirect *partial, *p;
- int k, err;
-
- *top = 0;
- /* Make k index the deepest non-null offset + 1 */
- for (k = depth; k > 1 && !offsets[k-1]; k--)
- ;
- partial = ext4_get_branch(inode, k, offsets, chain, &err);
- /* Writer: pointers */
- if (!partial)
- partial = chain + k-1;
- /*
- * If the branch acquired continuation since we've looked at it -
- * fine, it should all survive and (new) top doesn't belong to us.
- */
- if (!partial->key && *partial->p)
- /* Writer: end */
- goto no_top;
- for (p = partial; (p > chain) && all_zeroes((__le32 *) p->bh->b_data, p->p); p--)
- ;
- /*
- * OK, we've found the last block that must survive. The rest of our
- * branch should be detached before unlocking. However, if that rest
- * of branch is all ours and does not grow immediately from the inode
- * it's easier to cheat and just decrement partial->p.
- */
- if (p == chain + k - 1 && p > chain) {
- p->p--;
- } else {
- *top = *p->p;
- /* Nope, don't do this in ext4. Must leave the tree intact */
-#if 0
- *p->p = 0;
-#endif
- }
- /* Writer: end */
-
- while (partial > p) {
- brelse(partial->bh);
- partial--;
- }
-no_top:
- return partial;
-}
-
-/*
- * Zero a number of block pointers in either an inode or an indirect block.
- * If we restart the transaction we must again get write access to the
- * indirect block for further modification.
- *
- * We release `count' blocks on disk, but (last - first) may be greater
- * than `count' because there can be holes in there.
- *
- * Return 0 on success, 1 on invalid block range
- * and < 0 on fatal error.
- */
-static int ext4_clear_blocks(handle_t *handle, struct inode *inode,
- struct buffer_head *bh,
- ext4_fsblk_t block_to_free,
- unsigned long count, __le32 *first,
- __le32 *last)
-{
- __le32 *p;
- int flags = EXT4_FREE_BLOCKS_FORGET | EXT4_FREE_BLOCKS_VALIDATED;
- int err;
-
- if (S_ISDIR(inode->i_mode) || S_ISLNK(inode->i_mode))
- flags |= EXT4_FREE_BLOCKS_METADATA;
-
- if (!ext4_data_block_valid(EXT4_SB(inode->i_sb), block_to_free,
- count)) {
- EXT4_ERROR_INODE(inode, "attempt to clear invalid "
- "blocks %llu len %lu",
- (unsigned long long) block_to_free, count);
- return 1;
- }
-
- if (try_to_extend_transaction(handle, inode)) {
- if (bh) {
- BUFFER_TRACE(bh, "call ext4_handle_dirty_metadata");
- err = ext4_handle_dirty_metadata(handle, inode, bh);
- if (unlikely(err))
- goto out_err;
- }
- err = ext4_mark_inode_dirty(handle, inode);
- if (unlikely(err))
- goto out_err;
- err = ext4_truncate_restart_trans(handle, inode,
- blocks_for_truncate(inode));
- if (unlikely(err))
- goto out_err;
- if (bh) {
- BUFFER_TRACE(bh, "retaking write access");
- err = ext4_journal_get_write_access(handle, bh);
- if (unlikely(err))
- goto out_err;
- }
- }
-
- for (p = first; p < last; p++)
- *p = 0;
-
- ext4_free_blocks(handle, inode, NULL, block_to_free, count, flags);
- return 0;
-out_err:
- ext4_std_error(inode->i_sb, err);
- return err;
-}
-
-/**
- * ext4_free_data - free a list of data blocks
- * @handle: handle for this transaction
- * @inode: inode we are dealing with
- * @this_bh: indirect buffer_head which contains *@first and *@last
- * @first: array of block numbers
- * @last: points immediately past the end of array
- *
- * We are freeing all blocks referred from that array (numbers are stored as
- * little-endian 32-bit) and updating @inode->i_blocks appropriately.
- *
- * We accumulate contiguous runs of blocks to free. Conveniently, if these
- * blocks are contiguous then releasing them at one time will only affect one
- * or two bitmap blocks (+ group descriptor(s) and superblock) and we won't
- * actually use a lot of journal space.
- *
- * @this_bh will be %NULL if @first and @last point into the inode's direct
- * block pointers.
- */
-static void ext4_free_data(handle_t *handle, struct inode *inode,
- struct buffer_head *this_bh,
- __le32 *first, __le32 *last)
-{
- ext4_fsblk_t block_to_free = 0; /* Starting block # of a run */
- unsigned long count = 0; /* Number of blocks in the run */
- __le32 *block_to_free_p = NULL; /* Pointer into inode/ind
- corresponding to
- block_to_free */
- ext4_fsblk_t nr; /* Current block # */
- __le32 *p; /* Pointer into inode/ind
- for current block */
- int err = 0;
-
- if (this_bh) { /* For indirect block */
- BUFFER_TRACE(this_bh, "get_write_access");
- err = ext4_journal_get_write_access(handle, this_bh);
- /* Important: if we can't update the indirect pointers
- * to the blocks, we can't free them. */
- if (err)
- return;
- }
-
- for (p = first; p < last; p++) {
- nr = le32_to_cpu(*p);
- if (nr) {
- /* accumulate blocks to free if they're contiguous */
- if (count == 0) {
- block_to_free = nr;
- block_to_free_p = p;
- count = 1;
- } else if (nr == block_to_free + count) {
- count++;
- } else {
- err = ext4_clear_blocks(handle, inode, this_bh,
- block_to_free, count,
- block_to_free_p, p);
- if (err)
- break;
- block_to_free = nr;
- block_to_free_p = p;
- count = 1;
- }
- }
- }
-
- if (!err && count > 0)
- err = ext4_clear_blocks(handle, inode, this_bh, block_to_free,
- count, block_to_free_p, p);
- if (err < 0)
- /* fatal error */
- return;
-
- if (this_bh) {
- BUFFER_TRACE(this_bh, "call ext4_handle_dirty_metadata");
-
- /*
- * The buffer head should have an attached journal head at this
- * point. However, if the data is corrupted and an indirect
- * block pointed to itself, it would have been detached when
- * the block was cleared. Check for this instead of OOPSing.
- */
- if ((EXT4_JOURNAL(inode) == NULL) || bh2jh(this_bh))
- ext4_handle_dirty_metadata(handle, inode, this_bh);
- else
- EXT4_ERROR_INODE(inode,
- "circular indirect block detected at "
- "block %llu",
- (unsigned long long) this_bh->b_blocknr);
- }
-}
-
-/**
- * ext4_free_branches - free an array of branches
- * @handle: JBD handle for this transaction
- * @inode: inode we are dealing with
- * @parent_bh: the buffer_head which contains *@first and *@last
- * @first: array of block numbers
- * @last: pointer immediately past the end of array
- * @depth: depth of the branches to free
- *
- * We are freeing all blocks referred from these branches (numbers are
- * stored as little-endian 32-bit) and updating @inode->i_blocks
- * appropriately.
- */
-static void ext4_free_branches(handle_t *handle, struct inode *inode,
- struct buffer_head *parent_bh,
- __le32 *first, __le32 *last, int depth)
-{
- ext4_fsblk_t nr;
- __le32 *p;
-
- if (ext4_handle_is_aborted(handle))
- return;
-
- if (depth--) {
- struct buffer_head *bh;
- int addr_per_block = EXT4_ADDR_PER_BLOCK(inode->i_sb);
- p = last;
- while (--p >= first) {
- nr = le32_to_cpu(*p);
- if (!nr)
- continue; /* A hole */
-
- if (!ext4_data_block_valid(EXT4_SB(inode->i_sb),
- nr, 1)) {
- EXT4_ERROR_INODE(inode,
- "invalid indirect mapped "
- "block %lu (level %d)",
- (unsigned long) nr, depth);
- break;
- }
-
- /* Go read the buffer for the next level down */
- bh = sb_bread(inode->i_sb, nr);
-
- /*
- * A read failure? Report error and clear slot
- * (should be rare).
- */
- if (!bh) {
- EXT4_ERROR_INODE_BLOCK(inode, nr,
- "Read failure");
- continue;
- }
-
- /* This zaps the entire block. Bottom up. */
- BUFFER_TRACE(bh, "free child branches");
- ext4_free_branches(handle, inode, bh,
- (__le32 *) bh->b_data,
- (__le32 *) bh->b_data + addr_per_block,
- depth);
- brelse(bh);
-
- /*
- * Everything below this this pointer has been
- * released. Now let this top-of-subtree go.
- *
- * We want the freeing of this indirect block to be
- * atomic in the journal with the updating of the
- * bitmap block which owns it. So make some room in
- * the journal.
- *
- * We zero the parent pointer *after* freeing its
- * pointee in the bitmaps, so if extend_transaction()
- * for some reason fails to put the bitmap changes and
- * the release into the same transaction, recovery
- * will merely complain about releasing a free block,
- * rather than leaking blocks.
- */
- if (ext4_handle_is_aborted(handle))
- return;
- if (try_to_extend_transaction(handle, inode)) {
- ext4_mark_inode_dirty(handle, inode);
- ext4_truncate_restart_trans(handle, inode,
- blocks_for_truncate(inode));
- }
-
- /*
- * The forget flag here is critical because if
- * we are journaling (and not doing data
- * journaling), we have to make sure a revoke
- * record is written to prevent the journal
- * replay from overwriting the (former)
- * indirect block if it gets reallocated as a
- * data block. This must happen in the same
- * transaction where the data blocks are
- * actually freed.
- */
- ext4_free_blocks(handle, inode, NULL, nr, 1,
- EXT4_FREE_BLOCKS_METADATA|
- EXT4_FREE_BLOCKS_FORGET);
-
- if (parent_bh) {
- /*
- * The block which we have just freed is
- * pointed to by an indirect block: journal it
- */
- BUFFER_TRACE(parent_bh, "get_write_access");
- if (!ext4_journal_get_write_access(handle,
- parent_bh)){
- *p = 0;
- BUFFER_TRACE(parent_bh,
- "call ext4_handle_dirty_metadata");
- ext4_handle_dirty_metadata(handle,
- inode,
- parent_bh);
- }
- }
- }
- } else {
- /* We have reached the bottom of the tree. */
- BUFFER_TRACE(parent_bh, "free data blocks");
- ext4_free_data(handle, inode, parent_bh, first, last);
- }
-}
-
int ext4_can_truncate(struct inode *inode)
{
if (S_ISREG(inode->i_mode))
@@ -4476,19 +3129,6 @@
*/
void ext4_truncate(struct inode *inode)
{
- handle_t *handle;
- struct ext4_inode_info *ei = EXT4_I(inode);
- __le32 *i_data = ei->i_data;
- int addr_per_block = EXT4_ADDR_PER_BLOCK(inode->i_sb);
- struct address_space *mapping = inode->i_mapping;
- ext4_lblk_t offsets[4];
- Indirect chain[4];
- Indirect *partial;
- __le32 nr = 0;
- int n = 0;
- ext4_lblk_t last_block, max_block;
- unsigned blocksize = inode->i_sb->s_blocksize;
-
trace_ext4_truncate_enter(inode);
if (!ext4_can_truncate(inode))
@@ -4499,149 +3139,11 @@
if (inode->i_size == 0 && !test_opt(inode->i_sb, NO_AUTO_DA_ALLOC))
ext4_set_inode_state(inode, EXT4_STATE_DA_ALLOC_CLOSE);
- if (ext4_test_inode_flag(inode, EXT4_INODE_EXTENTS)) {
+ if (ext4_test_inode_flag(inode, EXT4_INODE_EXTENTS))
ext4_ext_truncate(inode);
- trace_ext4_truncate_exit(inode);
- return;
- }
+ else
+ ext4_ind_truncate(inode);
- handle = start_transaction(inode);
- if (IS_ERR(handle))
- return; /* AKPM: return what? */
-
- last_block = (inode->i_size + blocksize-1)
- >> EXT4_BLOCK_SIZE_BITS(inode->i_sb);
- max_block = (EXT4_SB(inode->i_sb)->s_bitmap_maxbytes + blocksize-1)
- >> EXT4_BLOCK_SIZE_BITS(inode->i_sb);
-
- if (inode->i_size & (blocksize - 1))
- if (ext4_block_truncate_page(handle, mapping, inode->i_size))
- goto out_stop;
-
- if (last_block != max_block) {
- n = ext4_block_to_path(inode, last_block, offsets, NULL);
- if (n == 0)
- goto out_stop; /* error */
- }
-
- /*
- * OK. This truncate is going to happen. We add the inode to the
- * orphan list, so that if this truncate spans multiple transactions,
- * and we crash, we will resume the truncate when the filesystem
- * recovers. It also marks the inode dirty, to catch the new size.
- *
- * Implication: the file must always be in a sane, consistent
- * truncatable state while each transaction commits.
- */
- if (ext4_orphan_add(handle, inode))
- goto out_stop;
-
- /*
- * From here we block out all ext4_get_block() callers who want to
- * modify the block allocation tree.
- */
- down_write(&ei->i_data_sem);
-
- ext4_discard_preallocations(inode);
-
- /*
- * The orphan list entry will now protect us from any crash which
- * occurs before the truncate completes, so it is now safe to propagate
- * the new, shorter inode size (held for now in i_size) into the
- * on-disk inode. We do this via i_disksize, which is the value which
- * ext4 *really* writes onto the disk inode.
- */
- ei->i_disksize = inode->i_size;
-
- if (last_block == max_block) {
- /*
- * It is unnecessary to free any data blocks if last_block is
- * equal to the indirect block limit.
- */
- goto out_unlock;
- } else if (n == 1) { /* direct blocks */
- ext4_free_data(handle, inode, NULL, i_data+offsets[0],
- i_data + EXT4_NDIR_BLOCKS);
- goto do_indirects;
- }
-
- partial = ext4_find_shared(inode, n, offsets, chain, &nr);
- /* Kill the top of shared branch (not detached) */
- if (nr) {
- if (partial == chain) {
- /* Shared branch grows from the inode */
- ext4_free_branches(handle, inode, NULL,
- &nr, &nr+1, (chain+n-1) - partial);
- *partial->p = 0;
- /*
- * We mark the inode dirty prior to restart,
- * and prior to stop. No need for it here.
- */
- } else {
- /* Shared branch grows from an indirect block */
- BUFFER_TRACE(partial->bh, "get_write_access");
- ext4_free_branches(handle, inode, partial->bh,
- partial->p,
- partial->p+1, (chain+n-1) - partial);
- }
- }
- /* Clear the ends of indirect blocks on the shared branch */
- while (partial > chain) {
- ext4_free_branches(handle, inode, partial->bh, partial->p + 1,
- (__le32*)partial->bh->b_data+addr_per_block,
- (chain+n-1) - partial);
- BUFFER_TRACE(partial->bh, "call brelse");
- brelse(partial->bh);
- partial--;
- }
-do_indirects:
- /* Kill the remaining (whole) subtrees */
- switch (offsets[0]) {
- default:
- nr = i_data[EXT4_IND_BLOCK];
- if (nr) {
- ext4_free_branches(handle, inode, NULL, &nr, &nr+1, 1);
- i_data[EXT4_IND_BLOCK] = 0;
- }
- case EXT4_IND_BLOCK:
- nr = i_data[EXT4_DIND_BLOCK];
- if (nr) {
- ext4_free_branches(handle, inode, NULL, &nr, &nr+1, 2);
- i_data[EXT4_DIND_BLOCK] = 0;
- }
- case EXT4_DIND_BLOCK:
- nr = i_data[EXT4_TIND_BLOCK];
- if (nr) {
- ext4_free_branches(handle, inode, NULL, &nr, &nr+1, 3);
- i_data[EXT4_TIND_BLOCK] = 0;
- }
- case EXT4_TIND_BLOCK:
- ;
- }
-
-out_unlock:
- up_write(&ei->i_data_sem);
- inode->i_mtime = inode->i_ctime = ext4_current_time(inode);
- ext4_mark_inode_dirty(handle, inode);
-
- /*
- * In a multi-transaction truncate, we only make the final transaction
- * synchronous
- */
- if (IS_SYNC(inode))
- ext4_handle_sync(handle);
-out_stop:
- /*
- * If this was a simple ftruncate(), and the file will remain alive
- * then we need to clear up the orphan record which we created above.
- * However, if this was a real unlink then we were called by
- * ext4_delete_inode(), and we allow that function to clean up the
- * orphan info for us.
- */
- if (inode->i_nlink)
- ext4_orphan_del(handle, inode);
-
- ext4_journal_stop(handle);
trace_ext4_truncate_exit(inode);
}
@@ -5012,7 +3514,7 @@
(S_ISLNK(inode->i_mode) &&
!ext4_inode_is_fast_symlink(inode))) {
/* Validate block references which are part of inode */
- ret = ext4_check_inode_blockref(inode);
+ ret = ext4_ind_check_inode(inode);
}
if (ret)
goto bad_inode;
@@ -5459,34 +3961,10 @@
return 0;
}
-static int ext4_indirect_trans_blocks(struct inode *inode, int nrblocks,
- int chunk)
-{
- int indirects;
-
- /* if nrblocks are contiguous */
- if (chunk) {
- /*
- * With N contiguous data blocks, we need at most
- * N/EXT4_ADDR_PER_BLOCK(inode->i_sb) + 1 indirect blocks,
- * 2 dindirect blocks, and 1 tindirect block
- */
- return DIV_ROUND_UP(nrblocks,
- EXT4_ADDR_PER_BLOCK(inode->i_sb)) + 4;
- }
- /*
- * if nrblocks are not contiguous, worse case, each block touch
- * a indirect block, and each indirect block touch a double indirect
- * block, plus a triple indirect block
- */
- indirects = nrblocks * 2 + 1;
- return indirects;
-}
-
static int ext4_index_trans_blocks(struct inode *inode, int nrblocks, int chunk)
{
if (!(ext4_test_inode_flag(inode, EXT4_INODE_EXTENTS)))
- return ext4_indirect_trans_blocks(inode, nrblocks, chunk);
+ return ext4_ind_trans_blocks(inode, nrblocks, chunk);
return ext4_ext_index_trans_blocks(inode, nrblocks, chunk);
}
diff --git a/fs/ext4/ioctl.c b/fs/ext4/ioctl.c
index 808c554..f18bfe3 100644
--- a/fs/ext4/ioctl.c
+++ b/fs/ext4/ioctl.c
@@ -202,8 +202,9 @@
struct super_block *sb = inode->i_sb;
int err, err2=0;
- if (!capable(CAP_SYS_RESOURCE))
- return -EPERM;
+ err = ext4_resize_begin(sb);
+ if (err)
+ return err;
if (get_user(n_blocks_count, (__u32 __user *)arg))
return -EFAULT;
@@ -221,6 +222,7 @@
if (err == 0)
err = err2;
mnt_drop_write(filp->f_path.mnt);
+ ext4_resize_end(sb);
return err;
}
@@ -271,8 +273,9 @@
struct super_block *sb = inode->i_sb;
int err, err2=0;
- if (!capable(CAP_SYS_RESOURCE))
- return -EPERM;
+ err = ext4_resize_begin(sb);
+ if (err)
+ return err;
if (copy_from_user(&input, (struct ext4_new_group_input __user *)arg,
sizeof(input)))
@@ -291,6 +294,7 @@
if (err == 0)
err = err2;
mnt_drop_write(filp->f_path.mnt);
+ ext4_resize_end(sb);
return err;
}
diff --git a/fs/ext4/mballoc.c b/fs/ext4/mballoc.c
index 6ed859d..17a5a57 100644
--- a/fs/ext4/mballoc.c
+++ b/fs/ext4/mballoc.c
@@ -75,8 +75,8 @@
*
* The inode preallocation space is used looking at the _logical_ start
* block. If only the logical file block falls within the range of prealloc
- * space we will consume the particular prealloc space. This make sure that
- * that the we have contiguous physical blocks representing the file blocks
+ * space we will consume the particular prealloc space. This makes sure that
+ * we have contiguous physical blocks representing the file blocks
*
* The important thing to be noted in case of inode prealloc space is that
* we don't modify the values associated to inode prealloc space except
@@ -84,7 +84,7 @@
*
* If we are not able to find blocks in the inode prealloc space and if we
* have the group allocation flag set then we look at the locality group
- * prealloc space. These are per CPU prealloc list repreasented as
+ * prealloc space. These are per CPU prealloc list represented as
*
* ext4_sb_info.s_locality_groups[smp_processor_id()]
*
@@ -128,12 +128,13 @@
* we are doing a group prealloc we try to normalize the request to
* sbi->s_mb_group_prealloc. Default value of s_mb_group_prealloc is
* 512 blocks. This can be tuned via
- * /sys/fs/ext4/<partition/mb_group_prealloc. The value is represented in
+ * /sys/fs/ext4/<partition>/mb_group_prealloc. The value is represented in
* terms of number of blocks. If we have mounted the file system with -O
* stripe=<value> option the group prealloc request is normalized to the
- * stripe value (sbi->s_stripe)
+ * the smallest multiple of the stripe value (sbi->s_stripe) which is
+ * greater than the default mb_group_prealloc.
*
- * The regular allocator(using the buddy cache) supports few tunables.
+ * The regular allocator (using the buddy cache) supports a few tunables.
*
* /sys/fs/ext4/<partition>/mb_min_to_scan
* /sys/fs/ext4/<partition>/mb_max_to_scan
@@ -152,7 +153,7 @@
* best extent in the found extents. Searching for the blocks starts with
* the group specified as the goal value in allocation context via
* ac_g_ex. Each group is first checked based on the criteria whether it
- * can used for allocation. ext4_mb_good_group explains how the groups are
+ * can be used for allocation. ext4_mb_good_group explains how the groups are
* checked.
*
* Both the prealloc space are getting populated as above. So for the first
@@ -492,10 +493,11 @@
b2 = (unsigned char *) bitmap;
for (i = 0; i < e4b->bd_sb->s_blocksize; i++) {
if (b1[i] != b2[i]) {
- printk(KERN_ERR "corruption in group %u "
- "at byte %u(%u): %x in copy != %x "
- "on disk/prealloc\n",
- e4b->bd_group, i, i * 8, b1[i], b2[i]);
+ ext4_msg(e4b->bd_sb, KERN_ERR,
+ "corruption in group %u "
+ "at byte %u(%u): %x in copy != %x "
+ "on disk/prealloc",
+ e4b->bd_group, i, i * 8, b1[i], b2[i]);
BUG();
}
}
@@ -1125,7 +1127,7 @@
grp = ext4_get_group_info(sb, group);
e4b->bd_blkbits = sb->s_blocksize_bits;
- e4b->bd_info = ext4_get_group_info(sb, group);
+ e4b->bd_info = grp;
e4b->bd_sb = sb;
e4b->bd_group = group;
e4b->bd_buddy_page = NULL;
@@ -1281,7 +1283,7 @@
}
}
-static void mb_set_bits(void *bm, int cur, int len)
+void ext4_set_bits(void *bm, int cur, int len)
{
__u32 *addr;
@@ -1510,7 +1512,7 @@
}
mb_set_largest_free_order(e4b->bd_sb, e4b->bd_info);
- mb_set_bits(EXT4_MB_BITMAP(e4b), ex->fe_start, len0);
+ ext4_set_bits(EXT4_MB_BITMAP(e4b), ex->fe_start, len0);
mb_check_buddy(e4b);
return ret;
@@ -2223,8 +2225,8 @@
EXT4_DESC_PER_BLOCK_BITS(sb);
meta_group_info = kmalloc(metalen, GFP_KERNEL);
if (meta_group_info == NULL) {
- printk(KERN_ERR "EXT4-fs: can't allocate mem for a "
- "buddy group\n");
+ ext4_msg(sb, KERN_ERR, "EXT4-fs: can't allocate mem "
+ "for a buddy group");
goto exit_meta_group_info;
}
sbi->s_group_info[group >> EXT4_DESC_PER_BLOCK_BITS(sb)] =
@@ -2237,7 +2239,7 @@
meta_group_info[i] = kmem_cache_alloc(cachep, GFP_KERNEL);
if (meta_group_info[i] == NULL) {
- printk(KERN_ERR "EXT4-fs: can't allocate buddy mem\n");
+ ext4_msg(sb, KERN_ERR, "EXT4-fs: can't allocate buddy mem");
goto exit_group_info;
}
memset(meta_group_info[i], 0, kmem_cache_size(cachep));
@@ -2279,8 +2281,10 @@
exit_group_info:
/* If a meta_group_info table has been allocated, release it now */
- if (group % EXT4_DESC_PER_BLOCK(sb) == 0)
+ if (group % EXT4_DESC_PER_BLOCK(sb) == 0) {
kfree(sbi->s_group_info[group >> EXT4_DESC_PER_BLOCK_BITS(sb)]);
+ sbi->s_group_info[group >> EXT4_DESC_PER_BLOCK_BITS(sb)] = NULL;
+ }
exit_meta_group_info:
return -ENOMEM;
} /* ext4_mb_add_groupinfo */
@@ -2328,23 +2332,26 @@
/* An 8TB filesystem with 64-bit pointers requires a 4096 byte
* kmalloc. A 128kb malloc should suffice for a 256TB filesystem.
* So a two level scheme suffices for now. */
- sbi->s_group_info = kzalloc(array_size, GFP_KERNEL);
+ sbi->s_group_info = ext4_kvzalloc(array_size, GFP_KERNEL);
if (sbi->s_group_info == NULL) {
- printk(KERN_ERR "EXT4-fs: can't allocate buddy meta group\n");
+ ext4_msg(sb, KERN_ERR, "can't allocate buddy meta group");
return -ENOMEM;
}
sbi->s_buddy_cache = new_inode(sb);
if (sbi->s_buddy_cache == NULL) {
- printk(KERN_ERR "EXT4-fs: can't get new inode\n");
+ ext4_msg(sb, KERN_ERR, "can't get new inode");
goto err_freesgi;
}
- sbi->s_buddy_cache->i_ino = get_next_ino();
+ /* To avoid potentially colliding with an valid on-disk inode number,
+ * use EXT4_BAD_INO for the buddy cache inode number. This inode is
+ * not in the inode hash, so it should never be found by iget(), but
+ * this will avoid confusion if it ever shows up during debugging. */
+ sbi->s_buddy_cache->i_ino = EXT4_BAD_INO;
EXT4_I(sbi->s_buddy_cache)->i_disksize = 0;
for (i = 0; i < ngroups; i++) {
desc = ext4_get_group_desc(sb, i, NULL);
if (desc == NULL) {
- printk(KERN_ERR
- "EXT4-fs: can't read descriptor %u\n", i);
+ ext4_msg(sb, KERN_ERR, "can't read descriptor %u", i);
goto err_freebuddy;
}
if (ext4_mb_add_groupinfo(sb, i, desc) != 0)
@@ -2362,7 +2369,7 @@
kfree(sbi->s_group_info[i]);
iput(sbi->s_buddy_cache);
err_freesgi:
- kfree(sbi->s_group_info);
+ ext4_kvfree(sbi->s_group_info);
return -ENOMEM;
}
@@ -2404,14 +2411,15 @@
slab_size, 0, SLAB_RECLAIM_ACCOUNT,
NULL);
+ ext4_groupinfo_caches[cache_index] = cachep;
+
mutex_unlock(&ext4_grpinfo_slab_create_mutex);
if (!cachep) {
- printk(KERN_EMERG "EXT4: no memory for groupinfo slab cache\n");
+ printk(KERN_EMERG
+ "EXT4-fs: no memory for groupinfo slab cache\n");
return -ENOMEM;
}
- ext4_groupinfo_caches[cache_index] = cachep;
-
return 0;
}
@@ -2457,12 +2465,6 @@
i++;
} while (i <= sb->s_blocksize_bits + 1);
- /* init file for buddy data */
- ret = ext4_mb_init_backend(sb);
- if (ret != 0) {
- goto out;
- }
-
spin_lock_init(&sbi->s_md_lock);
spin_lock_init(&sbi->s_bal_lock);
@@ -2472,6 +2474,18 @@
sbi->s_mb_stream_request = MB_DEFAULT_STREAM_THRESHOLD;
sbi->s_mb_order2_reqs = MB_DEFAULT_ORDER2_REQS;
sbi->s_mb_group_prealloc = MB_DEFAULT_GROUP_PREALLOC;
+ /*
+ * If there is a s_stripe > 1, then we set the s_mb_group_prealloc
+ * to the lowest multiple of s_stripe which is bigger than
+ * the s_mb_group_prealloc as determined above. We want
+ * the preallocation size to be an exact multiple of the
+ * RAID stripe size so that preallocations don't fragment
+ * the stripes.
+ */
+ if (sbi->s_stripe > 1) {
+ sbi->s_mb_group_prealloc = roundup(
+ sbi->s_mb_group_prealloc, sbi->s_stripe);
+ }
sbi->s_locality_groups = alloc_percpu(struct ext4_locality_group);
if (sbi->s_locality_groups == NULL) {
@@ -2487,6 +2501,12 @@
spin_lock_init(&lg->lg_prealloc_lock);
}
+ /* init file for buddy data */
+ ret = ext4_mb_init_backend(sb);
+ if (ret != 0) {
+ goto out;
+ }
+
if (sbi->s_proc)
proc_create_data("mb_groups", S_IRUGO, sbi->s_proc,
&ext4_mb_seq_groups_fops, sb);
@@ -2544,32 +2564,32 @@
EXT4_DESC_PER_BLOCK_BITS(sb);
for (i = 0; i < num_meta_group_infos; i++)
kfree(sbi->s_group_info[i]);
- kfree(sbi->s_group_info);
+ ext4_kvfree(sbi->s_group_info);
}
kfree(sbi->s_mb_offsets);
kfree(sbi->s_mb_maxs);
if (sbi->s_buddy_cache)
iput(sbi->s_buddy_cache);
if (sbi->s_mb_stats) {
- printk(KERN_INFO
- "EXT4-fs: mballoc: %u blocks %u reqs (%u success)\n",
+ ext4_msg(sb, KERN_INFO,
+ "mballoc: %u blocks %u reqs (%u success)",
atomic_read(&sbi->s_bal_allocated),
atomic_read(&sbi->s_bal_reqs),
atomic_read(&sbi->s_bal_success));
- printk(KERN_INFO
- "EXT4-fs: mballoc: %u extents scanned, %u goal hits, "
- "%u 2^N hits, %u breaks, %u lost\n",
+ ext4_msg(sb, KERN_INFO,
+ "mballoc: %u extents scanned, %u goal hits, "
+ "%u 2^N hits, %u breaks, %u lost",
atomic_read(&sbi->s_bal_ex_scanned),
atomic_read(&sbi->s_bal_goals),
atomic_read(&sbi->s_bal_2orders),
atomic_read(&sbi->s_bal_breaks),
atomic_read(&sbi->s_mb_lost_chunks));
- printk(KERN_INFO
- "EXT4-fs: mballoc: %lu generated and it took %Lu\n",
- sbi->s_mb_buddies_generated++,
+ ext4_msg(sb, KERN_INFO,
+ "mballoc: %lu generated and it took %Lu",
+ sbi->s_mb_buddies_generated,
sbi->s_mb_generation_time);
- printk(KERN_INFO
- "EXT4-fs: mballoc: %u preallocated, %u discarded\n",
+ ext4_msg(sb, KERN_INFO,
+ "mballoc: %u preallocated, %u discarded",
atomic_read(&sbi->s_mb_preallocated),
atomic_read(&sbi->s_mb_discarded));
}
@@ -2628,6 +2648,15 @@
rb_erase(&entry->node, &(db->bb_free_root));
mb_free_blocks(NULL, &e4b, entry->start_blk, entry->count);
+ /*
+ * Clear the trimmed flag for the group so that the next
+ * ext4_trim_fs can trim it.
+ * If the volume is mounted with -o discard, online discard
+ * is supported and the free blocks will be trimmed online.
+ */
+ if (!test_opt(sb, DISCARD))
+ EXT4_MB_GRP_CLEAR_TRIMMED(db);
+
if (!db->bb_free_root.rb_node) {
/* No more items in the per group rb tree
* balance refcounts from ext4_mb_free_metadata()
@@ -2771,8 +2800,8 @@
* We leak some of the blocks here.
*/
ext4_lock_group(sb, ac->ac_b_ex.fe_group);
- mb_set_bits(bitmap_bh->b_data, ac->ac_b_ex.fe_start,
- ac->ac_b_ex.fe_len);
+ ext4_set_bits(bitmap_bh->b_data, ac->ac_b_ex.fe_start,
+ ac->ac_b_ex.fe_len);
ext4_unlock_group(sb, ac->ac_b_ex.fe_group);
err = ext4_handle_dirty_metadata(handle, NULL, bitmap_bh);
if (!err)
@@ -2790,7 +2819,8 @@
}
}
#endif
- mb_set_bits(bitmap_bh->b_data, ac->ac_b_ex.fe_start,ac->ac_b_ex.fe_len);
+ ext4_set_bits(bitmap_bh->b_data, ac->ac_b_ex.fe_start,
+ ac->ac_b_ex.fe_len);
if (gdp->bg_flags & cpu_to_le16(EXT4_BG_BLOCK_UNINIT)) {
gdp->bg_flags &= cpu_to_le16(~EXT4_BG_BLOCK_UNINIT);
ext4_free_blks_set(sb, gdp,
@@ -2830,8 +2860,9 @@
/*
* here we normalize request for locality group
- * Group request are normalized to s_strip size if we set the same via mount
- * option. If not we set it to s_mb_group_prealloc which can be configured via
+ * Group request are normalized to s_mb_group_prealloc, which goes to
+ * s_strip if we set the same via mount option.
+ * s_mb_group_prealloc can be configured via
* /sys/fs/ext4/<partition>/mb_group_prealloc
*
* XXX: should we try to preallocate more than the group has now?
@@ -2842,10 +2873,7 @@
struct ext4_locality_group *lg = ac->ac_lg;
BUG_ON(lg == NULL);
- if (EXT4_SB(sb)->s_stripe)
- ac->ac_g_ex.fe_len = EXT4_SB(sb)->s_stripe;
- else
- ac->ac_g_ex.fe_len = EXT4_SB(sb)->s_mb_group_prealloc;
+ ac->ac_g_ex.fe_len = EXT4_SB(sb)->s_mb_group_prealloc;
mb_debug(1, "#%u: goal %u blocks for locality group\n",
current->pid, ac->ac_g_ex.fe_len);
}
@@ -3001,9 +3029,10 @@
if (start + size <= ac->ac_o_ex.fe_logical &&
start > ac->ac_o_ex.fe_logical) {
- printk(KERN_ERR "start %lu, size %lu, fe_logical %lu\n",
- (unsigned long) start, (unsigned long) size,
- (unsigned long) ac->ac_o_ex.fe_logical);
+ ext4_msg(ac->ac_sb, KERN_ERR,
+ "start %lu, size %lu, fe_logical %lu",
+ (unsigned long) start, (unsigned long) size,
+ (unsigned long) ac->ac_o_ex.fe_logical);
}
BUG_ON(start + size <= ac->ac_o_ex.fe_logical &&
start > ac->ac_o_ex.fe_logical);
@@ -3262,7 +3291,7 @@
while (n) {
entry = rb_entry(n, struct ext4_free_data, node);
- mb_set_bits(bitmap, entry->start_blk, entry->count);
+ ext4_set_bits(bitmap, entry->start_blk, entry->count);
n = rb_next(n);
}
return;
@@ -3304,7 +3333,7 @@
if (unlikely(len == 0))
continue;
BUG_ON(groupnr != group);
- mb_set_bits(bitmap, start, len);
+ ext4_set_bits(bitmap, start, len);
preallocated += len;
count++;
}
@@ -3584,10 +3613,11 @@
bit = next + 1;
}
if (free != pa->pa_free) {
- printk(KERN_CRIT "pa %p: logic %lu, phys. %lu, len %lu\n",
- pa, (unsigned long) pa->pa_lstart,
- (unsigned long) pa->pa_pstart,
- (unsigned long) pa->pa_len);
+ ext4_msg(e4b->bd_sb, KERN_CRIT,
+ "pa %p: logic %lu, phys. %lu, len %lu",
+ pa, (unsigned long) pa->pa_lstart,
+ (unsigned long) pa->pa_pstart,
+ (unsigned long) pa->pa_len);
ext4_grp_locked_error(sb, group, 0, 0, "free %u, pa_free %u",
free, pa->pa_free);
/*
@@ -3775,7 +3805,8 @@
* use preallocation while we're discarding it */
spin_unlock(&pa->pa_lock);
spin_unlock(&ei->i_prealloc_lock);
- printk(KERN_ERR "uh-oh! used pa while discarding\n");
+ ext4_msg(sb, KERN_ERR,
+ "uh-oh! used pa while discarding");
WARN_ON(1);
schedule_timeout_uninterruptible(HZ);
goto repeat;
@@ -3852,12 +3883,13 @@
(EXT4_SB(sb)->s_mount_flags & EXT4_MF_FS_ABORTED))
return;
- printk(KERN_ERR "EXT4-fs: Can't allocate:"
- " Allocation context details:\n");
- printk(KERN_ERR "EXT4-fs: status %d flags %d\n",
+ ext4_msg(ac->ac_sb, KERN_ERR, "EXT4-fs: Can't allocate:"
+ " Allocation context details:");
+ ext4_msg(ac->ac_sb, KERN_ERR, "EXT4-fs: status %d flags %d",
ac->ac_status, ac->ac_flags);
- printk(KERN_ERR "EXT4-fs: orig %lu/%lu/%lu@%lu, goal %lu/%lu/%lu@%lu, "
- "best %lu/%lu/%lu@%lu cr %d\n",
+ ext4_msg(ac->ac_sb, KERN_ERR, "EXT4-fs: orig %lu/%lu/%lu@%lu, "
+ "goal %lu/%lu/%lu@%lu, "
+ "best %lu/%lu/%lu@%lu cr %d",
(unsigned long)ac->ac_o_ex.fe_group,
(unsigned long)ac->ac_o_ex.fe_start,
(unsigned long)ac->ac_o_ex.fe_len,
@@ -3871,9 +3903,9 @@
(unsigned long)ac->ac_b_ex.fe_len,
(unsigned long)ac->ac_b_ex.fe_logical,
(int)ac->ac_criteria);
- printk(KERN_ERR "EXT4-fs: %lu scanned, %d found\n", ac->ac_ex_scanned,
- ac->ac_found);
- printk(KERN_ERR "EXT4-fs: groups: \n");
+ ext4_msg(ac->ac_sb, KERN_ERR, "EXT4-fs: %lu scanned, %d found",
+ ac->ac_ex_scanned, ac->ac_found);
+ ext4_msg(ac->ac_sb, KERN_ERR, "EXT4-fs: groups: ");
ngroups = ext4_get_groups_count(sb);
for (i = 0; i < ngroups; i++) {
struct ext4_group_info *grp = ext4_get_group_info(sb, i);
@@ -4637,7 +4669,7 @@
}
ext4_mark_super_dirty(sb);
error_return:
- if (freed)
+ if (freed && !(flags & EXT4_FREE_BLOCKS_NO_QUOT_UPDATE))
dquot_free_block(inode, freed);
brelse(bitmap_bh);
ext4_std_error(sb, err);
@@ -4645,7 +4677,7 @@
}
/**
- * ext4_add_groupblocks() -- Add given blocks to an existing group
+ * ext4_group_add_blocks() -- Add given blocks to an existing group
* @handle: handle to this transaction
* @sb: super block
* @block: start physcial block to add to the block group
@@ -4653,7 +4685,7 @@
*
* This marks the blocks as free in the bitmap and buddy.
*/
-void ext4_add_groupblocks(handle_t *handle, struct super_block *sb,
+int ext4_group_add_blocks(handle_t *handle, struct super_block *sb,
ext4_fsblk_t block, unsigned long count)
{
struct buffer_head *bitmap_bh = NULL;
@@ -4666,25 +4698,35 @@
struct ext4_buddy e4b;
int err = 0, ret, blk_free_count;
ext4_grpblk_t blocks_freed;
- struct ext4_group_info *grp;
ext4_debug("Adding block(s) %llu-%llu\n", block, block + count - 1);
+ if (count == 0)
+ return 0;
+
ext4_get_group_no_and_offset(sb, block, &block_group, &bit);
- grp = ext4_get_group_info(sb, block_group);
/*
* Check to see if we are freeing blocks across a group
* boundary.
*/
- if (bit + count > EXT4_BLOCKS_PER_GROUP(sb))
+ if (bit + count > EXT4_BLOCKS_PER_GROUP(sb)) {
+ ext4_warning(sb, "too much blocks added to group %u\n",
+ block_group);
+ err = -EINVAL;
goto error_return;
+ }
bitmap_bh = ext4_read_block_bitmap(sb, block_group);
- if (!bitmap_bh)
+ if (!bitmap_bh) {
+ err = -EIO;
goto error_return;
+ }
+
desc = ext4_get_group_desc(sb, block_group, &gd_bh);
- if (!desc)
+ if (!desc) {
+ err = -EIO;
goto error_return;
+ }
if (in_range(ext4_block_bitmap(sb, desc), block, count) ||
in_range(ext4_inode_bitmap(sb, desc), block, count) ||
@@ -4694,6 +4736,7 @@
ext4_error(sb, "Adding blocks in system zones - "
"Block = %llu, count = %lu",
block, count);
+ err = -EINVAL;
goto error_return;
}
@@ -4762,7 +4805,7 @@
error_return:
brelse(bitmap_bh);
ext4_std_error(sb, err);
- return;
+ return err;
}
/**
@@ -4782,6 +4825,8 @@
{
struct ext4_free_extent ex;
+ trace_ext4_trim_extent(sb, group, start, count);
+
assert_spin_locked(ext4_group_lock_ptr(sb, group));
ex.fe_start = start;
@@ -4802,7 +4847,7 @@
/**
* ext4_trim_all_free -- function to trim all free space in alloc. group
* @sb: super block for file system
- * @e4b: ext4 buddy
+ * @group: group to be trimmed
* @start: first group block to examine
* @max: last group block to examine
* @minblocks: minimum extent block count
@@ -4823,10 +4868,12 @@
ext4_grpblk_t minblocks)
{
void *bitmap;
- ext4_grpblk_t next, count = 0;
+ ext4_grpblk_t next, count = 0, free_count = 0;
struct ext4_buddy e4b;
int ret;
+ trace_ext4_trim_all_free(sb, group, start, max);
+
ret = ext4_mb_load_buddy(sb, group, &e4b);
if (ret) {
ext4_error(sb, "Error in loading buddy "
@@ -4836,6 +4883,10 @@
bitmap = e4b.bd_bitmap;
ext4_lock_group(sb, group);
+ if (EXT4_MB_GRP_WAS_TRIMMED(e4b.bd_info) &&
+ minblocks >= atomic_read(&EXT4_SB(sb)->s_last_trim_minblks))
+ goto out;
+
start = (e4b.bd_info->bb_first_free > start) ?
e4b.bd_info->bb_first_free : start;
@@ -4850,6 +4901,7 @@
next - start, group, &e4b);
count += next - start;
}
+ free_count += next - start;
start = next + 1;
if (fatal_signal_pending(current)) {
@@ -4863,9 +4915,13 @@
ext4_lock_group(sb, group);
}
- if ((e4b.bd_info->bb_free - count) < minblocks)
+ if ((e4b.bd_info->bb_free - free_count) < minblocks)
break;
}
+
+ if (!ret)
+ EXT4_MB_GRP_SET_TRIMMED(e4b.bd_info);
+out:
ext4_unlock_group(sb, group);
ext4_mb_unload_buddy(&e4b);
@@ -4904,6 +4960,8 @@
if (unlikely(minlen > EXT4_BLOCKS_PER_GROUP(sb)))
return -EINVAL;
+ if (start + len <= first_data_blk)
+ goto out;
if (start < first_data_blk) {
len -= first_data_blk - start;
start = first_data_blk;
@@ -4952,5 +5010,9 @@
}
range->len = trimmed * sb->s_blocksize;
+ if (!ret)
+ atomic_set(&EXT4_SB(sb)->s_last_trim_minblks, minlen);
+
+out:
return ret;
}
diff --git a/fs/ext4/mballoc.h b/fs/ext4/mballoc.h
index 20b5e7b..9d4a636 100644
--- a/fs/ext4/mballoc.h
+++ b/fs/ext4/mballoc.h
@@ -187,7 +187,6 @@
__u16 ac_flags; /* allocation hints */
__u8 ac_status;
__u8 ac_criteria;
- __u8 ac_repeats;
__u8 ac_2order; /* if request is to allocate 2^N blocks and
* N > 0, the field stores N, otherwise 0 */
__u8 ac_op; /* operation, for history only */
diff --git a/fs/ext4/namei.c b/fs/ext4/namei.c
index 8c9baba..f8068c7 100644
--- a/fs/ext4/namei.c
+++ b/fs/ext4/namei.c
@@ -289,7 +289,7 @@
while (len--) printk("%c", *name++);
ext4fs_dirhash(de->name, de->name_len, &h);
printk(":%x.%u ", h.hash,
- ((char *) de - base));
+ (unsigned) ((char *) de - base));
}
space += EXT4_DIR_REC_LEN(de->name_len);
names++;
@@ -1013,7 +1013,7 @@
*err = -ENOENT;
errout:
- dxtrace(printk(KERN_DEBUG "%s not found\n", name));
+ dxtrace(printk(KERN_DEBUG "%s not found\n", d_name->name));
dx_release (frames);
return NULL;
}
@@ -1985,18 +1985,11 @@
if (!list_empty(&EXT4_I(inode)->i_orphan))
goto out_unlock;
- /* Orphan handling is only valid for files with data blocks
- * being truncated, or files being unlinked. */
-
- /* @@@ FIXME: Observation from aviro:
- * I think I can trigger J_ASSERT in ext4_orphan_add(). We block
- * here (on s_orphan_lock), so race with ext4_link() which might bump
- * ->i_nlink. For, say it, character device. Not a regular file,
- * not a directory, not a symlink and ->i_nlink > 0.
- *
- * tytso, 4/25/2009: I'm not sure how that could happen;
- * shouldn't the fs core protect us from these sort of
- * unlink()/link() races?
+ /*
+ * Orphan handling is only valid for files with data blocks
+ * being truncated, or files being unlinked. Note that we either
+ * hold i_mutex, or the inode can not be referenced from outside,
+ * so i_nlink should not be bumped due to race
*/
J_ASSERT((S_ISREG(inode->i_mode) || S_ISDIR(inode->i_mode) ||
S_ISLNK(inode->i_mode)) || inode->i_nlink == 0);
@@ -2260,9 +2253,11 @@
/*
* For non-fast symlinks, we just allocate inode and put it on
* orphan list in the first transaction => we need bitmap,
- * group descriptor, sb, inode block, quota blocks.
+ * group descriptor, sb, inode block, quota blocks, and
+ * possibly selinux xattr blocks.
*/
- credits = 4 + EXT4_MAXQUOTAS_INIT_BLOCKS(dir->i_sb);
+ credits = 4 + EXT4_MAXQUOTAS_INIT_BLOCKS(dir->i_sb) +
+ EXT4_XATTR_TRANS_BLOCKS;
} else {
/*
* Fast symlink. We have to add entry to directory
diff --git a/fs/ext4/page-io.c b/fs/ext4/page-io.c
index 7bb8f76..430c401 100644
--- a/fs/ext4/page-io.c
+++ b/fs/ext4/page-io.c
@@ -285,11 +285,7 @@
io_end = ext4_init_io_end(inode, GFP_NOFS);
if (!io_end)
return -ENOMEM;
- do {
- bio = bio_alloc(GFP_NOIO, nvecs);
- nvecs >>= 1;
- } while (bio == NULL);
-
+ bio = bio_alloc(GFP_NOIO, min(nvecs, BIO_MAX_PAGES));
bio->bi_sector = bh->b_blocknr * (bh->b_size >> 9);
bio->bi_bdev = bh->b_bdev;
bio->bi_private = io->io_end = io_end;
diff --git a/fs/ext4/resize.c b/fs/ext4/resize.c
index 80bbc9c..707d3f1 100644
--- a/fs/ext4/resize.c
+++ b/fs/ext4/resize.c
@@ -16,6 +16,35 @@
#include "ext4_jbd2.h"
+int ext4_resize_begin(struct super_block *sb)
+{
+ int ret = 0;
+
+ if (!capable(CAP_SYS_RESOURCE))
+ return -EPERM;
+
+ /*
+ * We are not allowed to do online-resizing on a filesystem mounted
+ * with error, because it can destroy the filesystem easily.
+ */
+ if (EXT4_SB(sb)->s_mount_state & EXT4_ERROR_FS) {
+ ext4_warning(sb, "There are errors in the filesystem, "
+ "so online resizing is not allowed\n");
+ return -EPERM;
+ }
+
+ if (test_and_set_bit_lock(EXT4_RESIZING, &EXT4_SB(sb)->s_resize_flags))
+ ret = -EBUSY;
+
+ return ret;
+}
+
+void ext4_resize_end(struct super_block *sb)
+{
+ clear_bit_unlock(EXT4_RESIZING, &EXT4_SB(sb)->s_resize_flags);
+ smp_mb__after_clear_bit();
+}
+
#define outside(b, first, last) ((b) < (first) || (b) >= (last))
#define inside(b, first, last) ((b) >= (first) && (b) < (last))
@@ -118,10 +147,8 @@
brelse(bh);
bh = ERR_PTR(err);
} else {
- lock_buffer(bh);
memset(bh->b_data, 0, sb->s_blocksize);
set_buffer_uptodate(bh);
- unlock_buffer(bh);
}
return bh;
@@ -132,8 +159,7 @@
* If that fails, restart the transaction & regain write access for the
* buffer head which is used for block_bitmap modifications.
*/
-static int extend_or_restart_transaction(handle_t *handle, int thresh,
- struct buffer_head *bh)
+static int extend_or_restart_transaction(handle_t *handle, int thresh)
{
int err;
@@ -144,9 +170,8 @@
if (err < 0)
return err;
if (err) {
- if ((err = ext4_journal_restart(handle, EXT4_MAX_TRANS_DATA)))
- return err;
- if ((err = ext4_journal_get_write_access(handle, bh)))
+ err = ext4_journal_restart(handle, EXT4_MAX_TRANS_DATA);
+ if (err)
return err;
}
@@ -181,21 +206,7 @@
if (IS_ERR(handle))
return PTR_ERR(handle);
- mutex_lock(&sbi->s_resize_lock);
- if (input->group != sbi->s_groups_count) {
- err = -EBUSY;
- goto exit_journal;
- }
-
- if (IS_ERR(bh = bclean(handle, sb, input->block_bitmap))) {
- err = PTR_ERR(bh);
- goto exit_journal;
- }
-
- if (ext4_bg_has_super(sb, input->group)) {
- ext4_debug("mark backup superblock %#04llx (+0)\n", start);
- ext4_set_bit(0, bh->b_data);
- }
+ BUG_ON(input->group != sbi->s_groups_count);
/* Copy all of the GDT blocks into the backup in this group */
for (i = 0, bit = 1, block = start + 1;
@@ -203,29 +214,26 @@
struct buffer_head *gdb;
ext4_debug("update backup group %#04llx (+%d)\n", block, bit);
-
- if ((err = extend_or_restart_transaction(handle, 1, bh)))
- goto exit_bh;
+ err = extend_or_restart_transaction(handle, 1);
+ if (err)
+ goto exit_journal;
gdb = sb_getblk(sb, block);
if (!gdb) {
err = -EIO;
- goto exit_bh;
+ goto exit_journal;
}
if ((err = ext4_journal_get_write_access(handle, gdb))) {
brelse(gdb);
- goto exit_bh;
+ goto exit_journal;
}
- lock_buffer(gdb);
memcpy(gdb->b_data, sbi->s_group_desc[i]->b_data, gdb->b_size);
set_buffer_uptodate(gdb);
- unlock_buffer(gdb);
err = ext4_handle_dirty_metadata(handle, NULL, gdb);
if (unlikely(err)) {
brelse(gdb);
- goto exit_bh;
+ goto exit_journal;
}
- ext4_set_bit(bit, bh->b_data);
brelse(gdb);
}
@@ -235,9 +243,22 @@
err = sb_issue_zeroout(sb, gdblocks + start + 1, reserved_gdb,
GFP_NOFS);
if (err)
- goto exit_bh;
- for (i = 0, bit = gdblocks + 1; i < reserved_gdb; i++, bit++)
- ext4_set_bit(bit, bh->b_data);
+ goto exit_journal;
+
+ err = extend_or_restart_transaction(handle, 2);
+ if (err)
+ goto exit_journal;
+
+ bh = bclean(handle, sb, input->block_bitmap);
+ if (IS_ERR(bh)) {
+ err = PTR_ERR(bh);
+ goto exit_journal;
+ }
+
+ if (ext4_bg_has_super(sb, input->group)) {
+ ext4_debug("mark backup group tables %#04llx (+0)\n", start);
+ ext4_set_bits(bh->b_data, 0, gdblocks + reserved_gdb + 1);
+ }
ext4_debug("mark block bitmap %#04llx (+%llu)\n", input->block_bitmap,
input->block_bitmap - start);
@@ -253,12 +274,9 @@
err = sb_issue_zeroout(sb, block, sbi->s_itb_per_group, GFP_NOFS);
if (err)
goto exit_bh;
- for (i = 0, bit = input->inode_table - start;
- i < sbi->s_itb_per_group; i++, bit++)
- ext4_set_bit(bit, bh->b_data);
+ ext4_set_bits(bh->b_data, input->inode_table - start,
+ sbi->s_itb_per_group);
- if ((err = extend_or_restart_transaction(handle, 2, bh)))
- goto exit_bh;
ext4_mark_bitmap_end(input->blocks_count, sb->s_blocksize * 8,
bh->b_data);
@@ -285,7 +303,6 @@
brelse(bh);
exit_journal:
- mutex_unlock(&sbi->s_resize_lock);
if ((err2 = ext4_journal_stop(handle)) && !err)
err = err2;
@@ -377,15 +394,15 @@
* fail once we start modifying the data on disk, because JBD has no rollback.
*/
static int add_new_gdb(handle_t *handle, struct inode *inode,
- struct ext4_new_group_data *input,
- struct buffer_head **primary)
+ ext4_group_t group)
{
struct super_block *sb = inode->i_sb;
struct ext4_super_block *es = EXT4_SB(sb)->s_es;
- unsigned long gdb_num = input->group / EXT4_DESC_PER_BLOCK(sb);
+ unsigned long gdb_num = group / EXT4_DESC_PER_BLOCK(sb);
ext4_fsblk_t gdblock = EXT4_SB(sb)->s_sbh->b_blocknr + 1 + gdb_num;
struct buffer_head **o_group_desc, **n_group_desc;
struct buffer_head *dind;
+ struct buffer_head *gdb_bh;
int gdbackups;
struct ext4_iloc iloc;
__le32 *data;
@@ -408,11 +425,12 @@
return -EPERM;
}
- *primary = sb_bread(sb, gdblock);
- if (!*primary)
+ gdb_bh = sb_bread(sb, gdblock);
+ if (!gdb_bh)
return -EIO;
- if ((gdbackups = verify_reserved_gdb(sb, *primary)) < 0) {
+ gdbackups = verify_reserved_gdb(sb, gdb_bh);
+ if (gdbackups < 0) {
err = gdbackups;
goto exit_bh;
}
@@ -427,7 +445,7 @@
data = (__le32 *)dind->b_data;
if (le32_to_cpu(data[gdb_num % EXT4_ADDR_PER_BLOCK(sb)]) != gdblock) {
ext4_warning(sb, "new group %u GDT block %llu not reserved",
- input->group, gdblock);
+ group, gdblock);
err = -EINVAL;
goto exit_dind;
}
@@ -436,7 +454,7 @@
if (unlikely(err))
goto exit_dind;
- err = ext4_journal_get_write_access(handle, *primary);
+ err = ext4_journal_get_write_access(handle, gdb_bh);
if (unlikely(err))
goto exit_sbh;
@@ -449,12 +467,13 @@
if (unlikely(err))
goto exit_dindj;
- n_group_desc = kmalloc((gdb_num + 1) * sizeof(struct buffer_head *),
- GFP_NOFS);
+ n_group_desc = ext4_kvmalloc((gdb_num + 1) *
+ sizeof(struct buffer_head *),
+ GFP_NOFS);
if (!n_group_desc) {
err = -ENOMEM;
- ext4_warning(sb,
- "not enough memory for %lu groups", gdb_num + 1);
+ ext4_warning(sb, "not enough memory for %lu groups",
+ gdb_num + 1);
goto exit_inode;
}
@@ -475,8 +494,8 @@
}
inode->i_blocks -= (gdbackups + 1) * sb->s_blocksize >> 9;
ext4_mark_iloc_dirty(handle, inode, &iloc);
- memset((*primary)->b_data, 0, sb->s_blocksize);
- err = ext4_handle_dirty_metadata(handle, NULL, *primary);
+ memset(gdb_bh->b_data, 0, sb->s_blocksize);
+ err = ext4_handle_dirty_metadata(handle, NULL, gdb_bh);
if (unlikely(err)) {
ext4_std_error(sb, err);
goto exit_inode;
@@ -486,10 +505,10 @@
o_group_desc = EXT4_SB(sb)->s_group_desc;
memcpy(n_group_desc, o_group_desc,
EXT4_SB(sb)->s_gdb_count * sizeof(struct buffer_head *));
- n_group_desc[gdb_num] = *primary;
+ n_group_desc[gdb_num] = gdb_bh;
EXT4_SB(sb)->s_group_desc = n_group_desc;
EXT4_SB(sb)->s_gdb_count++;
- kfree(o_group_desc);
+ ext4_kvfree(o_group_desc);
le16_add_cpu(&es->s_reserved_gdt_blocks, -1);
err = ext4_handle_dirty_metadata(handle, NULL, EXT4_SB(sb)->s_sbh);
@@ -499,6 +518,7 @@
return err;
exit_inode:
+ ext4_kvfree(n_group_desc);
/* ext4_handle_release_buffer(handle, iloc.bh); */
brelse(iloc.bh);
exit_dindj:
@@ -508,7 +528,7 @@
exit_dind:
brelse(dind);
exit_bh:
- brelse(*primary);
+ brelse(gdb_bh);
ext4_debug("leaving with error %d\n", err);
return err;
@@ -528,7 +548,7 @@
* backup GDT blocks are stored in their reserved primary GDT block.
*/
static int reserve_backup_gdb(handle_t *handle, struct inode *inode,
- struct ext4_new_group_data *input)
+ ext4_group_t group)
{
struct super_block *sb = inode->i_sb;
int reserved_gdb =le16_to_cpu(EXT4_SB(sb)->s_es->s_reserved_gdt_blocks);
@@ -599,7 +619,7 @@
* Finally we can add each of the reserved backup GDT blocks from
* the new group to its reserved primary GDT block.
*/
- blk = input->group * EXT4_BLOCKS_PER_GROUP(sb);
+ blk = group * EXT4_BLOCKS_PER_GROUP(sb);
for (i = 0; i < reserved_gdb; i++) {
int err2;
data = (__le32 *)primary[i]->b_data;
@@ -799,13 +819,6 @@
goto exit_put;
}
- mutex_lock(&sbi->s_resize_lock);
- if (input->group != sbi->s_groups_count) {
- ext4_warning(sb, "multiple resizers run on filesystem!");
- err = -EBUSY;
- goto exit_journal;
- }
-
if ((err = ext4_journal_get_write_access(handle, sbi->s_sbh)))
goto exit_journal;
@@ -820,16 +833,25 @@
if ((err = ext4_journal_get_write_access(handle, primary)))
goto exit_journal;
- if (reserved_gdb && ext4_bg_num_gdb(sb, input->group) &&
- (err = reserve_backup_gdb(handle, inode, input)))
+ if (reserved_gdb && ext4_bg_num_gdb(sb, input->group)) {
+ err = reserve_backup_gdb(handle, inode, input->group);
+ if (err)
+ goto exit_journal;
+ }
+ } else {
+ /*
+ * Note that we can access new group descriptor block safely
+ * only if add_new_gdb() succeeds.
+ */
+ err = add_new_gdb(handle, inode, input->group);
+ if (err)
goto exit_journal;
- } else if ((err = add_new_gdb(handle, inode, input, &primary)))
- goto exit_journal;
+ primary = sbi->s_group_desc[gdb_num];
+ }
/*
* OK, now we've set up the new group. Time to make it active.
*
- * We do not lock all allocations via s_resize_lock
* so we have to be safe wrt. concurrent accesses the group
* data. So we need to be careful to set all of the relevant
* group descriptor data etc. *before* we enable the group.
@@ -886,13 +908,9 @@
*
* The precise rules we use are:
*
- * * Writers of s_groups_count *must* hold s_resize_lock
- * AND
* * Writers must perform a smp_wmb() after updating all dependent
* data and before modifying the groups count
*
- * * Readers must hold s_resize_lock over the access
- * OR
* * Readers must perform an smp_rmb() after reading the groups count
* and before reading any dependent data.
*
@@ -937,10 +955,9 @@
ext4_handle_dirty_super(handle, sb);
exit_journal:
- mutex_unlock(&sbi->s_resize_lock);
if ((err2 = ext4_journal_stop(handle)) && !err)
err = err2;
- if (!err) {
+ if (!err && primary) {
update_backups(sb, sbi->s_sbh->b_blocknr, (char *)es,
sizeof(struct ext4_super_block));
update_backups(sb, primary->b_blocknr, primary->b_data,
@@ -969,16 +986,13 @@
ext4_grpblk_t add;
struct buffer_head *bh;
handle_t *handle;
- int err;
+ int err, err2;
ext4_group_t group;
- /* We don't need to worry about locking wrt other resizers just
- * yet: we're going to revalidate es->s_blocks_count after
- * taking the s_resize_lock below. */
o_blocks_count = ext4_blocks_count(es);
if (test_opt(sb, DEBUG))
- printk(KERN_DEBUG "EXT4-fs: extending last group from %llu uto %llu blocks\n",
+ printk(KERN_DEBUG "EXT4-fs: extending last group from %llu to %llu blocks\n",
o_blocks_count, n_blocks_count);
if (n_blocks_count == 0 || n_blocks_count == o_blocks_count)
@@ -995,7 +1009,7 @@
if (n_blocks_count < o_blocks_count) {
ext4_warning(sb, "can't shrink FS - resize aborted");
- return -EBUSY;
+ return -EINVAL;
}
/* Handle the remaining blocks in the last group only. */
@@ -1038,32 +1052,25 @@
goto exit_put;
}
- mutex_lock(&EXT4_SB(sb)->s_resize_lock);
- if (o_blocks_count != ext4_blocks_count(es)) {
- ext4_warning(sb, "multiple resizers run on filesystem!");
- mutex_unlock(&EXT4_SB(sb)->s_resize_lock);
- ext4_journal_stop(handle);
- err = -EBUSY;
- goto exit_put;
- }
-
if ((err = ext4_journal_get_write_access(handle,
EXT4_SB(sb)->s_sbh))) {
ext4_warning(sb, "error %d on journal write access", err);
- mutex_unlock(&EXT4_SB(sb)->s_resize_lock);
ext4_journal_stop(handle);
goto exit_put;
}
ext4_blocks_count_set(es, o_blocks_count + add);
- mutex_unlock(&EXT4_SB(sb)->s_resize_lock);
ext4_debug("freeing blocks %llu through %llu\n", o_blocks_count,
o_blocks_count + add);
/* We add the blocks to the bitmap and set the group need init bit */
- ext4_add_groupblocks(handle, sb, o_blocks_count, add);
+ err = ext4_group_add_blocks(handle, sb, o_blocks_count, add);
ext4_handle_dirty_super(handle, sb);
ext4_debug("freed blocks %llu through %llu\n", o_blocks_count,
o_blocks_count + add);
- if ((err = ext4_journal_stop(handle)))
+ err2 = ext4_journal_stop(handle);
+ if (!err && err2)
+ err = err2;
+
+ if (err)
goto exit_put;
if (test_opt(sb, DEBUG))
diff --git a/fs/ext4/super.c b/fs/ext4/super.c
index 9ea71aa..4687fea 100644
--- a/fs/ext4/super.c
+++ b/fs/ext4/super.c
@@ -110,6 +110,35 @@
#define IS_EXT3_SB(sb) (0)
#endif
+void *ext4_kvmalloc(size_t size, gfp_t flags)
+{
+ void *ret;
+
+ ret = kmalloc(size, flags);
+ if (!ret)
+ ret = __vmalloc(size, flags, PAGE_KERNEL);
+ return ret;
+}
+
+void *ext4_kvzalloc(size_t size, gfp_t flags)
+{
+ void *ret;
+
+ ret = kzalloc(size, flags);
+ if (!ret)
+ ret = __vmalloc(size, flags | __GFP_ZERO, PAGE_KERNEL);
+ return ret;
+}
+
+void ext4_kvfree(void *ptr)
+{
+ if (is_vmalloc_addr(ptr))
+ vfree(ptr);
+ else
+ kfree(ptr);
+
+}
+
ext4_fsblk_t ext4_block_bitmap(struct super_block *sb,
struct ext4_group_desc *bg)
{
@@ -269,6 +298,7 @@
journal_t *journal;
handle_t *handle;
+ trace_ext4_journal_start(sb, nblocks, _RET_IP_);
if (sb->s_flags & MS_RDONLY)
return ERR_PTR(-EROFS);
@@ -789,11 +819,8 @@
for (i = 0; i < sbi->s_gdb_count; i++)
brelse(sbi->s_group_desc[i]);
- kfree(sbi->s_group_desc);
- if (is_vmalloc_addr(sbi->s_flex_groups))
- vfree(sbi->s_flex_groups);
- else
- kfree(sbi->s_flex_groups);
+ ext4_kvfree(sbi->s_group_desc);
+ ext4_kvfree(sbi->s_flex_groups);
percpu_counter_destroy(&sbi->s_freeblocks_counter);
percpu_counter_destroy(&sbi->s_freeinodes_counter);
percpu_counter_destroy(&sbi->s_dirs_counter);
@@ -1976,15 +2003,11 @@
((le16_to_cpu(sbi->s_es->s_reserved_gdt_blocks) + 1) <<
EXT4_DESC_PER_BLOCK_BITS(sb))) / groups_per_flex;
size = flex_group_count * sizeof(struct flex_groups);
- sbi->s_flex_groups = kzalloc(size, GFP_KERNEL);
+ sbi->s_flex_groups = ext4_kvzalloc(size, GFP_KERNEL);
if (sbi->s_flex_groups == NULL) {
- sbi->s_flex_groups = vzalloc(size);
- if (sbi->s_flex_groups == NULL) {
- ext4_msg(sb, KERN_ERR,
- "not enough memory for %u flex groups",
- flex_group_count);
- goto failed;
- }
+ ext4_msg(sb, KERN_ERR, "not enough memory for %u flex groups",
+ flex_group_count);
+ goto failed;
}
for (i = 0; i < sbi->s_groups_count; i++) {
@@ -2383,17 +2406,25 @@
unsigned long stride = le16_to_cpu(sbi->s_es->s_raid_stride);
unsigned long stripe_width =
le32_to_cpu(sbi->s_es->s_raid_stripe_width);
+ int ret;
if (sbi->s_stripe && sbi->s_stripe <= sbi->s_blocks_per_group)
- return sbi->s_stripe;
+ ret = sbi->s_stripe;
+ else if (stripe_width <= sbi->s_blocks_per_group)
+ ret = stripe_width;
+ else if (stride <= sbi->s_blocks_per_group)
+ ret = stride;
+ else
+ ret = 0;
- if (stripe_width <= sbi->s_blocks_per_group)
- return stripe_width;
+ /*
+ * If the stripe width is 1, this makes no sense and
+ * we set it to 0 to turn off stripe handling code.
+ */
+ if (ret <= 1)
+ ret = 0;
- if (stride <= sbi->s_blocks_per_group)
- return stride;
-
- return 0;
+ return ret;
}
/* sysfs supprt */
@@ -3408,8 +3439,9 @@
(EXT4_MAX_BLOCK_FILE_PHYS / EXT4_BLOCKS_PER_GROUP(sb)));
db_count = (sbi->s_groups_count + EXT4_DESC_PER_BLOCK(sb) - 1) /
EXT4_DESC_PER_BLOCK(sb);
- sbi->s_group_desc = kmalloc(db_count * sizeof(struct buffer_head *),
- GFP_KERNEL);
+ sbi->s_group_desc = ext4_kvmalloc(db_count *
+ sizeof(struct buffer_head *),
+ GFP_KERNEL);
if (sbi->s_group_desc == NULL) {
ext4_msg(sb, KERN_ERR, "not enough memory");
goto failed_mount;
@@ -3491,7 +3523,7 @@
INIT_LIST_HEAD(&sbi->s_orphan); /* unlinked but open files */
mutex_init(&sbi->s_orphan_lock);
- mutex_init(&sbi->s_resize_lock);
+ sbi->s_resize_flags = 0;
sb->s_root = NULL;
@@ -3741,12 +3773,8 @@
}
failed_mount3:
del_timer(&sbi->s_err_report);
- if (sbi->s_flex_groups) {
- if (is_vmalloc_addr(sbi->s_flex_groups))
- vfree(sbi->s_flex_groups);
- else
- kfree(sbi->s_flex_groups);
- }
+ if (sbi->s_flex_groups)
+ ext4_kvfree(sbi->s_flex_groups);
percpu_counter_destroy(&sbi->s_freeblocks_counter);
percpu_counter_destroy(&sbi->s_freeinodes_counter);
percpu_counter_destroy(&sbi->s_dirs_counter);
@@ -3756,7 +3784,7 @@
failed_mount2:
for (i = 0; i < db_count; i++)
brelse(sbi->s_group_desc[i]);
- kfree(sbi->s_group_desc);
+ ext4_kvfree(sbi->s_group_desc);
failed_mount:
if (sbi->s_proc) {
remove_proc_entry(sb->s_id, ext4_proc_root);
diff --git a/fs/ext4/truncate.h b/fs/ext4/truncate.h
new file mode 100644
index 0000000..011ba66
--- /dev/null
+++ b/fs/ext4/truncate.h
@@ -0,0 +1,43 @@
+/*
+ * linux/fs/ext4/truncate.h
+ *
+ * Common inline functions needed for truncate support
+ */
+
+/*
+ * Truncate blocks that were not used by write. We have to truncate the
+ * pagecache as well so that corresponding buffers get properly unmapped.
+ */
+static inline void ext4_truncate_failed_write(struct inode *inode)
+{
+ truncate_inode_pages(inode->i_mapping, inode->i_size);
+ ext4_truncate(inode);
+}
+
+/*
+ * Work out how many blocks we need to proceed with the next chunk of a
+ * truncate transaction.
+ */
+static inline unsigned long ext4_blocks_for_truncate(struct inode *inode)
+{
+ ext4_lblk_t needed;
+
+ needed = inode->i_blocks >> (inode->i_sb->s_blocksize_bits - 9);
+
+ /* Give ourselves just enough room to cope with inodes in which
+ * i_blocks is corrupt: we've seen disk corruptions in the past
+ * which resulted in random data in an inode which looked enough
+ * like a regular file for ext4 to try to delete it. Things
+ * will go a bit crazy if that happens, but at least we should
+ * try not to panic the whole kernel. */
+ if (needed < 2)
+ needed = 2;
+
+ /* But we need to bound the transaction so we don't overflow the
+ * journal. */
+ if (needed > EXT4_MAX_TRANS_DATA)
+ needed = EXT4_MAX_TRANS_DATA;
+
+ return EXT4_DATA_TRANS_BLOCKS(inode->i_sb) + needed;
+}
+
diff --git a/fs/fat/dir.c b/fs/fat/dir.c
index 4ad6473..5efbd5d 100644
--- a/fs/fat/dir.c
+++ b/fs/fat/dir.c
@@ -1231,7 +1231,7 @@
struct super_block *sb = dir->i_sb;
struct msdos_sb_info *sbi = MSDOS_SB(sb);
struct buffer_head *bh, *prev, *bhs[3]; /* 32*slots (672bytes) */
- struct msdos_dir_entry *de;
+ struct msdos_dir_entry *uninitialized_var(de);
int err, free_slots, i, nr_bhs;
loff_t pos, i_pos;
diff --git a/fs/fat/inode.c b/fs/fat/inode.c
index 5942fec..1726d73 100644
--- a/fs/fat/inode.c
+++ b/fs/fat/inode.c
@@ -1188,9 +1188,9 @@
out:
/* UTF-8 doesn't provide FAT semantics */
if (!strcmp(opts->iocharset, "utf8")) {
- fat_msg(sb, KERN_ERR, "utf8 is not a recommended IO charset"
+ fat_msg(sb, KERN_WARNING, "utf8 is not a recommended IO charset"
" for FAT filesystems, filesystem will be "
- "case sensitive!\n");
+ "case sensitive!");
}
/* If user doesn't specify allow_utime, it's initialized from dmask. */
@@ -1367,6 +1367,7 @@
sbi->free_clusters = -1; /* Don't know yet */
sbi->free_clus_valid = 0;
sbi->prev_free = FAT_START_ENT;
+ sb->s_maxbytes = 0xffffffff;
if (!sbi->fat_length && b->fat32_length) {
struct fat_boot_fsinfo *fsinfo;
@@ -1377,8 +1378,6 @@
sbi->fat_length = le32_to_cpu(b->fat32_length);
sbi->root_cluster = le32_to_cpu(b->root_cluster);
- sb->s_maxbytes = 0xffffffff;
-
/* MC - if info_sector is 0, don't multiply by 0 */
sbi->fsinfo_sector = le16_to_cpu(b->info_sector);
if (sbi->fsinfo_sector == 0)
diff --git a/fs/generic_acl.c b/fs/generic_acl.c
index d5e33a0..d0dddac 100644
--- a/fs/generic_acl.c
+++ b/fs/generic_acl.c
@@ -82,18 +82,14 @@
return PTR_ERR(acl);
}
if (acl) {
- mode_t mode;
-
error = posix_acl_valid(acl);
if (error)
goto failed;
switch (type) {
case ACL_TYPE_ACCESS:
- mode = inode->i_mode;
- error = posix_acl_equiv_mode(acl, &mode);
+ error = posix_acl_equiv_mode(acl, &inode->i_mode);
if (error < 0)
goto failed;
- inode->i_mode = mode;
inode->i_ctime = CURRENT_TIME;
if (error == 0) {
posix_acl_release(acl);
@@ -125,21 +121,20 @@
generic_acl_init(struct inode *inode, struct inode *dir)
{
struct posix_acl *acl = NULL;
- mode_t mode = inode->i_mode;
int error;
- inode->i_mode = mode & ~current_umask();
if (!S_ISLNK(inode->i_mode))
acl = get_cached_acl(dir, ACL_TYPE_DEFAULT);
if (acl) {
if (S_ISDIR(inode->i_mode))
set_cached_acl(inode, ACL_TYPE_DEFAULT, acl);
- error = posix_acl_create(&acl, GFP_KERNEL, &mode);
+ error = posix_acl_create(&acl, GFP_KERNEL, &inode->i_mode);
if (error < 0)
return error;
- inode->i_mode = mode;
if (error > 0)
set_cached_acl(inode, ACL_TYPE_ACCESS, acl);
+ } else {
+ inode->i_mode &= ~current_umask();
}
error = 0;
diff --git a/fs/gfs2/acl.c b/fs/gfs2/acl.c
index 884c9af..34501b6 100644
--- a/fs/gfs2/acl.c
+++ b/fs/gfs2/acl.c
@@ -72,7 +72,7 @@
return gfs2_acl_get(GFS2_I(inode), type);
}
-static int gfs2_set_mode(struct inode *inode, mode_t mode)
+static int gfs2_set_mode(struct inode *inode, umode_t mode)
{
int error = 0;
@@ -117,7 +117,7 @@
{
struct gfs2_sbd *sdp = GFS2_SB(&dip->i_inode);
struct posix_acl *acl;
- mode_t mode = inode->i_mode;
+ umode_t mode = inode->i_mode;
int error = 0;
if (!sdp->sd_args.ar_posix_acl)
@@ -276,7 +276,7 @@
goto out_release;
if (type == ACL_TYPE_ACCESS) {
- mode_t mode = inode->i_mode;
+ umode_t mode = inode->i_mode;
error = posix_acl_equiv_mode(acl, &mode);
if (error <= 0) {
diff --git a/fs/hppfs/hppfs.c b/fs/hppfs/hppfs.c
index 8635be5..970ea98 100644
--- a/fs/hppfs/hppfs.c
+++ b/fs/hppfs/hppfs.c
@@ -16,6 +16,7 @@
#include <linux/statfs.h>
#include <linux/types.h>
#include <linux/pid_namespace.h>
+#include <linux/namei.h>
#include <asm/uaccess.h>
#include "os.h"
diff --git a/fs/inode.c b/fs/inode.c
index d0c72ff..73920d5 100644
--- a/fs/inode.c
+++ b/fs/inode.c
@@ -143,6 +143,7 @@
inode->i_op = &empty_iops;
inode->i_fop = &empty_fops;
inode->i_nlink = 1;
+ inode->i_opflags = 0;
inode->i_uid = 0;
inode->i_gid = 0;
atomic_set(&inode->i_writecount, 0);
@@ -399,12 +400,12 @@
EXPORT_SYMBOL(__insert_inode_hash);
/**
- * remove_inode_hash - remove an inode from the hash
+ * __remove_inode_hash - remove an inode from the hash
* @inode: inode to unhash
*
* Remove an inode from the superblock.
*/
-void remove_inode_hash(struct inode *inode)
+void __remove_inode_hash(struct inode *inode)
{
spin_lock(&inode_hash_lock);
spin_lock(&inode->i_lock);
@@ -412,7 +413,7 @@
spin_unlock(&inode->i_lock);
spin_unlock(&inode_hash_lock);
}
-EXPORT_SYMBOL(remove_inode_hash);
+EXPORT_SYMBOL(__remove_inode_hash);
void end_writeback(struct inode *inode)
{
@@ -454,7 +455,9 @@
BUG_ON(!(inode->i_state & I_FREEING));
BUG_ON(!list_empty(&inode->i_lru));
- inode_wb_list_del(inode);
+ if (!list_empty(&inode->i_wb_list))
+ inode_wb_list_del(inode);
+
inode_sb_list_del(inode);
if (op->evict_inode) {
@@ -1328,7 +1331,8 @@
}
inode->i_state |= I_FREEING;
- inode_lru_list_del(inode);
+ if (!list_empty(&inode->i_lru))
+ inode_lru_list_del(inode);
spin_unlock(&inode->i_lock);
evict(inode);
diff --git a/fs/jbd2/checkpoint.c b/fs/jbd2/checkpoint.c
index 2c62c5a..16a698b 100644
--- a/fs/jbd2/checkpoint.c
+++ b/fs/jbd2/checkpoint.c
@@ -257,9 +257,12 @@
__flush_batch(journal_t *journal, int *batch_count)
{
int i;
+ struct blk_plug plug;
+ blk_start_plug(&plug);
for (i = 0; i < *batch_count; i++)
- write_dirty_buffer(journal->j_chkpt_bhs[i], WRITE);
+ write_dirty_buffer(journal->j_chkpt_bhs[i], WRITE_SYNC);
+ blk_finish_plug(&plug);
for (i = 0; i < *batch_count; i++) {
struct buffer_head *bh = journal->j_chkpt_bhs[i];
diff --git a/fs/jbd2/journal.c b/fs/jbd2/journal.c
index 0dfa5b5..f24df13 100644
--- a/fs/jbd2/journal.c
+++ b/fs/jbd2/journal.c
@@ -2390,73 +2390,6 @@
jbd2_journal_destroy_caches();
}
-/*
- * jbd2_dev_to_name is a utility function used by the jbd2 and ext4
- * tracing infrastructure to map a dev_t to a device name.
- *
- * The caller should use rcu_read_lock() in order to make sure the
- * device name stays valid until its done with it. We use
- * rcu_read_lock() as well to make sure we're safe in case the caller
- * gets sloppy, and because rcu_read_lock() is cheap and can be safely
- * nested.
- */
-struct devname_cache {
- struct rcu_head rcu;
- dev_t device;
- char devname[BDEVNAME_SIZE];
-};
-#define CACHE_SIZE_BITS 6
-static struct devname_cache *devcache[1 << CACHE_SIZE_BITS];
-static DEFINE_SPINLOCK(devname_cache_lock);
-
-static void free_devcache(struct rcu_head *rcu)
-{
- kfree(rcu);
-}
-
-const char *jbd2_dev_to_name(dev_t device)
-{
- int i = hash_32(device, CACHE_SIZE_BITS);
- char *ret;
- struct block_device *bd;
- static struct devname_cache *new_dev;
-
- rcu_read_lock();
- if (devcache[i] && devcache[i]->device == device) {
- ret = devcache[i]->devname;
- rcu_read_unlock();
- return ret;
- }
- rcu_read_unlock();
-
- new_dev = kmalloc(sizeof(struct devname_cache), GFP_KERNEL);
- if (!new_dev)
- return "NODEV-ALLOCFAILURE"; /* Something non-NULL */
- bd = bdget(device);
- spin_lock(&devname_cache_lock);
- if (devcache[i]) {
- if (devcache[i]->device == device) {
- kfree(new_dev);
- bdput(bd);
- ret = devcache[i]->devname;
- spin_unlock(&devname_cache_lock);
- return ret;
- }
- call_rcu(&devcache[i]->rcu, free_devcache);
- }
- devcache[i] = new_dev;
- devcache[i]->device = device;
- if (bd) {
- bdevname(bd, devcache[i]->devname);
- bdput(bd);
- } else
- __bdevname(device, devcache[i]->devname);
- ret = devcache[i]->devname;
- spin_unlock(&devname_cache_lock);
- return ret;
-}
-EXPORT_SYMBOL(jbd2_dev_to_name);
-
MODULE_LICENSE("GPL");
module_init(journal_init);
module_exit(journal_exit);
diff --git a/fs/jffs2/acl.c b/fs/jffs2/acl.c
index 27c511a..926d020 100644
--- a/fs/jffs2/acl.c
+++ b/fs/jffs2/acl.c
@@ -227,7 +227,7 @@
case ACL_TYPE_ACCESS:
xprefix = JFFS2_XPREFIX_ACL_ACCESS;
if (acl) {
- mode_t mode = inode->i_mode;
+ umode_t mode = inode->i_mode;
rc = posix_acl_equiv_mode(acl, &mode);
if (rc < 0)
return rc;
@@ -259,7 +259,7 @@
return rc;
}
-int jffs2_init_acl_pre(struct inode *dir_i, struct inode *inode, mode_t *i_mode)
+int jffs2_init_acl_pre(struct inode *dir_i, struct inode *inode, umode_t *i_mode)
{
struct posix_acl *acl;
int rc;
diff --git a/fs/jffs2/acl.h b/fs/jffs2/acl.h
index b3421c7..9b47724 100644
--- a/fs/jffs2/acl.h
+++ b/fs/jffs2/acl.h
@@ -28,7 +28,7 @@
struct posix_acl *jffs2_get_acl(struct inode *inode, int type);
extern int jffs2_acl_chmod(struct inode *);
-extern int jffs2_init_acl_pre(struct inode *, struct inode *, mode_t *);
+extern int jffs2_init_acl_pre(struct inode *, struct inode *, umode_t *);
extern int jffs2_init_acl_post(struct inode *);
extern const struct xattr_handler jffs2_acl_access_xattr_handler;
diff --git a/fs/jffs2/fs.c b/fs/jffs2/fs.c
index b81b35d..bbcb975 100644
--- a/fs/jffs2/fs.c
+++ b/fs/jffs2/fs.c
@@ -406,7 +406,7 @@
/* jffs2_new_inode: allocate a new inode and inocache, add it to the hash,
fill in the raw_inode while you're at it. */
-struct inode *jffs2_new_inode (struct inode *dir_i, mode_t mode, struct jffs2_raw_inode *ri)
+struct inode *jffs2_new_inode (struct inode *dir_i, umode_t mode, struct jffs2_raw_inode *ri)
{
struct inode *inode;
struct super_block *sb = dir_i->i_sb;
diff --git a/fs/jffs2/os-linux.h b/fs/jffs2/os-linux.h
index 526979c..6c1755c 100644
--- a/fs/jffs2/os-linux.h
+++ b/fs/jffs2/os-linux.h
@@ -173,7 +173,7 @@
struct inode *jffs2_iget(struct super_block *, unsigned long);
void jffs2_evict_inode (struct inode *);
void jffs2_dirty_inode(struct inode *inode, int flags);
-struct inode *jffs2_new_inode (struct inode *dir_i, mode_t mode,
+struct inode *jffs2_new_inode (struct inode *dir_i, umode_t mode,
struct jffs2_raw_inode *ri);
int jffs2_statfs (struct dentry *, struct kstatfs *);
int jffs2_remount_fs (struct super_block *, int *, char *);
diff --git a/fs/jfs/acl.c b/fs/jfs/acl.c
index b3a32ca..45559dc 100644
--- a/fs/jfs/acl.c
+++ b/fs/jfs/acl.c
@@ -127,16 +127,14 @@
return PTR_ERR(acl);
if (acl) {
- mode_t mode = inode->i_mode;
if (S_ISDIR(inode->i_mode)) {
rc = jfs_set_acl(tid, inode, ACL_TYPE_DEFAULT, acl);
if (rc)
goto cleanup;
}
- rc = posix_acl_create(&acl, GFP_KERNEL, &mode);
+ rc = posix_acl_create(&acl, GFP_KERNEL, &inode->i_mode);
if (rc < 0)
goto cleanup; /* posix_acl_release(NULL) is no-op */
- inode->i_mode = mode;
if (rc > 0)
rc = jfs_set_acl(tid, inode, ACL_TYPE_ACCESS, acl);
cleanup:
diff --git a/fs/jfs/jfs_umount.c b/fs/jfs/jfs_umount.c
index adcf92d..7971f37 100644
--- a/fs/jfs/jfs_umount.c
+++ b/fs/jfs/jfs_umount.c
@@ -68,7 +68,7 @@
/*
* Wait for outstanding transactions to be written to log:
*/
- jfs_flush_journal(log, 1);
+ jfs_flush_journal(log, 2);
/*
* close fileset inode allocation map (aka fileset inode)
@@ -146,7 +146,7 @@
*
* remove file system from log active file system list.
*/
- jfs_flush_journal(log, 1);
+ jfs_flush_journal(log, 2);
/*
* Make sure all metadata makes it to disk
diff --git a/fs/jfs/xattr.c b/fs/jfs/xattr.c
index 24838f1..e87fede 100644
--- a/fs/jfs/xattr.c
+++ b/fs/jfs/xattr.c
@@ -693,8 +693,7 @@
return rc;
}
if (acl) {
- mode_t mode = inode->i_mode;
- rc = posix_acl_equiv_mode(acl, &mode);
+ rc = posix_acl_equiv_mode(acl, &inode->i_mode);
posix_acl_release(acl);
if (rc < 0) {
printk(KERN_ERR
@@ -702,7 +701,6 @@
rc);
return rc;
}
- inode->i_mode = mode;
mark_inode_dirty(inode);
}
/*
diff --git a/fs/namei.c b/fs/namei.c
index f8c69d3..2826db3 100644
--- a/fs/namei.c
+++ b/fs/namei.c
@@ -179,19 +179,14 @@
#ifdef CONFIG_FS_POSIX_ACL
struct posix_acl *acl;
- /*
- * Under RCU walk, we cannot even do a "get_cached_acl()",
- * because that involves locking and getting a refcount on
- * a cached ACL.
- *
- * So the only case we handle during RCU walking is the
- * case of a cached "no ACL at all", which needs no locks
- * or refcounts.
- */
if (mask & MAY_NOT_BLOCK) {
- if (negative_cached_acl(inode, ACL_TYPE_ACCESS))
+ acl = get_cached_acl_rcu(inode, ACL_TYPE_ACCESS);
+ if (!acl)
return -EAGAIN;
- return -ECHILD;
+ /* no ->get_acl() calls in RCU mode... */
+ if (acl == ACL_NOT_CACHED)
+ return -ECHILD;
+ return posix_acl_permission(inode, acl, mask & ~MAY_NOT_BLOCK);
}
acl = get_cached_acl(inode, ACL_TYPE_ACCESS);
@@ -313,6 +308,26 @@
return -EACCES;
}
+/*
+ * We _really_ want to just do "generic_permission()" without
+ * even looking at the inode->i_op values. So we keep a cache
+ * flag in inode->i_opflags, that says "this has not special
+ * permission function, use the fast case".
+ */
+static inline int do_inode_permission(struct inode *inode, int mask)
+{
+ if (unlikely(!(inode->i_opflags & IOP_FASTPERM))) {
+ if (likely(inode->i_op->permission))
+ return inode->i_op->permission(inode, mask);
+
+ /* This gets set once for the inode lifetime */
+ spin_lock(&inode->i_lock);
+ inode->i_opflags |= IOP_FASTPERM;
+ spin_unlock(&inode->i_lock);
+ }
+ return generic_permission(inode, mask);
+}
+
/**
* inode_permission - check for access rights to a given inode
* @inode: inode to check permission on
@@ -327,7 +342,7 @@
{
int retval;
- if (mask & MAY_WRITE) {
+ if (unlikely(mask & MAY_WRITE)) {
umode_t mode = inode->i_mode;
/*
@@ -344,11 +359,7 @@
return -EACCES;
}
- if (inode->i_op->permission)
- retval = inode->i_op->permission(inode, mask);
- else
- retval = generic_permission(inode, mask);
-
+ retval = do_inode_permission(inode, mask);
if (retval)
return retval;
@@ -716,19 +727,25 @@
if ((flags & LOOKUP_NO_AUTOMOUNT) && !(flags & LOOKUP_PARENT))
return -EISDIR; /* we actually want to stop here */
- /* We want to mount if someone is trying to open/create a file of any
- * type under the mountpoint, wants to traverse through the mountpoint
- * or wants to open the mounted directory.
- *
+ /*
* We don't want to mount if someone's just doing a stat and they've
* set AT_SYMLINK_NOFOLLOW - unless they're stat'ing a directory and
* appended a '/' to the name.
*/
- if (!(flags & LOOKUP_FOLLOW) &&
- !(flags & (LOOKUP_PARENT | LOOKUP_DIRECTORY |
- LOOKUP_OPEN | LOOKUP_CREATE)))
- return -EISDIR;
-
+ if (!(flags & LOOKUP_FOLLOW)) {
+ /* We do, however, want to mount if someone wants to open or
+ * create a file of any type under the mountpoint, wants to
+ * traverse through the mountpoint or wants to open the mounted
+ * directory.
+ * Also, autofs may mark negative dentries as being automount
+ * points. These will need the attentions of the daemon to
+ * instantiate them before they can be used.
+ */
+ if (!(flags & (LOOKUP_PARENT | LOOKUP_DIRECTORY |
+ LOOKUP_OPEN | LOOKUP_CREATE)) &&
+ path->dentry->d_inode)
+ return -EISDIR;
+ }
current->total_link_count++;
if (current->total_link_count >= 40)
return -ELOOP;
@@ -1244,6 +1261,26 @@
}
}
+/*
+ * Do we need to follow links? We _really_ want to be able
+ * to do this check without having to look at inode->i_op,
+ * so we keep a cache of "no, this doesn't need follow_link"
+ * for the common case.
+ */
+static inline int should_follow_link(struct inode *inode, int follow)
+{
+ if (unlikely(!(inode->i_opflags & IOP_NOFOLLOW))) {
+ if (likely(inode->i_op->follow_link))
+ return follow;
+
+ /* This gets set once for the inode lifetime */
+ spin_lock(&inode->i_lock);
+ inode->i_opflags |= IOP_NOFOLLOW;
+ spin_unlock(&inode->i_lock);
+ }
+ return 0;
+}
+
static inline int walk_component(struct nameidata *nd, struct path *path,
struct qstr *name, int type, int follow)
{
@@ -1266,7 +1303,7 @@
terminate_walk(nd);
return -ENOENT;
}
- if (unlikely(inode->i_op->follow_link) && follow) {
+ if (should_follow_link(inode, follow)) {
if (nd->flags & LOOKUP_RCU) {
if (unlikely(unlazy_walk(nd, path->dentry))) {
terminate_walk(nd);
@@ -1319,6 +1356,26 @@
}
/*
+ * We really don't want to look at inode->i_op->lookup
+ * when we don't have to. So we keep a cache bit in
+ * the inode ->i_opflags field that says "yes, we can
+ * do lookup on this inode".
+ */
+static inline int can_lookup(struct inode *inode)
+{
+ if (likely(inode->i_opflags & IOP_LOOKUP))
+ return 1;
+ if (likely(!inode->i_op->lookup))
+ return 0;
+
+ /* We do this once for the lifetime of the inode */
+ spin_lock(&inode->i_lock);
+ inode->i_opflags |= IOP_LOOKUP;
+ spin_unlock(&inode->i_lock);
+ return 1;
+}
+
+/*
* Name resolution.
* This is the basic name resolution function, turning a pathname into
* the final dentry. We expect 'base' to be positive and a directory.
@@ -1397,10 +1454,10 @@
if (err)
return err;
}
+ if (can_lookup(nd->inode))
+ continue;
err = -ENOTDIR;
- if (!nd->inode->i_op->lookup)
- break;
- continue;
+ break;
/* here ends the main loop */
last_component:
diff --git a/fs/nfs/Kconfig b/fs/nfs/Kconfig
index be02077..dbcd821 100644
--- a/fs/nfs/Kconfig
+++ b/fs/nfs/Kconfig
@@ -79,12 +79,9 @@
depends on NFS_FS && NFS_V4 && EXPERIMENTAL
select SUNRPC_BACKCHANNEL
select PNFS_FILE_LAYOUT
- select PNFS_BLOCK
- select MD
- select BLK_DEV_DM
help
This option enables support for minor version 1 of the NFSv4 protocol
- (RFC 5661 and RFC 5663) in the kernel's NFS client.
+ (RFC 5661) in the kernel's NFS client.
If unsure, say N.
@@ -93,16 +90,13 @@
config PNFS_BLOCK
tristate
+ depends on NFS_FS && NFS_V4_1 && BLK_DEV_DM
+ default m
config PNFS_OBJLAYOUT
- tristate "Provide support for the pNFS Objects Layout Driver for NFSv4.1 pNFS (EXPERIMENTAL)"
+ tristate
depends on NFS_FS && NFS_V4_1 && SCSI_OSD_ULD
- help
- Say M here if you want your pNFS client to support the Objects Layout Driver.
- Requires the SCSI osd initiator library (SCSI_OSD_INITIATOR) and
- upper level driver (SCSI_OSD_ULD).
-
- If unsure, say N.
+ default m
config ROOT_NFS
bool "Root file system on NFS"
diff --git a/fs/nfs/blocklayout/blocklayout.c b/fs/nfs/blocklayout/blocklayout.c
index e56564d..9561c8f 100644
--- a/fs/nfs/blocklayout/blocklayout.c
+++ b/fs/nfs/blocklayout/blocklayout.c
@@ -36,6 +36,7 @@
#include <linux/namei.h>
#include <linux/bio.h> /* struct bio */
#include <linux/buffer_head.h> /* various write calls */
+#include <linux/prefetch.h>
#include "blocklayout.h"
diff --git a/fs/nfs/callback.h b/fs/nfs/callback.h
index b257383..07df5f1 100644
--- a/fs/nfs/callback.h
+++ b/fs/nfs/callback.h
@@ -38,6 +38,7 @@
struct cb_process_state {
__be32 drc_status;
struct nfs_client *clp;
+ int slotid;
};
struct cb_compound_hdr_arg {
@@ -166,7 +167,6 @@
void *dummy, struct cb_process_state *cps);
extern void nfs4_check_drain_bc_complete(struct nfs4_session *ses);
-extern void nfs4_cb_take_slot(struct nfs_client *clp);
struct cb_devicenotifyitem {
uint32_t cbd_notify_type;
diff --git a/fs/nfs/callback_proc.c b/fs/nfs/callback_proc.c
index 74780f9..43926ad 100644
--- a/fs/nfs/callback_proc.c
+++ b/fs/nfs/callback_proc.c
@@ -348,7 +348,7 @@
/* Normal */
if (likely(args->csa_sequenceid == slot->seq_nr + 1)) {
slot->seq_nr++;
- return htonl(NFS4_OK);
+ goto out_ok;
}
/* Replay */
@@ -367,11 +367,14 @@
/* Wraparound */
if (args->csa_sequenceid == 1 && (slot->seq_nr + 1) == 0) {
slot->seq_nr = 1;
- return htonl(NFS4_OK);
+ goto out_ok;
}
/* Misordered request */
return htonl(NFS4ERR_SEQ_MISORDERED);
+out_ok:
+ tbl->highest_used_slotid = args->csa_slotid;
+ return htonl(NFS4_OK);
}
/*
@@ -433,26 +436,37 @@
struct cb_sequenceres *res,
struct cb_process_state *cps)
{
+ struct nfs4_slot_table *tbl;
struct nfs_client *clp;
int i;
__be32 status = htonl(NFS4ERR_BADSESSION);
- cps->clp = NULL;
-
clp = nfs4_find_client_sessionid(args->csa_addr, &args->csa_sessionid);
if (clp == NULL)
goto out;
+ tbl = &clp->cl_session->bc_slot_table;
+
+ spin_lock(&tbl->slot_tbl_lock);
/* state manager is resetting the session */
if (test_bit(NFS4_SESSION_DRAINING, &clp->cl_session->session_state)) {
- status = NFS4ERR_DELAY;
+ spin_unlock(&tbl->slot_tbl_lock);
+ status = htonl(NFS4ERR_DELAY);
+ /* Return NFS4ERR_BADSESSION if we're draining the session
+ * in order to reset it.
+ */
+ if (test_bit(NFS4CLNT_SESSION_RESET, &clp->cl_state))
+ status = htonl(NFS4ERR_BADSESSION);
goto out;
}
status = validate_seqid(&clp->cl_session->bc_slot_table, args);
+ spin_unlock(&tbl->slot_tbl_lock);
if (status)
goto out;
+ cps->slotid = args->csa_slotid;
+
/*
* Check for pending referring calls. If a match is found, a
* related callback was received before the response to the original
@@ -469,7 +483,6 @@
res->csr_slotid = args->csa_slotid;
res->csr_highestslotid = NFS41_BC_MAX_CALLBACKS - 1;
res->csr_target_highestslotid = NFS41_BC_MAX_CALLBACKS - 1;
- nfs4_cb_take_slot(clp);
out:
cps->clp = clp; /* put in nfs4_callback_compound */
diff --git a/fs/nfs/callback_xdr.c b/fs/nfs/callback_xdr.c
index c6c86a7..918ad64 100644
--- a/fs/nfs/callback_xdr.c
+++ b/fs/nfs/callback_xdr.c
@@ -754,26 +754,15 @@
* Let the state manager know callback processing done.
* A single slot, so highest used slotid is either 0 or -1
*/
- tbl->highest_used_slotid--;
+ tbl->highest_used_slotid = -1;
nfs4_check_drain_bc_complete(session);
spin_unlock(&tbl->slot_tbl_lock);
}
-static void nfs4_cb_free_slot(struct nfs_client *clp)
+static void nfs4_cb_free_slot(struct cb_process_state *cps)
{
- if (clp && clp->cl_session)
- nfs4_callback_free_slot(clp->cl_session);
-}
-
-/* A single slot, so highest used slotid is either 0 or -1 */
-void nfs4_cb_take_slot(struct nfs_client *clp)
-{
- struct nfs4_slot_table *tbl = &clp->cl_session->bc_slot_table;
-
- spin_lock(&tbl->slot_tbl_lock);
- tbl->highest_used_slotid++;
- BUG_ON(tbl->highest_used_slotid != 0);
- spin_unlock(&tbl->slot_tbl_lock);
+ if (cps->slotid != -1)
+ nfs4_callback_free_slot(cps->clp->cl_session);
}
#else /* CONFIG_NFS_V4_1 */
@@ -784,7 +773,7 @@
return htonl(NFS4ERR_MINOR_VERS_MISMATCH);
}
-static void nfs4_cb_free_slot(struct nfs_client *clp)
+static void nfs4_cb_free_slot(struct cb_process_state *cps)
{
}
#endif /* CONFIG_NFS_V4_1 */
@@ -866,6 +855,7 @@
struct cb_process_state cps = {
.drc_status = 0,
.clp = NULL,
+ .slotid = -1,
};
unsigned int nops = 0;
@@ -906,7 +896,7 @@
*hdr_res.status = status;
*hdr_res.nops = htonl(nops);
- nfs4_cb_free_slot(cps.clp);
+ nfs4_cb_free_slot(&cps);
nfs_put_client(cps.clp);
dprintk("%s: done, status = %u\n", __func__, ntohl(status));
return rpc_success;
diff --git a/fs/nfs/nfs3acl.c b/fs/nfs/nfs3acl.c
index e49e731..7ef2397 100644
--- a/fs/nfs/nfs3acl.c
+++ b/fs/nfs/nfs3acl.c
@@ -415,7 +415,7 @@
}
int nfs3_proc_set_default_acl(struct inode *dir, struct inode *inode,
- mode_t mode)
+ umode_t mode)
{
struct posix_acl *dfacl, *acl;
int error = 0;
diff --git a/fs/nfs/nfs3proc.c b/fs/nfs/nfs3proc.c
index 38053d8..85f1690 100644
--- a/fs/nfs/nfs3proc.c
+++ b/fs/nfs/nfs3proc.c
@@ -316,7 +316,7 @@
int flags, struct nfs_open_context *ctx)
{
struct nfs3_createdata *data;
- mode_t mode = sattr->ia_mode;
+ umode_t mode = sattr->ia_mode;
int status = -ENOMEM;
dprintk("NFS call create %s\n", dentry->d_name.name);
@@ -562,7 +562,7 @@
nfs3_proc_mkdir(struct inode *dir, struct dentry *dentry, struct iattr *sattr)
{
struct nfs3_createdata *data;
- int mode = sattr->ia_mode;
+ umode_t mode = sattr->ia_mode;
int status = -ENOMEM;
dprintk("NFS call mkdir %s\n", dentry->d_name.name);
@@ -681,7 +681,7 @@
dev_t rdev)
{
struct nfs3_createdata *data;
- mode_t mode = sattr->ia_mode;
+ umode_t mode = sattr->ia_mode;
int status = -ENOMEM;
dprintk("NFS call mknod %s %u:%u\n", dentry->d_name.name,
diff --git a/fs/nfs/objlayout/objio_osd.c b/fs/nfs/objlayout/objio_osd.c
index 9383ca7..d0cda12 100644
--- a/fs/nfs/objlayout/objio_osd.c
+++ b/fs/nfs/objlayout/objio_osd.c
@@ -479,7 +479,6 @@
for (i = 0; i < ios->numdevs; i++) {
struct osd_sense_info osi;
struct osd_request *or = ios->per_dev[i].or;
- unsigned dev;
int ret;
if (!or)
@@ -500,9 +499,8 @@
continue; /* we recovered */
}
- dev = ios->per_dev[i].dev;
- objlayout_io_set_result(&ios->ol_state, dev,
- &ios->layout->comps[dev].oc_object_id,
+ objlayout_io_set_result(&ios->ol_state, i,
+ &ios->layout->comps[i].oc_object_id,
osd_pri_2_pnfs_err(osi.osd_err_pri),
ios->per_dev[i].offset,
ios->per_dev[i].length,
@@ -589,22 +587,19 @@
}
static int _add_stripe_unit(struct objio_state *ios, unsigned *cur_pg,
- unsigned pgbase, struct _objio_per_comp *per_dev, int cur_len,
+ unsigned pgbase, struct _objio_per_comp *per_dev, int len,
gfp_t gfp_flags)
{
unsigned pg = *cur_pg;
+ int cur_len = len;
struct request_queue *q =
osd_request_queue(_io_od(ios, per_dev->dev));
- per_dev->length += cur_len;
-
if (per_dev->bio == NULL) {
- unsigned stripes = ios->layout->num_comps /
- ios->layout->mirrors_p1;
- unsigned pages_in_stripe = stripes *
+ unsigned pages_in_stripe = ios->layout->group_width *
(ios->layout->stripe_unit / PAGE_SIZE);
unsigned bio_size = (ios->ol_state.nr_pages + pages_in_stripe) /
- stripes;
+ ios->layout->group_width;
if (BIO_MAX_PAGES_KMALLOC < bio_size)
bio_size = BIO_MAX_PAGES_KMALLOC;
@@ -632,6 +627,7 @@
}
BUG_ON(cur_len);
+ per_dev->length += len;
*cur_pg = pg;
return 0;
}
@@ -650,7 +646,7 @@
int ret = 0;
while (length) {
- struct _objio_per_comp *per_dev = &ios->per_dev[dev];
+ struct _objio_per_comp *per_dev = &ios->per_dev[dev - first_dev];
unsigned cur_len, page_off = 0;
if (!per_dev->length) {
@@ -670,8 +666,8 @@
cur_len = stripe_unit;
}
- if (max_comp < dev)
- max_comp = dev;
+ if (max_comp < dev - first_dev)
+ max_comp = dev - first_dev;
} else {
cur_len = stripe_unit;
}
@@ -806,7 +802,7 @@
struct _objio_per_comp *per_dev = &ios->per_dev[cur_comp];
unsigned dev = per_dev->dev;
struct pnfs_osd_object_cred *cred =
- &ios->layout->comps[dev];
+ &ios->layout->comps[cur_comp];
struct osd_obj_id obj = {
.partition = cred->oc_object_id.oid_partition_id,
.id = cred->oc_object_id.oid_object_id,
@@ -904,7 +900,7 @@
for (; cur_comp < last_comp; ++cur_comp, ++dev) {
struct osd_request *or = NULL;
struct pnfs_osd_object_cred *cred =
- &ios->layout->comps[dev];
+ &ios->layout->comps[cur_comp];
struct osd_obj_id obj = {
.partition = cred->oc_object_id.oid_partition_id,
.id = cred->oc_object_id.oid_object_id,
diff --git a/fs/nfs/objlayout/pnfs_osd_xdr_cli.c b/fs/nfs/objlayout/pnfs_osd_xdr_cli.c
index 16fc758..b3918f7 100644
--- a/fs/nfs/objlayout/pnfs_osd_xdr_cli.c
+++ b/fs/nfs/objlayout/pnfs_osd_xdr_cli.c
@@ -170,6 +170,9 @@
p = _osd_xdr_decode_data_map(p, &layout->olo_map);
layout->olo_comps_index = be32_to_cpup(p++);
layout->olo_num_comps = be32_to_cpup(p++);
+ dprintk("%s: olo_comps_index=%d olo_num_comps=%d\n", __func__,
+ layout->olo_comps_index, layout->olo_num_comps);
+
iter->total_comps = layout->olo_num_comps;
return 0;
}
diff --git a/fs/nfs/pnfs.h b/fs/nfs/pnfs.h
index e0b5d80..01cbfd5 100644
--- a/fs/nfs/pnfs.h
+++ b/fs/nfs/pnfs.h
@@ -380,7 +380,7 @@
}
static inline void set_pnfs_layoutdriver(struct nfs_server *s,
- const struct nfs_fh *mntfh, u32 id);
+ const struct nfs_fh *mntfh, u32 id)
{
}
diff --git a/fs/ocfs2/acl.c b/fs/ocfs2/acl.c
index 783c58d..a721907 100644
--- a/fs/ocfs2/acl.c
+++ b/fs/ocfs2/acl.c
@@ -247,7 +247,7 @@
case ACL_TYPE_ACCESS:
name_index = OCFS2_XATTR_INDEX_POSIX_ACL_ACCESS;
if (acl) {
- mode_t mode = inode->i_mode;
+ umode_t mode = inode->i_mode;
ret = posix_acl_equiv_mode(acl, &mode);
if (ret < 0)
return ret;
@@ -351,7 +351,7 @@
struct ocfs2_super *osb = OCFS2_SB(inode->i_sb);
struct posix_acl *acl = NULL;
int ret = 0, ret2;
- mode_t mode;
+ umode_t mode;
if (!S_ISLNK(inode->i_mode)) {
if (osb->s_mount_opt & OCFS2_MOUNT_POSIX_ACL) {
diff --git a/fs/posix_acl.c b/fs/posix_acl.c
index d43729a7..10027b4 100644
--- a/fs/posix_acl.c
+++ b/fs/posix_acl.c
@@ -149,10 +149,10 @@
* file mode permission bits, or else 1. Returns -E... on error.
*/
int
-posix_acl_equiv_mode(const struct posix_acl *acl, mode_t *mode_p)
+posix_acl_equiv_mode(const struct posix_acl *acl, umode_t *mode_p)
{
const struct posix_acl_entry *pa, *pe;
- mode_t mode = 0;
+ umode_t mode = 0;
int not_equiv = 0;
FOREACH_ACL_ENTRY(pa, acl, pe) {
@@ -188,7 +188,7 @@
* Create an ACL representing the file mode permission bits of an inode.
*/
struct posix_acl *
-posix_acl_from_mode(mode_t mode, gfp_t flags)
+posix_acl_from_mode(umode_t mode, gfp_t flags)
{
struct posix_acl *acl = posix_acl_alloc(3, flags);
if (!acl)
@@ -279,11 +279,11 @@
* system calls. All permissions that are not granted by the acl are removed.
* The permissions in the acl are changed to reflect the mode_p parameter.
*/
-static int posix_acl_create_masq(struct posix_acl *acl, mode_t *mode_p)
+static int posix_acl_create_masq(struct posix_acl *acl, umode_t *mode_p)
{
struct posix_acl_entry *pa, *pe;
struct posix_acl_entry *group_obj = NULL, *mask_obj = NULL;
- mode_t mode = *mode_p;
+ umode_t mode = *mode_p;
int not_equiv = 0;
/* assert(atomic_read(acl->a_refcount) == 1); */
@@ -336,7 +336,7 @@
/*
* Modify the ACL for the chmod syscall.
*/
-static int posix_acl_chmod_masq(struct posix_acl *acl, mode_t mode)
+static int posix_acl_chmod_masq(struct posix_acl *acl, umode_t mode)
{
struct posix_acl_entry *group_obj = NULL, *mask_obj = NULL;
struct posix_acl_entry *pa, *pe;
@@ -382,7 +382,7 @@
}
int
-posix_acl_create(struct posix_acl **acl, gfp_t gfp, mode_t *mode_p)
+posix_acl_create(struct posix_acl **acl, gfp_t gfp, umode_t *mode_p)
{
struct posix_acl *clone = posix_acl_clone(*acl, gfp);
int err = -ENOMEM;
@@ -400,7 +400,7 @@
EXPORT_SYMBOL(posix_acl_create);
int
-posix_acl_chmod(struct posix_acl **acl, gfp_t gfp, mode_t mode)
+posix_acl_chmod(struct posix_acl **acl, gfp_t gfp, umode_t mode)
{
struct posix_acl *clone = posix_acl_clone(*acl, gfp);
int err = -ENOMEM;
diff --git a/fs/proc/base.c b/fs/proc/base.c
index 08e3ecc..5eb0206 100644
--- a/fs/proc/base.c
+++ b/fs/proc/base.c
@@ -1118,7 +1118,7 @@
* Warn that /proc/pid/oom_adj is deprecated, see
* Documentation/feature-removal-schedule.txt.
*/
- WARN_ONCE(1, "%s (%d): /proc/%d/oom_adj is deprecated, please use /proc/%d/oom_score_adj instead.\n",
+ printk_once(KERN_WARNING "%s (%d): /proc/%d/oom_adj is deprecated, please use /proc/%d/oom_score_adj instead.\n",
current->comm, task_pid_nr(current), task_pid_nr(task),
task_pid_nr(task));
task->signal->oom_adj = oom_adjust;
@@ -1919,6 +1919,14 @@
spin_lock(&files->file_lock);
file = fcheck_files(files, fd);
if (file) {
+ unsigned int f_flags;
+ struct fdtable *fdt;
+
+ fdt = files_fdtable(files);
+ f_flags = file->f_flags & ~O_CLOEXEC;
+ if (FD_ISSET(fd, fdt->close_on_exec))
+ f_flags |= O_CLOEXEC;
+
if (path) {
*path = file->f_path;
path_get(&file->f_path);
@@ -1928,7 +1936,7 @@
"pos:\t%lli\n"
"flags:\t0%o\n",
(long long) file->f_pos,
- file->f_flags);
+ f_flags);
spin_unlock(&files->file_lock);
put_files_struct(files);
return 0;
diff --git a/fs/pstore/inode.c b/fs/pstore/inode.c
index 977ed27..893b961 100644
--- a/fs/pstore/inode.c
+++ b/fs/pstore/inode.c
@@ -39,8 +39,9 @@
#define PSTORE_NAMELEN 64
struct pstore_private {
+ struct pstore_info *psi;
+ enum pstore_type_id type;
u64 id;
- int (*erase)(u64);
ssize_t size;
char data[];
};
@@ -73,7 +74,7 @@
{
struct pstore_private *p = dentry->d_inode->i_private;
- p->erase(p->id);
+ p->psi->erase(p->type, p->id, p->psi);
return simple_unlink(dir, dentry);
}
@@ -175,8 +176,8 @@
* Set the mtime & ctime to the date that this record was originally stored.
*/
int pstore_mkfile(enum pstore_type_id type, char *psname, u64 id,
- char *data, size_t size,
- struct timespec time, int (*erase)(u64))
+ char *data, size_t size, struct timespec time,
+ struct pstore_info *psi)
{
struct dentry *root = pstore_sb->s_root;
struct dentry *dentry;
@@ -192,8 +193,9 @@
private = kmalloc(sizeof *private + size, GFP_KERNEL);
if (!private)
goto fail_alloc;
+ private->type = type;
private->id = id;
- private->erase = erase;
+ private->psi = psi;
switch (type) {
case PSTORE_TYPE_DMESG:
diff --git a/fs/pstore/internal.h b/fs/pstore/internal.h
index 8c9f23e..611c1b3 100644
--- a/fs/pstore/internal.h
+++ b/fs/pstore/internal.h
@@ -2,5 +2,5 @@
extern void pstore_get_records(void);
extern int pstore_mkfile(enum pstore_type_id, char *psname, u64 id,
char *data, size_t size,
- struct timespec time, int (*erase)(u64));
+ struct timespec time, struct pstore_info *psi);
extern int pstore_is_mounted(void);
diff --git a/fs/pstore/platform.c b/fs/pstore/platform.c
index f2c3ff2..c5300ec 100644
--- a/fs/pstore/platform.c
+++ b/fs/pstore/platform.c
@@ -37,6 +37,8 @@
static DEFINE_SPINLOCK(pstore_lock);
static struct pstore_info *psinfo;
+static char *backend;
+
/* How much of the console log to snapshot */
static unsigned long kmsg_bytes = 10240;
@@ -67,7 +69,8 @@
unsigned long size, total = 0;
char *dst, *why;
u64 id;
- int hsize, part = 1;
+ int hsize;
+ unsigned int part = 1;
if (reason < ARRAY_SIZE(reason_str))
why = reason_str[reason];
@@ -78,7 +81,7 @@
oopscount++;
while (total < kmsg_bytes) {
dst = psinfo->buf;
- hsize = sprintf(dst, "%s#%d Part%d\n", why, oopscount, part++);
+ hsize = sprintf(dst, "%s#%d Part%d\n", why, oopscount, part);
size = psinfo->bufsize - hsize;
dst += hsize;
@@ -94,14 +97,16 @@
memcpy(dst, s1 + s1_start, l1_cpy);
memcpy(dst + l1_cpy, s2 + s2_start, l2_cpy);
- id = psinfo->write(PSTORE_TYPE_DMESG, hsize + l1_cpy + l2_cpy);
+ id = psinfo->write(PSTORE_TYPE_DMESG, part,
+ hsize + l1_cpy + l2_cpy, psinfo);
if (reason == KMSG_DUMP_OOPS && pstore_is_mounted())
pstore_mkfile(PSTORE_TYPE_DMESG, psinfo->name, id,
psinfo->buf, hsize + l1_cpy + l2_cpy,
- CURRENT_TIME, psinfo->erase);
+ CURRENT_TIME, psinfo);
l1 -= l1_cpy;
l2 -= l2_cpy;
total += l1_cpy + l2_cpy;
+ part++;
}
mutex_unlock(&psinfo->buf_mutex);
}
@@ -128,6 +133,12 @@
spin_unlock(&pstore_lock);
return -EBUSY;
}
+
+ if (backend && strcmp(backend, psi->name)) {
+ spin_unlock(&pstore_lock);
+ return -EINVAL;
+ }
+
psinfo = psi;
spin_unlock(&pstore_lock);
@@ -166,9 +177,9 @@
if (rc)
goto out;
- while ((size = psi->read(&id, &type, &time)) > 0) {
+ while ((size = psi->read(&id, &type, &time, psi)) > 0) {
if (pstore_mkfile(type, psi->name, id, psi->buf, (size_t)size,
- time, psi->erase))
+ time, psi))
failed++;
}
psi->close(psi);
@@ -196,12 +207,15 @@
mutex_lock(&psinfo->buf_mutex);
memcpy(psinfo->buf, buf, size);
- id = psinfo->write(type, size);
+ id = psinfo->write(type, 0, size, psinfo);
if (pstore_is_mounted())
pstore_mkfile(PSTORE_TYPE_DMESG, psinfo->name, id, psinfo->buf,
- size, CURRENT_TIME, psinfo->erase);
+ size, CURRENT_TIME, psinfo);
mutex_unlock(&psinfo->buf_mutex);
return 0;
}
EXPORT_SYMBOL_GPL(pstore_write);
+
+module_param(backend, charp, 0444);
+MODULE_PARM_DESC(backend, "Pstore backend to use");
diff --git a/fs/reiserfs/xattr_acl.c b/fs/reiserfs/xattr_acl.c
index 7362cf4..6da0396 100644
--- a/fs/reiserfs/xattr_acl.c
+++ b/fs/reiserfs/xattr_acl.c
@@ -272,12 +272,10 @@
case ACL_TYPE_ACCESS:
name = POSIX_ACL_XATTR_ACCESS;
if (acl) {
- mode_t mode = inode->i_mode;
- error = posix_acl_equiv_mode(acl, &mode);
+ error = posix_acl_equiv_mode(acl, &inode->i_mode);
if (error < 0)
return error;
else {
- inode->i_mode = mode;
if (error == 0)
acl = NULL;
}
@@ -354,8 +352,6 @@
return PTR_ERR(acl);
if (acl) {
- mode_t mode = inode->i_mode;
-
/* Copy the default ACL to the default ACL of a new directory */
if (S_ISDIR(inode->i_mode)) {
err = reiserfs_set_acl(th, inode, ACL_TYPE_DEFAULT,
@@ -366,12 +362,10 @@
/* Now we reconcile the new ACL and the mode,
potentially modifying both */
- err = posix_acl_create(&acl, GFP_NOFS, &mode);
+ err = posix_acl_create(&acl, GFP_NOFS, &inode->i_mode);
if (err < 0)
return err;
- inode->i_mode = mode;
-
/* If we need an ACL.. */
if (err > 0)
err = reiserfs_set_acl(th, inode, ACL_TYPE_ACCESS, acl);
diff --git a/fs/stack.c b/fs/stack.c
index 4a6f7f4..b4f2ab4 100644
--- a/fs/stack.c
+++ b/fs/stack.c
@@ -29,10 +29,7 @@
*
* We don't actually know what locking is used at the lower level;
* but if it's a filesystem that supports quotas, it will be using
- * i_lock as in inode_add_bytes(). tmpfs uses other locking, and
- * its 32-bit is (just) able to exceed 2TB i_size with the aid of
- * holes; but its i_blocks cannot carry into the upper long without
- * almost 2TB swap - let's ignore that case.
+ * i_lock as in inode_add_bytes().
*/
if (sizeof(i_blocks) > sizeof(long))
spin_lock(&src->i_lock);
diff --git a/fs/stat.c b/fs/stat.c
index 9610391..ba5316f 100644
--- a/fs/stat.c
+++ b/fs/stat.c
@@ -27,12 +27,12 @@
stat->uid = inode->i_uid;
stat->gid = inode->i_gid;
stat->rdev = inode->i_rdev;
+ stat->size = i_size_read(inode);
stat->atime = inode->i_atime;
stat->mtime = inode->i_mtime;
stat->ctime = inode->i_ctime;
- stat->size = i_size_read(inode);
- stat->blocks = inode->i_blocks;
stat->blksize = (1 << inode->i_blkbits);
+ stat->blocks = inode->i_blocks;
}
EXPORT_SYMBOL(generic_fillattr);
diff --git a/fs/xfs/linux-2.6/xfs_acl.c b/fs/xfs/linux-2.6/xfs_acl.c
index 44ce516..b6c4b37 100644
--- a/fs/xfs/linux-2.6/xfs_acl.c
+++ b/fs/xfs/linux-2.6/xfs_acl.c
@@ -221,7 +221,7 @@
}
static int
-xfs_set_mode(struct inode *inode, mode_t mode)
+xfs_set_mode(struct inode *inode, umode_t mode)
{
int error = 0;
@@ -267,7 +267,7 @@
int
xfs_inherit_acl(struct inode *inode, struct posix_acl *acl)
{
- mode_t mode = inode->i_mode;
+ umode_t mode = inode->i_mode;
int error = 0, inherit = 0;
if (S_ISDIR(inode->i_mode)) {
@@ -381,7 +381,7 @@
goto out_release;
if (type == ACL_TYPE_ACCESS) {
- mode_t mode = inode->i_mode;
+ umode_t mode = inode->i_mode;
error = posix_acl_equiv_mode(acl, &mode);
if (error <= 0) {
diff --git a/fs/xfs/linux-2.6/xfs_buf.c b/fs/xfs/linux-2.6/xfs_buf.c
index d1fe745..c57836d 100644
--- a/fs/xfs/linux-2.6/xfs_buf.c
+++ b/fs/xfs/linux-2.6/xfs_buf.c
@@ -596,7 +596,7 @@
bp->b_flags |= flags & (XBF_READ | XBF_ASYNC | XBF_READ_AHEAD);
status = xfs_buf_iorequest(bp);
- if (status || XFS_BUF_ISERROR(bp) || (flags & XBF_ASYNC))
+ if (status || bp->b_error || (flags & XBF_ASYNC))
return status;
return xfs_buf_iowait(bp);
}
@@ -679,7 +679,6 @@
/* set up the buffer for a read IO */
XFS_BUF_SET_ADDR(bp, daddr);
XFS_BUF_READ(bp);
- XFS_BUF_BUSY(bp);
xfsbdstrat(mp, bp);
error = xfs_buf_iowait(bp);
@@ -1069,7 +1068,7 @@
/*
* No need to wait until the buffer is unpinned, we aren't flushing it.
*/
- XFS_BUF_ERROR(bp, EIO);
+ xfs_buf_ioerror(bp, EIO);
/*
* We're calling xfs_buf_ioend, so delete XBF_DONE flag.
@@ -1094,7 +1093,7 @@
xfs_bioerror_relse(
struct xfs_buf *bp)
{
- int64_t fl = XFS_BUF_BFLAGS(bp);
+ int64_t fl = bp->b_flags;
/*
* No need to wait until the buffer is unpinned.
* We aren't flushing it.
@@ -1115,7 +1114,7 @@
* There's no reason to mark error for
* ASYNC buffers.
*/
- XFS_BUF_ERROR(bp, EIO);
+ xfs_buf_ioerror(bp, EIO);
XFS_BUF_FINISH_IOWAIT(bp);
} else {
xfs_buf_relse(bp);
@@ -1324,7 +1323,7 @@
struct page *page;
if (bp->b_flags & XBF_MAPPED)
- return XFS_BUF_PTR(bp) + offset;
+ return bp->b_addr + offset;
offset += bp->b_offset;
page = bp->b_pages[offset >> PAGE_SHIFT];
@@ -1484,7 +1483,7 @@
if (set_blocksize(btp->bt_bdev, sectorsize)) {
xfs_warn(btp->bt_mount,
"Cannot set_blocksize to %u on device %s\n",
- sectorsize, XFS_BUFTARG_NAME(btp));
+ sectorsize, xfs_buf_target_name(btp));
return EINVAL;
}
@@ -1681,7 +1680,7 @@
list_for_each_entry_safe(bp, n, dwq, b_list) {
ASSERT(bp->b_flags & XBF_DELWRI);
- if (!XFS_BUF_ISPINNED(bp) && xfs_buf_trylock(bp)) {
+ if (!xfs_buf_ispinned(bp) && xfs_buf_trylock(bp)) {
if (!force &&
time_before(jiffies, bp->b_queuetime + age)) {
xfs_buf_unlock(bp);
diff --git a/fs/xfs/linux-2.6/xfs_buf.h b/fs/xfs/linux-2.6/xfs_buf.h
index 6a83b46..620972b 100644
--- a/fs/xfs/linux-2.6/xfs_buf.h
+++ b/fs/xfs/linux-2.6/xfs_buf.h
@@ -228,11 +228,15 @@
extern int xfs_buf_init(void);
extern void xfs_buf_terminate(void);
-#define xfs_buf_target_name(target) \
- ({ char __b[BDEVNAME_SIZE]; bdevname((target)->bt_bdev, __b); __b; })
+static inline const char *
+xfs_buf_target_name(struct xfs_buftarg *target)
+{
+ static char __b[BDEVNAME_SIZE];
+
+ return bdevname(target->bt_bdev, __b);
+}
-#define XFS_BUF_BFLAGS(bp) ((bp)->b_flags)
#define XFS_BUF_ZEROFLAGS(bp) \
((bp)->b_flags &= ~(XBF_READ|XBF_WRITE|XBF_ASYNC|XBF_DELWRI| \
XBF_SYNCIO|XBF_FUA|XBF_FLUSH))
@@ -251,23 +255,14 @@
#define XFS_BUF_UNDELAYWRITE(bp) xfs_buf_delwri_dequeue(bp)
#define XFS_BUF_ISDELAYWRITE(bp) ((bp)->b_flags & XBF_DELWRI)
-#define XFS_BUF_ERROR(bp,no) xfs_buf_ioerror(bp,no)
-#define XFS_BUF_GETERROR(bp) xfs_buf_geterror(bp)
-#define XFS_BUF_ISERROR(bp) (xfs_buf_geterror(bp) ? 1 : 0)
-
#define XFS_BUF_DONE(bp) ((bp)->b_flags |= XBF_DONE)
#define XFS_BUF_UNDONE(bp) ((bp)->b_flags &= ~XBF_DONE)
#define XFS_BUF_ISDONE(bp) ((bp)->b_flags & XBF_DONE)
-#define XFS_BUF_BUSY(bp) do { } while (0)
-#define XFS_BUF_UNBUSY(bp) do { } while (0)
-#define XFS_BUF_ISBUSY(bp) (1)
-
#define XFS_BUF_ASYNC(bp) ((bp)->b_flags |= XBF_ASYNC)
#define XFS_BUF_UNASYNC(bp) ((bp)->b_flags &= ~XBF_ASYNC)
#define XFS_BUF_ISASYNC(bp) ((bp)->b_flags & XBF_ASYNC)
-#define XFS_BUF_HOLD(bp) xfs_buf_hold(bp)
#define XFS_BUF_READ(bp) ((bp)->b_flags |= XBF_READ)
#define XFS_BUF_UNREAD(bp) ((bp)->b_flags &= ~XBF_READ)
#define XFS_BUF_ISREAD(bp) ((bp)->b_flags & XBF_READ)
@@ -276,10 +271,6 @@
#define XFS_BUF_UNWRITE(bp) ((bp)->b_flags &= ~XBF_WRITE)
#define XFS_BUF_ISWRITE(bp) ((bp)->b_flags & XBF_WRITE)
-#define XFS_BUF_SET_START(bp) do { } while (0)
-
-#define XFS_BUF_PTR(bp) (xfs_caddr_t)((bp)->b_addr)
-#define XFS_BUF_SET_PTR(bp, val, cnt) xfs_buf_associate_memory(bp, val, cnt)
#define XFS_BUF_ADDR(bp) ((bp)->b_bn)
#define XFS_BUF_SET_ADDR(bp, bno) ((bp)->b_bn = (xfs_daddr_t)(bno))
#define XFS_BUF_OFFSET(bp) ((bp)->b_file_offset)
@@ -299,14 +290,13 @@
#define XFS_BUF_SET_VTYPE_REF(bp, type, ref) xfs_buf_set_ref(bp, ref)
#define XFS_BUF_SET_VTYPE(bp, type) do { } while (0)
-#define XFS_BUF_ISPINNED(bp) atomic_read(&((bp)->b_pin_count))
+static inline int xfs_buf_ispinned(struct xfs_buf *bp)
+{
+ return atomic_read(&bp->b_pin_count);
+}
#define XFS_BUF_FINISH_IOWAIT(bp) complete(&bp->b_iowait);
-#define XFS_BUF_SET_TARGET(bp, target) ((bp)->b_target = (target))
-#define XFS_BUF_TARGET(bp) ((bp)->b_target)
-#define XFS_BUFTARG_NAME(target) xfs_buf_target_name(target)
-
static inline void xfs_buf_relse(xfs_buf_t *bp)
{
xfs_buf_unlock(bp);
diff --git a/fs/xfs/linux-2.6/xfs_sync.c b/fs/xfs/linux-2.6/xfs_sync.c
index e4c938a..4604f90 100644
--- a/fs/xfs/linux-2.6/xfs_sync.c
+++ b/fs/xfs/linux-2.6/xfs_sync.c
@@ -332,7 +332,7 @@
* between there and here.
*/
bp = xfs_getsb(mp, 0);
- if (XFS_BUF_ISPINNED(bp))
+ if (xfs_buf_ispinned(bp))
xfs_log_force(mp, 0);
return xfs_bwrite(mp, bp);
diff --git a/fs/xfs/quota/xfs_dquot.c b/fs/xfs/quota/xfs_dquot.c
index 837f311..db62959 100644
--- a/fs/xfs/quota/xfs_dquot.c
+++ b/fs/xfs/quota/xfs_dquot.c
@@ -318,10 +318,9 @@
int curid, i;
ASSERT(tp);
- ASSERT(XFS_BUF_ISBUSY(bp));
ASSERT(xfs_buf_islocked(bp));
- d = (xfs_dqblk_t *)XFS_BUF_PTR(bp);
+ d = bp->b_addr;
/*
* ID of the first dquot in the block - id's are zero based.
@@ -403,7 +402,7 @@
dqp->q_blkno,
mp->m_quotainfo->qi_dqchunklen,
0);
- if (!bp || (error = XFS_BUF_GETERROR(bp)))
+ if (!bp || (error = xfs_buf_geterror(bp)))
goto error1;
/*
* Make a chunk of dquots out of this buffer and log
@@ -534,13 +533,12 @@
return XFS_ERROR(error);
}
- ASSERT(XFS_BUF_ISBUSY(bp));
ASSERT(xfs_buf_islocked(bp));
/*
* calculate the location of the dquot inside the buffer.
*/
- ddq = (struct xfs_disk_dquot *)(XFS_BUF_PTR(bp) + dqp->q_bufoffset);
+ ddq = bp->b_addr + dqp->q_bufoffset;
/*
* A simple sanity check in case we got a corrupted dquot...
@@ -553,7 +551,6 @@
xfs_trans_brelse(tp, bp);
return XFS_ERROR(EIO);
}
- XFS_BUF_BUSY(bp); /* We dirtied this */
}
*O_bpp = bp;
@@ -622,7 +619,6 @@
* this particular dquot was repaired. We still aren't afraid to
* brelse it because we have the changes incore.
*/
- ASSERT(XFS_BUF_ISBUSY(bp));
ASSERT(xfs_buf_islocked(bp));
xfs_trans_brelse(tp, bp);
@@ -1204,7 +1200,7 @@
/*
* Calculate the location of the dquot inside the buffer.
*/
- ddqp = (struct xfs_disk_dquot *)(XFS_BUF_PTR(bp) + dqp->q_bufoffset);
+ ddqp = bp->b_addr + dqp->q_bufoffset;
/*
* A simple sanity check in case we got a corrupted dquot..
@@ -1240,7 +1236,7 @@
* If the buffer is pinned then push on the log so we won't
* get stuck waiting in the write for too long.
*/
- if (XFS_BUF_ISPINNED(bp)) {
+ if (xfs_buf_ispinned(bp)) {
trace_xfs_dqflush_force(dqp);
xfs_log_force(mp, 0);
}
@@ -1447,7 +1443,7 @@
goto out_lock;
if (XFS_BUF_ISDELAYWRITE(bp)) {
- if (XFS_BUF_ISPINNED(bp))
+ if (xfs_buf_ispinned(bp))
xfs_log_force(mp, 0);
xfs_buf_delwri_promote(bp);
wake_up_process(bp->b_target->bt_task);
diff --git a/fs/xfs/quota/xfs_qm.c b/fs/xfs/quota/xfs_qm.c
index 46e54ad..9a0aa76 100644
--- a/fs/xfs/quota/xfs_qm.c
+++ b/fs/xfs/quota/xfs_qm.c
@@ -1240,7 +1240,7 @@
do_div(j, sizeof(xfs_dqblk_t));
ASSERT(mp->m_quotainfo->qi_dqperchunk == j);
#endif
- ddq = (xfs_disk_dquot_t *)XFS_BUF_PTR(bp);
+ ddq = bp->b_addr;
for (j = 0; j < mp->m_quotainfo->qi_dqperchunk; j++) {
/*
* Do a sanity check, and if needed, repair the dqblk. Don't
diff --git a/fs/xfs/xfs_ag.h b/fs/xfs/xfs_ag.h
index 6530769..4805f00 100644
--- a/fs/xfs/xfs_ag.h
+++ b/fs/xfs/xfs_ag.h
@@ -103,7 +103,7 @@
/* disk block (xfs_daddr_t) in the AG */
#define XFS_AGF_DADDR(mp) ((xfs_daddr_t)(1 << (mp)->m_sectbb_log))
#define XFS_AGF_BLOCK(mp) XFS_HDR_BLOCK(mp, XFS_AGF_DADDR(mp))
-#define XFS_BUF_TO_AGF(bp) ((xfs_agf_t *)XFS_BUF_PTR(bp))
+#define XFS_BUF_TO_AGF(bp) ((xfs_agf_t *)((bp)->b_addr))
extern int xfs_read_agf(struct xfs_mount *mp, struct xfs_trans *tp,
xfs_agnumber_t agno, int flags, struct xfs_buf **bpp);
@@ -156,7 +156,7 @@
/* disk block (xfs_daddr_t) in the AG */
#define XFS_AGI_DADDR(mp) ((xfs_daddr_t)(2 << (mp)->m_sectbb_log))
#define XFS_AGI_BLOCK(mp) XFS_HDR_BLOCK(mp, XFS_AGI_DADDR(mp))
-#define XFS_BUF_TO_AGI(bp) ((xfs_agi_t *)XFS_BUF_PTR(bp))
+#define XFS_BUF_TO_AGI(bp) ((xfs_agi_t *)((bp)->b_addr))
extern int xfs_read_agi(struct xfs_mount *mp, struct xfs_trans *tp,
xfs_agnumber_t agno, struct xfs_buf **bpp);
@@ -168,7 +168,7 @@
#define XFS_AGFL_DADDR(mp) ((xfs_daddr_t)(3 << (mp)->m_sectbb_log))
#define XFS_AGFL_BLOCK(mp) XFS_HDR_BLOCK(mp, XFS_AGFL_DADDR(mp))
#define XFS_AGFL_SIZE(mp) ((mp)->m_sb.sb_sectsize / sizeof(xfs_agblock_t))
-#define XFS_BUF_TO_AGFL(bp) ((xfs_agfl_t *)XFS_BUF_PTR(bp))
+#define XFS_BUF_TO_AGFL(bp) ((xfs_agfl_t *)((bp)->b_addr))
typedef struct xfs_agfl {
__be32 agfl_bno[1]; /* actually XFS_AGFL_SIZE(mp) */
diff --git a/fs/xfs/xfs_alloc.c b/fs/xfs/xfs_alloc.c
index 1e00b3e..bdd9cb5 100644
--- a/fs/xfs/xfs_alloc.c
+++ b/fs/xfs/xfs_alloc.c
@@ -451,8 +451,7 @@
XFS_FSS_TO_BB(mp, 1), 0, &bp);
if (error)
return error;
- ASSERT(bp);
- ASSERT(!XFS_BUF_GETERROR(bp));
+ ASSERT(!xfs_buf_geterror(bp));
XFS_BUF_SET_VTYPE_REF(bp, B_FS_AGFL, XFS_AGFL_REF);
*bpp = bp;
return 0;
@@ -2116,7 +2115,7 @@
if (!*bpp)
return 0;
- ASSERT(!XFS_BUF_GETERROR(*bpp));
+ ASSERT(!(*bpp)->b_error);
agf = XFS_BUF_TO_AGF(*bpp);
/*
@@ -2168,7 +2167,7 @@
return error;
if (!*bpp)
return 0;
- ASSERT(!XFS_BUF_GETERROR(*bpp));
+ ASSERT(!(*bpp)->b_error);
agf = XFS_BUF_TO_AGF(*bpp);
pag = xfs_perag_get(mp, agno);
diff --git a/fs/xfs/xfs_attr.c b/fs/xfs/xfs_attr.c
index cbae424..160bcdc 100644
--- a/fs/xfs/xfs_attr.c
+++ b/fs/xfs/xfs_attr.c
@@ -2121,8 +2121,7 @@
bp = xfs_buf_get(mp->m_ddev_targp, dblkno, blkcnt,
XBF_LOCK | XBF_DONT_BLOCK);
- ASSERT(bp);
- ASSERT(!XFS_BUF_GETERROR(bp));
+ ASSERT(!xfs_buf_geterror(bp));
tmp = (valuelen < XFS_BUF_SIZE(bp)) ? valuelen :
XFS_BUF_SIZE(bp);
diff --git a/fs/xfs/xfs_bmap.c b/fs/xfs/xfs_bmap.c
index ab3e5c6..452a291 100644
--- a/fs/xfs/xfs_bmap.c
+++ b/fs/xfs/xfs_bmap.c
@@ -3383,8 +3383,7 @@
ASSERT(args.len == 1);
*firstblock = args.fsbno;
bp = xfs_btree_get_bufl(args.mp, tp, args.fsbno, 0);
- memcpy((char *)XFS_BUF_PTR(bp), ifp->if_u1.if_data,
- ifp->if_bytes);
+ memcpy(bp->b_addr, ifp->if_u1.if_data, ifp->if_bytes);
xfs_trans_log_buf(tp, bp, 0, ifp->if_bytes - 1);
xfs_bmap_forkoff_reset(args.mp, ip, whichfork);
xfs_idata_realloc(ip, -ifp->if_bytes, whichfork);
diff --git a/fs/xfs/xfs_btree.c b/fs/xfs/xfs_btree.c
index cabf4b5..2b9fd38 100644
--- a/fs/xfs/xfs_btree.c
+++ b/fs/xfs/xfs_btree.c
@@ -275,8 +275,7 @@
return error;
}
new->bc_bufs[i] = bp;
- ASSERT(bp);
- ASSERT(!XFS_BUF_GETERROR(bp));
+ ASSERT(!xfs_buf_geterror(bp));
} else
new->bc_bufs[i] = NULL;
}
@@ -467,8 +466,7 @@
ASSERT(fsbno != NULLFSBLOCK);
d = XFS_FSB_TO_DADDR(mp, fsbno);
bp = xfs_trans_get_buf(tp, mp->m_ddev_targp, d, mp->m_bsize, lock);
- ASSERT(bp);
- ASSERT(!XFS_BUF_GETERROR(bp));
+ ASSERT(!xfs_buf_geterror(bp));
return bp;
}
@@ -491,8 +489,7 @@
ASSERT(agbno != NULLAGBLOCK);
d = XFS_AGB_TO_DADDR(mp, agno, agbno);
bp = xfs_trans_get_buf(tp, mp->m_ddev_targp, d, mp->m_bsize, lock);
- ASSERT(bp);
- ASSERT(!XFS_BUF_GETERROR(bp));
+ ASSERT(!xfs_buf_geterror(bp));
return bp;
}
@@ -632,7 +629,7 @@
mp->m_bsize, lock, &bp))) {
return error;
}
- ASSERT(!bp || !XFS_BUF_GETERROR(bp));
+ ASSERT(!xfs_buf_geterror(bp));
if (bp)
XFS_BUF_SET_VTYPE_REF(bp, B_FS_MAP, refval);
*bpp = bp;
@@ -973,8 +970,7 @@
*bpp = xfs_trans_get_buf(cur->bc_tp, mp->m_ddev_targp, d,
mp->m_bsize, flags);
- ASSERT(*bpp);
- ASSERT(!XFS_BUF_GETERROR(*bpp));
+ ASSERT(!xfs_buf_geterror(*bpp));
*block = XFS_BUF_TO_BLOCK(*bpp);
return 0;
@@ -1006,8 +1002,7 @@
if (error)
return error;
- ASSERT(*bpp != NULL);
- ASSERT(!XFS_BUF_GETERROR(*bpp));
+ ASSERT(!xfs_buf_geterror(*bpp));
xfs_btree_set_refs(cur, *bpp);
*block = XFS_BUF_TO_BLOCK(*bpp);
diff --git a/fs/xfs/xfs_btree.h b/fs/xfs/xfs_btree.h
index 8d05a6a..5b240de 100644
--- a/fs/xfs/xfs_btree.h
+++ b/fs/xfs/xfs_btree.h
@@ -262,7 +262,7 @@
/*
* Convert from buffer to btree block header.
*/
-#define XFS_BUF_TO_BLOCK(bp) ((struct xfs_btree_block *)XFS_BUF_PTR(bp))
+#define XFS_BUF_TO_BLOCK(bp) ((struct xfs_btree_block *)((bp)->b_addr))
/*
diff --git a/fs/xfs/xfs_buf_item.c b/fs/xfs/xfs_buf_item.c
index 8849291..cac2ecf 100644
--- a/fs/xfs/xfs_buf_item.c
+++ b/fs/xfs/xfs_buf_item.c
@@ -124,9 +124,9 @@
bp = bip->bli_buf;
ASSERT(XFS_BUF_COUNT(bp) > 0);
- ASSERT(XFS_BUF_PTR(bp) != NULL);
+ ASSERT(bp->b_addr != NULL);
orig = bip->bli_orig;
- buffer = XFS_BUF_PTR(bp);
+ buffer = bp->b_addr;
for (x = 0; x < XFS_BUF_COUNT(bp); x++) {
if (orig[x] != buffer[x] && !btst(bip->bli_logged, x)) {
xfs_emerg(bp->b_mount,
@@ -371,7 +371,6 @@
{
struct xfs_buf_log_item *bip = BUF_ITEM(lip);
- ASSERT(XFS_BUF_ISBUSY(bip->bli_buf));
ASSERT(atomic_read(&bip->bli_refcount) > 0);
ASSERT((bip->bli_flags & XFS_BLI_LOGGED) ||
(bip->bli_flags & XFS_BLI_STALE));
@@ -479,13 +478,13 @@
struct xfs_buf_log_item *bip = BUF_ITEM(lip);
struct xfs_buf *bp = bip->bli_buf;
- if (XFS_BUF_ISPINNED(bp))
+ if (xfs_buf_ispinned(bp))
return XFS_ITEM_PINNED;
if (!xfs_buf_trylock(bp))
return XFS_ITEM_LOCKED;
/* take a reference to the buffer. */
- XFS_BUF_HOLD(bp);
+ xfs_buf_hold(bp);
ASSERT(!(bip->bli_flags & XFS_BLI_STALE));
trace_xfs_buf_item_trylock(bip);
@@ -726,7 +725,7 @@
* to have logged.
*/
bip->bli_orig = (char *)kmem_alloc(XFS_BUF_COUNT(bp), KM_SLEEP);
- memcpy(bip->bli_orig, XFS_BUF_PTR(bp), XFS_BUF_COUNT(bp));
+ memcpy(bip->bli_orig, bp->b_addr, XFS_BUF_COUNT(bp));
bip->bli_logged = (char *)kmem_zalloc(XFS_BUF_COUNT(bp) / NBBY, KM_SLEEP);
#endif
@@ -895,7 +894,6 @@
{
xfs_log_item_t *head_lip;
- ASSERT(XFS_BUF_ISBUSY(bp));
ASSERT(xfs_buf_islocked(bp));
lip->li_cb = cb;
@@ -960,7 +958,7 @@
static ulong lasttime;
static xfs_buftarg_t *lasttarg;
- if (likely(!XFS_BUF_GETERROR(bp)))
+ if (likely(!xfs_buf_geterror(bp)))
goto do_callbacks;
/*
@@ -973,14 +971,14 @@
goto do_callbacks;
}
- if (XFS_BUF_TARGET(bp) != lasttarg ||
+ if (bp->b_target != lasttarg ||
time_after(jiffies, (lasttime + 5*HZ))) {
lasttime = jiffies;
xfs_alert(mp, "Device %s: metadata write error block 0x%llx",
- XFS_BUFTARG_NAME(XFS_BUF_TARGET(bp)),
+ xfs_buf_target_name(bp->b_target),
(__uint64_t)XFS_BUF_ADDR(bp));
}
- lasttarg = XFS_BUF_TARGET(bp);
+ lasttarg = bp->b_target;
/*
* If the write was asynchronous then no one will be looking for the
@@ -991,12 +989,11 @@
* around.
*/
if (XFS_BUF_ISASYNC(bp)) {
- XFS_BUF_ERROR(bp, 0); /* errno of 0 unsets the flag */
+ xfs_buf_ioerror(bp, 0); /* errno of 0 unsets the flag */
if (!XFS_BUF_ISSTALE(bp)) {
XFS_BUF_DELAYWRITE(bp);
XFS_BUF_DONE(bp);
- XFS_BUF_SET_START(bp);
}
ASSERT(bp->b_iodone != NULL);
trace_xfs_buf_item_iodone_async(bp, _RET_IP_);
@@ -1013,7 +1010,6 @@
XFS_BUF_UNDELAYWRITE(bp);
trace_xfs_buf_error_relse(bp, _RET_IP_);
- xfs_force_shutdown(mp, SHUTDOWN_META_IO_ERROR);
do_callbacks:
xfs_buf_do_callbacks(bp);
diff --git a/fs/xfs/xfs_da_btree.c b/fs/xfs/xfs_da_btree.c
index 5bfcb87..ee9d542 100644
--- a/fs/xfs/xfs_da_btree.c
+++ b/fs/xfs/xfs_da_btree.c
@@ -2050,7 +2050,7 @@
case 0:
bp = xfs_trans_get_buf(trans, mp->m_ddev_targp,
mappedbno, nmapped, 0);
- error = bp ? XFS_BUF_GETERROR(bp) : XFS_ERROR(EIO);
+ error = bp ? bp->b_error : XFS_ERROR(EIO);
break;
case 1:
case 2:
@@ -2268,7 +2268,7 @@
dabuf->nbuf = 1;
bp = bps[0];
dabuf->bbcount = (short)BTOBB(XFS_BUF_COUNT(bp));
- dabuf->data = XFS_BUF_PTR(bp);
+ dabuf->data = bp->b_addr;
dabuf->bps[0] = bp;
} else {
dabuf->nbuf = nbuf;
@@ -2279,7 +2279,7 @@
dabuf->data = kmem_alloc(BBTOB(dabuf->bbcount), KM_SLEEP);
for (i = off = 0; i < nbuf; i++, off += XFS_BUF_COUNT(bp)) {
bp = bps[i];
- memcpy((char *)dabuf->data + off, XFS_BUF_PTR(bp),
+ memcpy((char *)dabuf->data + off, bp->b_addr,
XFS_BUF_COUNT(bp));
}
}
@@ -2302,8 +2302,8 @@
for (i = off = 0; i < dabuf->nbuf;
i++, off += XFS_BUF_COUNT(bp)) {
bp = dabuf->bps[i];
- memcpy(XFS_BUF_PTR(bp), (char *)dabuf->data + off,
- XFS_BUF_COUNT(bp));
+ memcpy(bp->b_addr, dabuf->data + off,
+ XFS_BUF_COUNT(bp));
}
}
}
@@ -2340,7 +2340,7 @@
ASSERT(dabuf->nbuf && dabuf->data && dabuf->bbcount && dabuf->bps[0]);
if (dabuf->nbuf == 1) {
- ASSERT(dabuf->data == (void *)XFS_BUF_PTR(dabuf->bps[0]));
+ ASSERT(dabuf->data == dabuf->bps[0]->b_addr);
xfs_trans_log_buf(tp, dabuf->bps[0], first, last);
return;
}
diff --git a/fs/xfs/xfs_dinode.h b/fs/xfs/xfs_dinode.h
index dffba9b..a372163 100644
--- a/fs/xfs/xfs_dinode.h
+++ b/fs/xfs/xfs_dinode.h
@@ -148,7 +148,7 @@
be32_to_cpu((dip)->di_nextents) : \
be16_to_cpu((dip)->di_anextents))
-#define XFS_BUF_TO_DINODE(bp) ((xfs_dinode_t *)XFS_BUF_PTR(bp))
+#define XFS_BUF_TO_DINODE(bp) ((xfs_dinode_t *)((bp)->b_addr))
/*
* For block and character special files the 32bit dev_t is stored at the
diff --git a/fs/xfs/xfs_ialloc.c b/fs/xfs/xfs_ialloc.c
index dd5628b..9f24ec2 100644
--- a/fs/xfs/xfs_ialloc.c
+++ b/fs/xfs/xfs_ialloc.c
@@ -202,8 +202,7 @@
fbuf = xfs_trans_get_buf(tp, mp->m_ddev_targp, d,
mp->m_bsize * blks_per_cluster,
XBF_LOCK);
- ASSERT(fbuf);
- ASSERT(!XFS_BUF_GETERROR(fbuf));
+ ASSERT(!xfs_buf_geterror(fbuf));
/*
* Initialize all inodes in this buffer and then log them.
@@ -1486,7 +1485,7 @@
if (error)
return error;
- ASSERT(*bpp && !XFS_BUF_GETERROR(*bpp));
+ ASSERT(!xfs_buf_geterror(*bpp));
agi = XFS_BUF_TO_AGI(*bpp);
/*
diff --git a/fs/xfs/xfs_inode.c b/fs/xfs/xfs_inode.c
index 2fcca4b..0239a7c 100644
--- a/fs/xfs/xfs_inode.c
+++ b/fs/xfs/xfs_inode.c
@@ -2473,7 +2473,7 @@
if (bp->b_iodone) {
XFS_BUF_UNDONE(bp);
XFS_BUF_STALE(bp);
- XFS_BUF_ERROR(bp,EIO);
+ xfs_buf_ioerror(bp, EIO);
xfs_buf_ioend(bp, 0);
} else {
XFS_BUF_STALE(bp);
@@ -2585,7 +2585,7 @@
* If the buffer is pinned then push on the log now so we won't
* get stuck waiting in the write for too long.
*/
- if (XFS_BUF_ISPINNED(bp))
+ if (xfs_buf_ispinned(bp))
xfs_log_force(mp, 0);
/*
diff --git a/fs/xfs/xfs_log.c b/fs/xfs/xfs_log.c
index 06ff843..3a8d4f6 100644
--- a/fs/xfs/xfs_log.c
+++ b/fs/xfs/xfs_log.c
@@ -878,7 +878,7 @@
/*
* Race to shutdown the filesystem if we see an error.
*/
- if (XFS_TEST_ERROR((XFS_BUF_GETERROR(bp)), l->l_mp,
+ if (XFS_TEST_ERROR((xfs_buf_geterror(bp)), l->l_mp,
XFS_ERRTAG_IODONE_IOERR, XFS_RANDOM_IODONE_IOERR)) {
xfs_ioerror_alert("xlog_iodone", l->l_mp, bp, XFS_BUF_ADDR(bp));
XFS_BUF_STALE(bp);
@@ -1051,7 +1051,6 @@
if (!bp)
goto out_free_log;
bp->b_iodone = xlog_iodone;
- ASSERT(XFS_BUF_ISBUSY(bp));
ASSERT(xfs_buf_islocked(bp));
log->l_xbuf = bp;
@@ -1108,7 +1107,6 @@
iclog->ic_callback_tail = &(iclog->ic_callback);
iclog->ic_datap = (char *)iclog->ic_data + log->l_iclog_hsize;
- ASSERT(XFS_BUF_ISBUSY(iclog->ic_bp));
ASSERT(xfs_buf_islocked(iclog->ic_bp));
init_waitqueue_head(&iclog->ic_force_wait);
init_waitqueue_head(&iclog->ic_write_wait);
@@ -1248,7 +1246,7 @@
struct xlog_in_core *iclog = bp->b_fspriv;
if (iclog->ic_state & XLOG_STATE_IOERROR) {
- XFS_BUF_ERROR(bp, EIO);
+ xfs_buf_ioerror(bp, EIO);
XFS_BUF_STALE(bp);
xfs_buf_ioend(bp, 0);
/*
@@ -1355,7 +1353,6 @@
XFS_BUF_SET_COUNT(bp, count);
bp->b_fspriv = iclog;
XFS_BUF_ZEROFLAGS(bp);
- XFS_BUF_BUSY(bp);
XFS_BUF_ASYNC(bp);
bp->b_flags |= XBF_SYNCIO;
@@ -1398,16 +1395,15 @@
if (split) {
bp = iclog->ic_log->l_xbuf;
XFS_BUF_SET_ADDR(bp, 0); /* logical 0 */
- XFS_BUF_SET_PTR(bp, (xfs_caddr_t)((__psint_t)&(iclog->ic_header)+
- (__psint_t)count), split);
+ xfs_buf_associate_memory(bp,
+ (char *)&iclog->ic_header + count, split);
bp->b_fspriv = iclog;
XFS_BUF_ZEROFLAGS(bp);
- XFS_BUF_BUSY(bp);
XFS_BUF_ASYNC(bp);
bp->b_flags |= XBF_SYNCIO;
if (log->l_mp->m_flags & XFS_MOUNT_BARRIER)
bp->b_flags |= XBF_FUA;
- dptr = XFS_BUF_PTR(bp);
+ dptr = bp->b_addr;
/*
* Bump the cycle numbers at the start of each block
* since this part of the buffer is at the start of
diff --git a/fs/xfs/xfs_log_recover.c b/fs/xfs/xfs_log_recover.c
index 052a2c0..a199dbc 100644
--- a/fs/xfs/xfs_log_recover.c
+++ b/fs/xfs/xfs_log_recover.c
@@ -147,7 +147,7 @@
xfs_daddr_t offset = blk_no & ((xfs_daddr_t)log->l_sectBBsize - 1);
ASSERT(BBTOB(offset + nbblks) <= XFS_BUF_SIZE(bp));
- return XFS_BUF_PTR(bp) + BBTOB(offset);
+ return bp->b_addr + BBTOB(offset);
}
@@ -178,9 +178,7 @@
XFS_BUF_SET_ADDR(bp, log->l_logBBstart + blk_no);
XFS_BUF_READ(bp);
- XFS_BUF_BUSY(bp);
XFS_BUF_SET_COUNT(bp, BBTOB(nbblks));
- XFS_BUF_SET_TARGET(bp, log->l_mp->m_logdev_targp);
xfsbdstrat(log->l_mp, bp);
error = xfs_buf_iowait(bp);
@@ -220,18 +218,18 @@
xfs_buf_t *bp,
xfs_caddr_t offset)
{
- xfs_caddr_t orig_offset = XFS_BUF_PTR(bp);
+ xfs_caddr_t orig_offset = bp->b_addr;
int orig_len = bp->b_buffer_length;
int error, error2;
- error = XFS_BUF_SET_PTR(bp, offset, BBTOB(nbblks));
+ error = xfs_buf_associate_memory(bp, offset, BBTOB(nbblks));
if (error)
return error;
error = xlog_bread_noalign(log, blk_no, nbblks, bp);
/* must reset buffer pointer even on error */
- error2 = XFS_BUF_SET_PTR(bp, orig_offset, orig_len);
+ error2 = xfs_buf_associate_memory(bp, orig_offset, orig_len);
if (error)
return error;
return error2;
@@ -266,11 +264,9 @@
XFS_BUF_SET_ADDR(bp, log->l_logBBstart + blk_no);
XFS_BUF_ZEROFLAGS(bp);
- XFS_BUF_BUSY(bp);
- XFS_BUF_HOLD(bp);
+ xfs_buf_hold(bp);
xfs_buf_lock(bp);
XFS_BUF_SET_COUNT(bp, BBTOB(nbblks));
- XFS_BUF_SET_TARGET(bp, log->l_mp->m_logdev_targp);
if ((error = xfs_bwrite(log->l_mp, bp)))
xfs_ioerror_alert("xlog_bwrite", log->l_mp,
@@ -360,7 +356,7 @@
xlog_recover_iodone(
struct xfs_buf *bp)
{
- if (XFS_BUF_GETERROR(bp)) {
+ if (bp->b_error) {
/*
* We're not going to bother about retrying
* this during recovery. One strike!
@@ -1262,7 +1258,7 @@
*/
ealign = round_down(end_block, sectbb);
if (j == 0 && (start_block + endcount > ealign)) {
- offset = XFS_BUF_PTR(bp) + BBTOB(ealign - start_block);
+ offset = bp->b_addr + BBTOB(ealign - start_block);
error = xlog_bread_offset(log, ealign, sectbb,
bp, offset);
if (error)
@@ -2135,15 +2131,16 @@
bp = xfs_buf_read(mp->m_ddev_targp, buf_f->blf_blkno, buf_f->blf_len,
buf_flags);
- if (XFS_BUF_ISERROR(bp)) {
+ if (!bp)
+ return XFS_ERROR(ENOMEM);
+ error = bp->b_error;
+ if (error) {
xfs_ioerror_alert("xlog_recover_do..(read#1)", mp,
bp, buf_f->blf_blkno);
- error = XFS_BUF_GETERROR(bp);
xfs_buf_relse(bp);
return error;
}
- error = 0;
if (buf_f->blf_flags & XFS_BLF_INODE_BUF) {
error = xlog_recover_do_inode_buffer(mp, item, bp, buf_f);
} else if (buf_f->blf_flags &
@@ -2227,14 +2224,17 @@
bp = xfs_buf_read(mp->m_ddev_targp, in_f->ilf_blkno, in_f->ilf_len,
XBF_LOCK);
- if (XFS_BUF_ISERROR(bp)) {
+ if (!bp) {
+ error = ENOMEM;
+ goto error;
+ }
+ error = bp->b_error;
+ if (error) {
xfs_ioerror_alert("xlog_recover_do..(read#2)", mp,
bp, in_f->ilf_blkno);
- error = XFS_BUF_GETERROR(bp);
xfs_buf_relse(bp);
goto error;
}
- error = 0;
ASSERT(in_f->ilf_fields & XFS_ILOG_CORE);
dip = (xfs_dinode_t *)xfs_buf_offset(bp, in_f->ilf_boffset);
@@ -3437,7 +3437,7 @@
/*
* Check for header wrapping around physical end-of-log
*/
- offset = XFS_BUF_PTR(hbp);
+ offset = hbp->b_addr;
split_hblks = 0;
wrapped_hblks = 0;
if (blk_no + hblks <= log->l_logBBsize) {
@@ -3497,7 +3497,7 @@
} else {
/* This log record is split across the
* physical end of log */
- offset = XFS_BUF_PTR(dbp);
+ offset = dbp->b_addr;
split_bblks = 0;
if (blk_no != log->l_logBBsize) {
/* some data is before the physical
diff --git a/fs/xfs/xfs_mount.c b/fs/xfs/xfs_mount.c
index 092e16a..0081657 100644
--- a/fs/xfs/xfs_mount.c
+++ b/fs/xfs/xfs_mount.c
@@ -1615,7 +1615,7 @@
XFS_BUF_UNDELAYWRITE(sbp);
XFS_BUF_WRITE(sbp);
XFS_BUF_UNASYNC(sbp);
- ASSERT(XFS_BUF_TARGET(sbp) == mp->m_ddev_targp);
+ ASSERT(sbp->b_target == mp->m_ddev_targp);
xfsbdstrat(mp, sbp);
error = xfs_buf_iowait(sbp);
if (error)
@@ -1938,7 +1938,7 @@
xfs_buf_lock(bp);
}
- XFS_BUF_HOLD(bp);
+ xfs_buf_hold(bp);
ASSERT(XFS_BUF_ISDONE(bp));
return bp;
}
diff --git a/fs/xfs/xfs_rtalloc.c b/fs/xfs/xfs_rtalloc.c
index 8f76fdf..35561a5 100644
--- a/fs/xfs/xfs_rtalloc.c
+++ b/fs/xfs/xfs_rtalloc.c
@@ -168,7 +168,7 @@
xfs_trans_cancel(tp, cancelflags);
goto error;
}
- memset(XFS_BUF_PTR(bp), 0, mp->m_sb.sb_blocksize);
+ memset(bp->b_addr, 0, mp->m_sb.sb_blocksize);
xfs_trans_log_buf(tp, bp, 0, mp->m_sb.sb_blocksize - 1);
/*
* Commit the transaction.
@@ -883,7 +883,7 @@
if (error) {
return error;
}
- ASSERT(bp && !XFS_BUF_GETERROR(bp));
+ ASSERT(!xfs_buf_geterror(bp));
*bpp = bp;
return 0;
}
@@ -943,7 +943,7 @@
if (error) {
return error;
}
- bufp = (xfs_rtword_t *)XFS_BUF_PTR(bp);
+ bufp = bp->b_addr;
/*
* Compute the starting word's address, and starting bit.
*/
@@ -994,7 +994,7 @@
if (error) {
return error;
}
- b = bufp = (xfs_rtword_t *)XFS_BUF_PTR(bp);
+ b = bufp = bp->b_addr;
word = 0;
} else {
/*
@@ -1040,7 +1040,7 @@
if (error) {
return error;
}
- b = bufp = (xfs_rtword_t *)XFS_BUF_PTR(bp);
+ b = bufp = bp->b_addr;
word = 0;
} else {
/*
@@ -1158,7 +1158,7 @@
if (error) {
return error;
}
- bufp = (xfs_rtword_t *)XFS_BUF_PTR(bp);
+ bufp = bp->b_addr;
/*
* Get the first word's index & point to it.
*/
@@ -1210,7 +1210,7 @@
if (error) {
return error;
}
- bufp = (xfs_rtword_t *)XFS_BUF_PTR(bp);
+ bufp = bp->b_addr;
word = XFS_BLOCKWMASK(mp);
b = &bufp[word];
} else {
@@ -1256,7 +1256,7 @@
if (error) {
return error;
}
- bufp = (xfs_rtword_t *)XFS_BUF_PTR(bp);
+ bufp = bp->b_addr;
word = XFS_BLOCKWMASK(mp);
b = &bufp[word];
} else {
@@ -1333,7 +1333,7 @@
if (error) {
return error;
}
- bufp = (xfs_rtword_t *)XFS_BUF_PTR(bp);
+ bufp = bp->b_addr;
/*
* Get the first word's index & point to it.
*/
@@ -1384,7 +1384,7 @@
if (error) {
return error;
}
- b = bufp = (xfs_rtword_t *)XFS_BUF_PTR(bp);
+ b = bufp = bp->b_addr;
word = 0;
} else {
/*
@@ -1429,7 +1429,7 @@
if (error) {
return error;
}
- b = bufp = (xfs_rtword_t *)XFS_BUF_PTR(bp);
+ b = bufp = bp->b_addr;
word = 0;
} else {
/*
@@ -1649,7 +1649,7 @@
if (error) {
return error;
}
- bufp = (xfs_rtword_t *)XFS_BUF_PTR(bp);
+ bufp = bp->b_addr;
/*
* Compute the starting word's address, and starting bit.
*/
@@ -1694,7 +1694,7 @@
if (error) {
return error;
}
- first = b = bufp = (xfs_rtword_t *)XFS_BUF_PTR(bp);
+ first = b = bufp = bp->b_addr;
word = 0;
} else {
/*
@@ -1734,7 +1734,7 @@
if (error) {
return error;
}
- first = b = bufp = (xfs_rtword_t *)XFS_BUF_PTR(bp);
+ first = b = bufp = bp->b_addr;
word = 0;
} else {
/*
@@ -1832,8 +1832,8 @@
*/
sp = XFS_SUMPTR(mp, bp, so);
*sp += delta;
- xfs_trans_log_buf(tp, bp, (uint)((char *)sp - (char *)XFS_BUF_PTR(bp)),
- (uint)((char *)sp - (char *)XFS_BUF_PTR(bp) + sizeof(*sp) - 1));
+ xfs_trans_log_buf(tp, bp, (uint)((char *)sp - (char *)bp->b_addr),
+ (uint)((char *)sp - (char *)bp->b_addr + sizeof(*sp) - 1));
return 0;
}
diff --git a/fs/xfs/xfs_rtalloc.h b/fs/xfs/xfs_rtalloc.h
index 09e1f4f..f7f3a35 100644
--- a/fs/xfs/xfs_rtalloc.h
+++ b/fs/xfs/xfs_rtalloc.h
@@ -47,7 +47,7 @@
#define XFS_SUMOFFSTOBLOCK(mp,s) \
(((s) * (uint)sizeof(xfs_suminfo_t)) >> (mp)->m_sb.sb_blocklog)
#define XFS_SUMPTR(mp,bp,so) \
- ((xfs_suminfo_t *)((char *)XFS_BUF_PTR(bp) + \
+ ((xfs_suminfo_t *)((bp)->b_addr + \
(((so) * (uint)sizeof(xfs_suminfo_t)) & XFS_BLOCKMASK(mp))))
#define XFS_BITTOBLOCK(mp,bi) ((bi) >> (mp)->m_blkbit_log)
diff --git a/fs/xfs/xfs_rw.c b/fs/xfs/xfs_rw.c
index d6d6fdf..c96a8a0 100644
--- a/fs/xfs/xfs_rw.c
+++ b/fs/xfs/xfs_rw.c
@@ -104,9 +104,9 @@
xfs_alert(mp,
"I/O error occurred: meta-data dev %s block 0x%llx"
" (\"%s\") error %d buf count %zd",
- XFS_BUFTARG_NAME(XFS_BUF_TARGET(bp)),
+ xfs_buf_target_name(bp->b_target),
(__uint64_t)blkno, func,
- XFS_BUF_GETERROR(bp), XFS_BUF_COUNT(bp));
+ bp->b_error, XFS_BUF_COUNT(bp));
}
/*
@@ -137,8 +137,8 @@
bp = xfs_buf_read(target, blkno, len, flags);
if (!bp)
return XFS_ERROR(EIO);
- error = XFS_BUF_GETERROR(bp);
- if (bp && !error && !XFS_FORCED_SHUTDOWN(mp)) {
+ error = bp->b_error;
+ if (!error && !XFS_FORCED_SHUTDOWN(mp)) {
*bpp = bp;
} else {
*bpp = NULL;
diff --git a/fs/xfs/xfs_sb.h b/fs/xfs/xfs_sb.h
index 1eb2ba5..cb6ae71 100644
--- a/fs/xfs/xfs_sb.h
+++ b/fs/xfs/xfs_sb.h
@@ -509,7 +509,7 @@
#define XFS_SB_DADDR ((xfs_daddr_t)0) /* daddr in filesystem/ag */
#define XFS_SB_BLOCK(mp) XFS_HDR_BLOCK(mp, XFS_SB_DADDR)
-#define XFS_BUF_TO_SBP(bp) ((xfs_dsb_t *)XFS_BUF_PTR(bp))
+#define XFS_BUF_TO_SBP(bp) ((xfs_dsb_t *)((bp)->b_addr))
#define XFS_HDR_BLOCK(mp,d) ((xfs_agblock_t)XFS_BB_TO_FSBT(mp,d))
#define XFS_DADDR_TO_FSB(mp,d) XFS_AGB_TO_FSB(mp, \
diff --git a/fs/xfs/xfs_trans_ail.c b/fs/xfs/xfs_trans_ail.c
index 43233e9..c15aa29 100644
--- a/fs/xfs/xfs_trans_ail.c
+++ b/fs/xfs/xfs_trans_ail.c
@@ -299,7 +299,7 @@
* Splice the log item list into the AIL at the given LSN. We splice to the
* tail of the given LSN to maintain insert order for push traversals. The
* cursor is optional, allowing repeated updates to the same LSN to avoid
- * repeated traversals.
+ * repeated traversals. This should not be called with an empty list.
*/
static void
xfs_ail_splice(
@@ -308,50 +308,39 @@
struct list_head *list,
xfs_lsn_t lsn)
{
- struct xfs_log_item *lip = cur ? cur->item : NULL;
- struct xfs_log_item *next_lip;
+ struct xfs_log_item *lip;
+
+ ASSERT(!list_empty(list));
/*
- * Get a new cursor if we don't have a placeholder or the existing one
- * has been invalidated.
+ * Use the cursor to determine the insertion point if one is
+ * provided. If not, or if the one we got is not valid,
+ * find the place in the AIL where the items belong.
*/
- if (!lip || (__psint_t)lip & 1) {
+ lip = cur ? cur->item : NULL;
+ if (!lip || (__psint_t) lip & 1)
lip = __xfs_trans_ail_cursor_last(ailp, lsn);
- if (!lip) {
- /* The list is empty, so just splice and return. */
- if (cur)
- cur->item = NULL;
- list_splice(list, &ailp->xa_ail);
- return;
- }
- }
+ /*
+ * If a cursor is provided, we know we're processing the AIL
+ * in lsn order, and future items to be spliced in will
+ * follow the last one being inserted now. Update the
+ * cursor to point to that last item, now while we have a
+ * reliable pointer to it.
+ */
+ if (cur)
+ cur->item = list_entry(list->prev, struct xfs_log_item, li_ail);
/*
- * Our cursor points to the item we want to insert _after_, so we have
- * to update the cursor to point to the end of the list we are splicing
- * in so that it points to the correct location for the next splice.
- * i.e. before the splice
- *
- * lsn -> lsn -> lsn + x -> lsn + x ...
- * ^
- * | cursor points here
- *
- * After the splice we have:
- *
- * lsn -> lsn -> lsn -> lsn -> .... -> lsn -> lsn + x -> lsn + x ...
- * ^ ^
- * | cursor points here | needs to move here
- *
- * So we set the cursor to the last item in the list to be spliced
- * before we execute the splice, resulting in the cursor pointing to
- * the correct item after the splice occurs.
+ * Finally perform the splice. Unless the AIL was empty,
+ * lip points to the item in the AIL _after_ which the new
+ * items should go. If lip is null the AIL was empty, so
+ * the new items go at the head of the AIL.
*/
- if (cur) {
- next_lip = list_entry(list->prev, struct xfs_log_item, li_ail);
- cur->item = next_lip;
- }
- list_splice(list, &lip->li_ail);
+ if (lip)
+ list_splice(list, &lip->li_ail);
+ else
+ list_splice(list, &ailp->xa_ail);
}
/*
@@ -682,6 +671,7 @@
int i;
LIST_HEAD(tmp);
+ ASSERT(nr_items > 0); /* Not required, but true. */
mlip = xfs_ail_min(ailp);
for (i = 0; i < nr_items; i++) {
@@ -701,7 +691,8 @@
list_add(&lip->li_ail, &tmp);
}
- xfs_ail_splice(ailp, cur, &tmp, lsn);
+ if (!list_empty(&tmp))
+ xfs_ail_splice(ailp, cur, &tmp, lsn);
if (!mlip_changed) {
spin_unlock(&ailp->xa_lock);
diff --git a/fs/xfs/xfs_trans_buf.c b/fs/xfs/xfs_trans_buf.c
index 15584fc..137e2b9 100644
--- a/fs/xfs/xfs_trans_buf.c
+++ b/fs/xfs/xfs_trans_buf.c
@@ -54,7 +54,7 @@
list_for_each_entry(lidp, &tp->t_items, lid_trans) {
blip = (struct xfs_buf_log_item *)lidp->lid_item;
if (blip->bli_item.li_type == XFS_LI_BUF &&
- XFS_BUF_TARGET(blip->bli_buf) == target &&
+ blip->bli_buf->b_target == target &&
XFS_BUF_ADDR(blip->bli_buf) == blkno &&
XFS_BUF_COUNT(blip->bli_buf) == len)
return blip->bli_buf;
@@ -80,7 +80,6 @@
{
struct xfs_buf_log_item *bip;
- ASSERT(XFS_BUF_ISBUSY(bp));
ASSERT(bp->b_transp == NULL);
/*
@@ -194,7 +193,7 @@
return NULL;
}
- ASSERT(!XFS_BUF_GETERROR(bp));
+ ASSERT(!bp->b_error);
_xfs_trans_bjoin(tp, bp, 1);
trace_xfs_trans_get_buf(bp->b_fspriv);
@@ -293,10 +292,10 @@
return (flags & XBF_TRYLOCK) ?
EAGAIN : XFS_ERROR(ENOMEM);
- if (XFS_BUF_GETERROR(bp) != 0) {
+ if (bp->b_error) {
+ error = bp->b_error;
xfs_ioerror_alert("xfs_trans_read_buf", mp,
bp, blkno);
- error = XFS_BUF_GETERROR(bp);
xfs_buf_relse(bp);
return error;
}
@@ -330,7 +329,7 @@
ASSERT(xfs_buf_islocked(bp));
ASSERT(bp->b_transp == tp);
ASSERT(bp->b_fspriv != NULL);
- ASSERT((XFS_BUF_ISERROR(bp)) == 0);
+ ASSERT(!bp->b_error);
if (!(XFS_BUF_ISDONE(bp))) {
trace_xfs_trans_read_buf_io(bp, _RET_IP_);
ASSERT(!XFS_BUF_ISASYNC(bp));
@@ -386,10 +385,9 @@
return (flags & XBF_TRYLOCK) ?
0 : XFS_ERROR(ENOMEM);
}
- if (XFS_BUF_GETERROR(bp) != 0) {
- XFS_BUF_SUPER_STALE(bp);
- error = XFS_BUF_GETERROR(bp);
-
+ if (bp->b_error) {
+ error = bp->b_error;
+ XFS_BUF_SUPER_STALE(bp);
xfs_ioerror_alert("xfs_trans_read_buf", mp,
bp, blkno);
if (tp->t_flags & XFS_TRANS_DIRTY)
@@ -430,7 +428,7 @@
if (XFS_BUF_ISSTALE(bp) && XFS_BUF_ISDELAYWRITE(bp))
xfs_notice(mp, "about to pop assert, bp == 0x%p", bp);
#endif
- ASSERT((XFS_BUF_BFLAGS(bp) & (XBF_STALE|XBF_DELWRI)) !=
+ ASSERT((bp->b_flags & (XBF_STALE|XBF_DELWRI)) !=
(XBF_STALE|XBF_DELWRI));
trace_xfs_trans_read_buf_shut(bp, _RET_IP_);
@@ -581,7 +579,6 @@
{
xfs_buf_log_item_t *bip = bp->b_fspriv;
- ASSERT(XFS_BUF_ISBUSY(bp));
ASSERT(bp->b_transp == tp);
ASSERT(bip != NULL);
ASSERT(!(bip->bli_flags & XFS_BLI_STALE));
@@ -602,7 +599,6 @@
{
xfs_buf_log_item_t *bip = bp->b_fspriv;
- ASSERT(XFS_BUF_ISBUSY(bp));
ASSERT(bp->b_transp == tp);
ASSERT(bip != NULL);
ASSERT(!(bip->bli_flags & XFS_BLI_STALE));
@@ -631,7 +627,6 @@
{
xfs_buf_log_item_t *bip = bp->b_fspriv;
- ASSERT(XFS_BUF_ISBUSY(bp));
ASSERT(bp->b_transp == tp);
ASSERT(bip != NULL);
ASSERT((first <= last) && (last < XFS_BUF_COUNT(bp)));
@@ -702,7 +697,6 @@
{
xfs_buf_log_item_t *bip = bp->b_fspriv;
- ASSERT(XFS_BUF_ISBUSY(bp));
ASSERT(bp->b_transp == tp);
ASSERT(bip != NULL);
ASSERT(atomic_read(&bip->bli_refcount) > 0);
@@ -774,7 +768,6 @@
{
xfs_buf_log_item_t *bip = bp->b_fspriv;
- ASSERT(XFS_BUF_ISBUSY(bp));
ASSERT(bp->b_transp == tp);
ASSERT(bip != NULL);
ASSERT(atomic_read(&bip->bli_refcount) > 0);
@@ -798,7 +791,6 @@
{
xfs_buf_log_item_t *bip = bp->b_fspriv;
- ASSERT(XFS_BUF_ISBUSY(bp));
ASSERT(bp->b_transp == tp);
ASSERT(bip != NULL);
ASSERT(atomic_read(&bip->bli_refcount) > 0);
@@ -823,7 +815,6 @@
{
xfs_buf_log_item_t *bip = bp->b_fspriv;
- ASSERT(XFS_BUF_ISBUSY(bp));
ASSERT(bp->b_transp == tp);
ASSERT(bip != NULL);
ASSERT(atomic_read(&bip->bli_refcount) > 0);
@@ -851,7 +842,6 @@
{
xfs_buf_log_item_t *bip = bp->b_fspriv;
- ASSERT(XFS_BUF_ISBUSY(bp));
ASSERT(bp->b_transp == tp);
ASSERT(bip != NULL);
ASSERT(type == XFS_BLF_UDQUOT_BUF ||
diff --git a/fs/xfs/xfs_vnodeops.c b/fs/xfs/xfs_vnodeops.c
index 9322e13..51fc429 100644
--- a/fs/xfs/xfs_vnodeops.c
+++ b/fs/xfs/xfs_vnodeops.c
@@ -83,7 +83,9 @@
bp = xfs_buf_read(mp->m_ddev_targp, d, BTOBB(byte_cnt),
XBF_LOCK | XBF_MAPPED | XBF_DONT_BLOCK);
- error = XFS_BUF_GETERROR(bp);
+ if (!bp)
+ return XFS_ERROR(ENOMEM);
+ error = bp->b_error;
if (error) {
xfs_ioerror_alert("xfs_readlink",
ip->i_mount, bp, XFS_BUF_ADDR(bp));
@@ -94,7 +96,7 @@
byte_cnt = pathlen;
pathlen -= byte_cnt;
- memcpy(link, XFS_BUF_PTR(bp), byte_cnt);
+ memcpy(link, bp->b_addr, byte_cnt);
xfs_buf_relse(bp);
}
@@ -1648,13 +1650,13 @@
byte_cnt = XFS_FSB_TO_B(mp, mval[n].br_blockcount);
bp = xfs_trans_get_buf(tp, mp->m_ddev_targp, d,
BTOBB(byte_cnt), 0);
- ASSERT(bp && !XFS_BUF_GETERROR(bp));
+ ASSERT(!xfs_buf_geterror(bp));
if (pathlen < byte_cnt) {
byte_cnt = pathlen;
}
pathlen -= byte_cnt;
- memcpy(XFS_BUF_PTR(bp), cur_chunk, byte_cnt);
+ memcpy(bp->b_addr, cur_chunk, byte_cnt);
cur_chunk += byte_cnt;
xfs_trans_log_buf(tp, bp, 0, byte_cnt - 1);
@@ -1999,7 +2001,7 @@
mp, bp, XFS_BUF_ADDR(bp));
break;
}
- memset(XFS_BUF_PTR(bp) +
+ memset(bp->b_addr +
(offset - XFS_FSB_TO_B(mp, imap.br_startoff)),
0, lastoffset - offset + 1);
XFS_BUF_UNDONE(bp);
diff --git a/include/acpi/acpi_drivers.h b/include/acpi/acpi_drivers.h
index 3090471..e49c36d 100644
--- a/include/acpi/acpi_drivers.h
+++ b/include/acpi/acpi_drivers.h
@@ -128,7 +128,7 @@
extern int register_dock_notifier(struct notifier_block *nb);
extern void unregister_dock_notifier(struct notifier_block *nb);
extern int register_hotplug_dock_device(acpi_handle handle,
- struct acpi_dock_ops *ops,
+ const struct acpi_dock_ops *ops,
void *context);
extern void unregister_hotplug_dock_device(acpi_handle handle);
#else
diff --git a/include/acpi/acpixf.h b/include/acpi/acpixf.h
index 2ed0a84..f554a93 100644
--- a/include/acpi/acpixf.h
+++ b/include/acpi/acpixf.h
@@ -47,7 +47,7 @@
/* Current ACPICA subsystem version in YYYYMMDD format */
-#define ACPI_CA_VERSION 0x20110413
+#define ACPI_CA_VERSION 0x20110623
#include "actypes.h"
#include "actbl.h"
@@ -69,6 +69,7 @@
extern u32 acpi_gbl_enable_aml_debug_object;
extern u8 acpi_gbl_copy_dsdt_locally;
extern u8 acpi_gbl_truncate_io_addresses;
+extern u8 acpi_gbl_disable_auto_repair;
extern u32 acpi_current_gpe_count;
extern struct acpi_table_fadt acpi_gbl_FADT;
diff --git a/include/acpi/apei.h b/include/acpi/apei.h
index e67b523..51a527d 100644
--- a/include/acpi/apei.h
+++ b/include/acpi/apei.h
@@ -18,6 +18,11 @@
extern int hest_disable;
extern int erst_disable;
+#ifdef CONFIG_ACPI_APEI_GHES
+extern int ghes_disable;
+#else
+#define ghes_disable 1
+#endif
#ifdef CONFIG_ACPI_APEI
void __init acpi_hest_init(void);
diff --git a/include/acpi/processor.h b/include/acpi/processor.h
index ba4928c..67055f1 100644
--- a/include/acpi/processor.h
+++ b/include/acpi/processor.h
@@ -337,7 +337,7 @@
/* in processor_thermal.c */
int acpi_processor_get_limit_info(struct acpi_processor *pr);
-extern struct thermal_cooling_device_ops processor_cooling_ops;
+extern const struct thermal_cooling_device_ops processor_cooling_ops;
#ifdef CONFIG_CPU_FREQ
void acpi_thermal_cpufreq_init(void);
void acpi_thermal_cpufreq_exit(void);
diff --git a/include/asm-generic/memory_model.h b/include/asm-generic/memory_model.h
index fb2d63f..aea9e45 100644
--- a/include/asm-generic/memory_model.h
+++ b/include/asm-generic/memory_model.h
@@ -39,7 +39,7 @@
})
#define __page_to_pfn(pg) \
-({ struct page *__pg = (pg); \
+({ const struct page *__pg = (pg); \
struct pglist_data *__pgdat = NODE_DATA(page_to_nid(__pg)); \
(unsigned long)(__pg - __pgdat->node_mem_map) + \
__pgdat->node_start_pfn; \
@@ -57,7 +57,7 @@
* section[i].section_mem_map == mem_map's address - start_pfn;
*/
#define __page_to_pfn(pg) \
-({ struct page *__pg = (pg); \
+({ const struct page *__pg = (pg); \
int __sec = page_to_section(__pg); \
(unsigned long)(__pg - __section_mem_map_addr(__nr_to_section(__sec))); \
})
diff --git a/include/drm/drm_crtc.h b/include/drm/drm_crtc.h
index 33d12f8..44335e5 100644
--- a/include/drm/drm_crtc.h
+++ b/include/drm/drm_crtc.h
@@ -205,6 +205,8 @@
enum subpixel_order subpixel_order;
u32 color_formats;
+ u8 cea_rev;
+
char *raw_edid; /* if any */
};
@@ -802,6 +804,7 @@
extern int drm_add_modes_noedid(struct drm_connector *connector,
int hdisplay, int vdisplay);
+extern int drm_edid_header_is_valid(const u8 *raw_edid);
extern bool drm_edid_is_valid(struct edid *edid);
struct drm_display_mode *drm_mode_find_dmt(struct drm_device *dev,
int hsize, int vsize, int fresh);
diff --git a/include/drm/i915_drm.h b/include/drm/i915_drm.h
index c4d6dbf..28c0d11 100644
--- a/include/drm/i915_drm.h
+++ b/include/drm/i915_drm.h
@@ -237,7 +237,7 @@
#define DRM_IOCTL_I915_GEM_GET_APERTURE DRM_IOR (DRM_COMMAND_BASE + DRM_I915_GEM_GET_APERTURE, struct drm_i915_gem_get_aperture)
#define DRM_IOCTL_I915_GET_PIPE_FROM_CRTC_ID DRM_IOWR(DRM_COMMAND_BASE + DRM_I915_GET_PIPE_FROM_CRTC_ID, struct drm_i915_get_pipe_from_crtc_id)
#define DRM_IOCTL_I915_GEM_MADVISE DRM_IOWR(DRM_COMMAND_BASE + DRM_I915_GEM_MADVISE, struct drm_i915_gem_madvise)
-#define DRM_IOCTL_I915_OVERLAY_PUT_IMAGE DRM_IOW(DRM_COMMAND_BASE + DRM_IOCTL_I915_OVERLAY_ATTRS, struct drm_intel_overlay_put_image)
+#define DRM_IOCTL_I915_OVERLAY_PUT_IMAGE DRM_IOW(DRM_COMMAND_BASE + DRM_I915_OVERLAY_PUT_IMAGE, struct drm_intel_overlay_put_image)
#define DRM_IOCTL_I915_OVERLAY_ATTRS DRM_IOWR(DRM_COMMAND_BASE + DRM_I915_OVERLAY_ATTRS, struct drm_intel_overlay_attrs)
/* Allow drivers to submit batchbuffers directly to hardware, relying
diff --git a/include/linux/acpi.h b/include/linux/acpi.h
index 1deb2a7..6001b4da 100644
--- a/include/linux/acpi.h
+++ b/include/linux/acpi.h
@@ -238,7 +238,6 @@
extern int pnpacpi_disabled;
#define PXM_INVAL (-1)
-#define NID_INVAL (-1)
int acpi_check_resource_conflict(const struct resource *res);
@@ -280,6 +279,8 @@
#define OSC_SB_CPUHP_OST_SUPPORT 8
#define OSC_SB_APEI_SUPPORT 16
+extern bool osc_sb_apei_support_acked;
+
/* PCI defined _OSC bits */
/* _OSC DW1 Definition (OS Support Fields) */
#define OSC_EXT_PCI_CONFIG_SUPPORT 1
diff --git a/include/linux/amba/pl08x.h b/include/linux/amba/pl08x.h
index 3111385..e6e28f3 100644
--- a/include/linux/amba/pl08x.h
+++ b/include/linux/amba/pl08x.h
@@ -172,8 +172,11 @@
int phychan_hold;
struct tasklet_struct tasklet;
char *name;
- struct pl08x_channel_data *cd;
- dma_addr_t runtime_addr;
+ const struct pl08x_channel_data *cd;
+ dma_addr_t src_addr;
+ dma_addr_t dst_addr;
+ u32 src_cctl;
+ u32 dst_cctl;
enum dma_data_direction runtime_direction;
dma_cookie_t lc;
struct list_head pend_list;
@@ -202,7 +205,7 @@
* @mem_buses: buses which memory can be accessed from: PL08X_AHB1 | PL08X_AHB2
*/
struct pl08x_platform_data {
- struct pl08x_channel_data *slave_channels;
+ const struct pl08x_channel_data *slave_channels;
unsigned int num_slave_channels;
struct pl08x_channel_data memcpy_channel;
int (*get_signal)(struct pl08x_dma_chan *);
diff --git a/include/linux/bitmap.h b/include/linux/bitmap.h
index 3bac44c..7ad6345 100644
--- a/include/linux/bitmap.h
+++ b/include/linux/bitmap.h
@@ -146,6 +146,7 @@
extern void bitmap_copy_le(void *dst, const unsigned long *src, int nbits);
extern int bitmap_ord_to_pos(const unsigned long *bitmap, int n, int bits);
+#define BITMAP_FIRST_WORD_MASK(start) (~0UL << ((start) % BITS_PER_LONG))
#define BITMAP_LAST_WORD_MASK(nbits) \
( \
((nbits) % BITS_PER_LONG) ? \
diff --git a/include/linux/cpuidle.h b/include/linux/cpuidle.h
index 36719ea..b51629e 100644
--- a/include/linux/cpuidle.h
+++ b/include/linux/cpuidle.h
@@ -122,6 +122,8 @@
};
#ifdef CONFIG_CPU_IDLE
+extern void disable_cpuidle(void);
+extern int cpuidle_idle_call(void);
extern int cpuidle_register_driver(struct cpuidle_driver *drv);
struct cpuidle_driver *cpuidle_get_driver(void);
@@ -135,6 +137,8 @@
extern void cpuidle_disable_device(struct cpuidle_device *dev);
#else
+static inline void disable_cpuidle(void) { }
+static inline int cpuidle_idle_call(void) { return -ENODEV; }
static inline int cpuidle_register_driver(struct cpuidle_driver *drv)
{return -ENODEV; }
diff --git a/include/linux/cred.h b/include/linux/cred.h
index 48e82af..4030896 100644
--- a/include/linux/cred.h
+++ b/include/linux/cred.h
@@ -265,10 +265,11 @@
/**
* current_cred - Access the current task's subjective credentials
*
- * Access the subjective credentials of the current task.
+ * Access the subjective credentials of the current task. RCU-safe,
+ * since nobody else can modify it.
*/
#define current_cred() \
- (current->cred)
+ rcu_dereference_protected(current->cred, 1)
/**
* __task_cred - Access a task's objective credentials
@@ -306,8 +307,8 @@
#define get_current_user() \
({ \
struct user_struct *__u; \
- struct cred *__cred; \
- __cred = (struct cred *) current_cred(); \
+ const struct cred *__cred; \
+ __cred = current_cred(); \
__u = get_uid(__cred->user); \
__u; \
})
@@ -321,8 +322,8 @@
#define get_current_groups() \
({ \
struct group_info *__groups; \
- struct cred *__cred; \
- __cred = (struct cred *) current_cred(); \
+ const struct cred *__cred; \
+ __cred = current_cred(); \
__groups = get_group_info(__cred->group_info); \
__groups; \
})
@@ -341,7 +342,7 @@
#define current_cred_xxx(xxx) \
({ \
- current->cred->xxx; \
+ current_cred()->xxx; \
})
#define current_uid() (current_cred_xxx(uid))
diff --git a/include/linux/cryptohash.h b/include/linux/cryptohash.h
index ec78a4b..2cd9f1c 100644
--- a/include/linux/cryptohash.h
+++ b/include/linux/cryptohash.h
@@ -3,11 +3,16 @@
#define SHA_DIGEST_WORDS 5
#define SHA_MESSAGE_BYTES (512 /*bits*/ / 8)
-#define SHA_WORKSPACE_WORDS 80
+#define SHA_WORKSPACE_WORDS 16
void sha_init(__u32 *buf);
void sha_transform(__u32 *digest, const char *data, __u32 *W);
+#define MD5_DIGEST_WORDS 4
+#define MD5_MESSAGE_BYTES 64
+
+void md5_transform(__u32 *hash, __u32 const *in);
+
__u32 half_md4_transform(__u32 buf[4], __u32 const in[8]);
#endif
diff --git a/include/linux/dcache.h b/include/linux/dcache.h
index d37d2a7..62157c0 100644
--- a/include/linux/dcache.h
+++ b/include/linux/dcache.h
@@ -180,12 +180,12 @@
*/
/* d_flags entries */
-#define DCACHE_AUTOFS_PENDING 0x0001 /* autofs: "under construction" */
-#define DCACHE_NFSFS_RENAMED 0x0002
- /* this dentry has been "silly renamed" and has to be deleted on the last
- * dput() */
+#define DCACHE_OP_HASH 0x0001
+#define DCACHE_OP_COMPARE 0x0002
+#define DCACHE_OP_REVALIDATE 0x0004
+#define DCACHE_OP_DELETE 0x0008
-#define DCACHE_DISCONNECTED 0x0004
+#define DCACHE_DISCONNECTED 0x0010
/* This dentry is possibly not currently connected to the dcache tree, in
* which case its parent will either be itself, or will have this flag as
* well. nfsd will not use a dentry with this bit set, but will first
@@ -196,22 +196,18 @@
* dentry into place and return that dentry rather than the passed one,
* typically using d_splice_alias. */
-#define DCACHE_REFERENCED 0x0008 /* Recently used, don't discard. */
-#define DCACHE_RCUACCESS 0x0010 /* Entry has ever been RCU-visible */
-#define DCACHE_INOTIFY_PARENT_WATCHED 0x0020
- /* Parent inode is watched by inotify */
-
-#define DCACHE_COOKIE 0x0040 /* For use by dcookie subsystem */
-#define DCACHE_FSNOTIFY_PARENT_WATCHED 0x0080
- /* Parent inode is watched by some fsnotify listener */
+#define DCACHE_REFERENCED 0x0020 /* Recently used, don't discard. */
+#define DCACHE_RCUACCESS 0x0040 /* Entry has ever been RCU-visible */
#define DCACHE_CANT_MOUNT 0x0100
#define DCACHE_GENOCIDE 0x0200
-#define DCACHE_OP_HASH 0x1000
-#define DCACHE_OP_COMPARE 0x2000
-#define DCACHE_OP_REVALIDATE 0x4000
-#define DCACHE_OP_DELETE 0x8000
+#define DCACHE_NFSFS_RENAMED 0x1000
+ /* this dentry has been "silly renamed" and has to be deleted on the last
+ * dput() */
+#define DCACHE_COOKIE 0x2000 /* For use by dcookie subsystem */
+#define DCACHE_FSNOTIFY_PARENT_WATCHED 0x4000
+ /* Parent inode is watched by some fsnotify listener */
#define DCACHE_MOUNTED 0x10000 /* is a mountpoint */
#define DCACHE_NEED_AUTOMOUNT 0x20000 /* handle automount on this dir */
diff --git a/include/linux/device-mapper.h b/include/linux/device-mapper.h
index 4427e04..3fa1f3d 100644
--- a/include/linux/device-mapper.h
+++ b/include/linux/device-mapper.h
@@ -208,6 +208,49 @@
int dm_register_target(struct target_type *t);
void dm_unregister_target(struct target_type *t);
+/*
+ * Target argument parsing.
+ */
+struct dm_arg_set {
+ unsigned argc;
+ char **argv;
+};
+
+/*
+ * The minimum and maximum value of a numeric argument, together with
+ * the error message to use if the number is found to be outside that range.
+ */
+struct dm_arg {
+ unsigned min;
+ unsigned max;
+ char *error;
+};
+
+/*
+ * Validate the next argument, either returning it as *value or, if invalid,
+ * returning -EINVAL and setting *error.
+ */
+int dm_read_arg(struct dm_arg *arg, struct dm_arg_set *arg_set,
+ unsigned *value, char **error);
+
+/*
+ * Process the next argument as the start of a group containing between
+ * arg->min and arg->max further arguments. Either return the size as
+ * *num_args or, if invalid, return -EINVAL and set *error.
+ */
+int dm_read_arg_group(struct dm_arg *arg, struct dm_arg_set *arg_set,
+ unsigned *num_args, char **error);
+
+/*
+ * Return the current argument and shift to the next.
+ */
+const char *dm_shift_arg(struct dm_arg_set *as);
+
+/*
+ * Move through num_args arguments.
+ */
+void dm_consume_args(struct dm_arg_set *as, unsigned num_args);
+
/*-----------------------------------------------------------------
* Functions for creating and manipulating mapped devices.
* Drop the reference with dm_put when you finish with the object.
diff --git a/include/linux/dm-ioctl.h b/include/linux/dm-ioctl.h
index 3708455..0cb8eff 100644
--- a/include/linux/dm-ioctl.h
+++ b/include/linux/dm-ioctl.h
@@ -267,9 +267,9 @@
#define DM_DEV_SET_GEOMETRY _IOWR(DM_IOCTL, DM_DEV_SET_GEOMETRY_CMD, struct dm_ioctl)
#define DM_VERSION_MAJOR 4
-#define DM_VERSION_MINOR 20
+#define DM_VERSION_MINOR 21
#define DM_VERSION_PATCHLEVEL 0
-#define DM_VERSION_EXTRA "-ioctl (2011-02-02)"
+#define DM_VERSION_EXTRA "-ioctl (2011-07-06)"
/* Status bits */
#define DM_READONLY_FLAG (1 << 0) /* In/Out */
diff --git a/include/linux/dm-kcopyd.h b/include/linux/dm-kcopyd.h
index 298d587..5e54458 100644
--- a/include/linux/dm-kcopyd.h
+++ b/include/linux/dm-kcopyd.h
@@ -42,5 +42,20 @@
unsigned num_dests, struct dm_io_region *dests,
unsigned flags, dm_kcopyd_notify_fn fn, void *context);
+/*
+ * Prepare a callback and submit it via the kcopyd thread.
+ *
+ * dm_kcopyd_prepare_callback allocates a callback structure and returns it.
+ * It must not be called from interrupt context.
+ * The returned value should be passed into dm_kcopyd_do_callback.
+ *
+ * dm_kcopyd_do_callback submits the callback.
+ * It may be called from interrupt context.
+ * The callback is issued from the kcopyd thread.
+ */
+void *dm_kcopyd_prepare_callback(struct dm_kcopyd_client *kc,
+ dm_kcopyd_notify_fn fn, void *context);
+void dm_kcopyd_do_callback(void *job, int read_err, unsigned long write_err);
+
#endif /* __KERNEL__ */
#endif /* _LINUX_DM_KCOPYD_H */
diff --git a/include/linux/efi.h b/include/linux/efi.h
index ec25726..2362a0b 100644
--- a/include/linux/efi.h
+++ b/include/linux/efi.h
@@ -19,6 +19,7 @@
#include <linux/rtc.h>
#include <linux/ioport.h>
#include <linux/pfn.h>
+#include <linux/pstore.h>
#include <asm/page.h>
#include <asm/system.h>
@@ -232,6 +233,9 @@
#define UV_SYSTEM_TABLE_GUID \
EFI_GUID( 0x3b13a7d4, 0x633e, 0x11dd, 0x93, 0xec, 0xda, 0x25, 0x56, 0xd8, 0x95, 0x93 )
+#define LINUX_EFI_CRASH_GUID \
+ EFI_GUID( 0xcfc8fc79, 0xbe2e, 0x4ddc, 0x97, 0xf0, 0x9f, 0x98, 0xbf, 0xe2, 0x98, 0xa0 )
+
typedef struct {
efi_guid_t guid;
unsigned long table;
@@ -458,6 +462,8 @@
struct kset *kset;
struct bin_attribute *new_var, *del_var;
const struct efivar_operations *ops;
+ struct efivar_entry *walk_entry;
+ struct pstore_info efi_pstore_info;
};
int register_efivars(struct efivars *efivars,
diff --git a/include/linux/fault-inject.h b/include/linux/fault-inject.h
index 3ff060a..c6f996f 100644
--- a/include/linux/fault-inject.h
+++ b/include/linux/fault-inject.h
@@ -25,10 +25,6 @@
unsigned long reject_end;
unsigned long count;
-
-#ifdef CONFIG_FAULT_INJECTION_DEBUG_FS
- struct dentry *dir;
-#endif
};
#define FAULT_ATTR_INITIALIZER { \
@@ -45,19 +41,15 @@
#ifdef CONFIG_FAULT_INJECTION_DEBUG_FS
-int init_fault_attr_dentries(struct fault_attr *attr, const char *name);
-void cleanup_fault_attr_dentries(struct fault_attr *attr);
+struct dentry *fault_create_debugfs_attr(const char *name,
+ struct dentry *parent, struct fault_attr *attr);
#else /* CONFIG_FAULT_INJECTION_DEBUG_FS */
-static inline int init_fault_attr_dentries(struct fault_attr *attr,
- const char *name)
+static inline struct dentry *fault_create_debugfs_attr(const char *name,
+ struct dentry *parent, struct fault_attr *attr)
{
- return -ENODEV;
-}
-
-static inline void cleanup_fault_attr_dentries(struct fault_attr *attr)
-{
+ return ERR_PTR(-ENODEV);
}
#endif /* CONFIG_FAULT_INJECTION_DEBUG_FS */
diff --git a/include/linux/fs.h b/include/linux/fs.h
index f23bcb7..178cdb4 100644
--- a/include/linux/fs.h
+++ b/include/linux/fs.h
@@ -738,22 +738,54 @@
struct posix_acl;
#define ACL_NOT_CACHED ((void *)(-1))
+#define IOP_FASTPERM 0x0001
+#define IOP_LOOKUP 0x0002
+#define IOP_NOFOLLOW 0x0004
+
+/*
+ * Keep mostly read-only and often accessed (especially for
+ * the RCU path lookup and 'stat' data) fields at the beginning
+ * of the 'struct inode'
+ */
struct inode {
- /* RCU path lookup touches following: */
umode_t i_mode;
+ unsigned short i_opflags;
uid_t i_uid;
gid_t i_gid;
+ unsigned int i_flags;
+
+#ifdef CONFIG_FS_POSIX_ACL
+ struct posix_acl *i_acl;
+ struct posix_acl *i_default_acl;
+#endif
+
const struct inode_operations *i_op;
struct super_block *i_sb;
+ struct address_space *i_mapping;
- spinlock_t i_lock; /* i_blocks, i_bytes, maybe i_size */
- unsigned int i_flags;
- unsigned long i_state;
#ifdef CONFIG_SECURITY
void *i_security;
#endif
- struct mutex i_mutex;
+ /* Stat data, not accessed from path walking */
+ unsigned long i_ino;
+ unsigned int i_nlink;
+ dev_t i_rdev;
+ loff_t i_size;
+ struct timespec i_atime;
+ struct timespec i_mtime;
+ struct timespec i_ctime;
+ unsigned int i_blkbits;
+ blkcnt_t i_blocks;
+
+#ifdef __NEED_I_SIZE_ORDERED
+ seqcount_t i_size_seqcount;
+#endif
+
+ /* Misc */
+ unsigned long i_state;
+ spinlock_t i_lock; /* i_blocks, i_bytes, maybe i_size */
+ struct mutex i_mutex;
unsigned long dirtied_when; /* jiffies of first dirtying */
@@ -765,25 +797,12 @@
struct list_head i_dentry;
struct rcu_head i_rcu;
};
- unsigned long i_ino;
atomic_t i_count;
- unsigned int i_nlink;
- dev_t i_rdev;
- unsigned int i_blkbits;
u64 i_version;
- loff_t i_size;
-#ifdef __NEED_I_SIZE_ORDERED
- seqcount_t i_size_seqcount;
-#endif
- struct timespec i_atime;
- struct timespec i_mtime;
- struct timespec i_ctime;
- blkcnt_t i_blocks;
unsigned short i_bytes;
atomic_t i_dio_count;
const struct file_operations *i_fop; /* former ->i_op->default_file_ops */
struct file_lock *i_flock;
- struct address_space *i_mapping;
struct address_space i_data;
#ifdef CONFIG_QUOTA
struct dquot *i_dquot[MAXQUOTAS];
@@ -806,10 +825,6 @@
atomic_t i_readcount; /* struct files open RO */
#endif
atomic_t i_writecount;
-#ifdef CONFIG_FS_POSIX_ACL
- struct posix_acl *i_acl;
- struct posix_acl *i_default_acl;
-#endif
void *i_private; /* fs or device private pointer */
};
@@ -2317,11 +2332,18 @@
extern int file_remove_suid(struct file *);
extern void __insert_inode_hash(struct inode *, unsigned long hashval);
-extern void remove_inode_hash(struct inode *);
static inline void insert_inode_hash(struct inode *inode)
{
__insert_inode_hash(inode, inode->i_ino);
}
+
+extern void __remove_inode_hash(struct inode *);
+static inline void remove_inode_hash(struct inode *inode)
+{
+ if (!inode_unhashed(inode))
+ __remove_inode_hash(inode);
+}
+
extern void inode_sb_list_add(struct inode *inode);
#ifdef CONFIG_BLOCK
diff --git a/include/linux/genalloc.h b/include/linux/genalloc.h
index 5bbebda..5e98eeb 100644
--- a/include/linux/genalloc.h
+++ b/include/linux/genalloc.h
@@ -1,8 +1,26 @@
/*
- * Basic general purpose allocator for managing special purpose memory
- * not managed by the regular kmalloc/kfree interface.
- * Uses for this includes on-device special memory, uncached memory
- * etc.
+ * Basic general purpose allocator for managing special purpose
+ * memory, for example, memory that is not managed by the regular
+ * kmalloc/kfree interface. Uses for this includes on-device special
+ * memory, uncached memory etc.
+ *
+ * It is safe to use the allocator in NMI handlers and other special
+ * unblockable contexts that could otherwise deadlock on locks. This
+ * is implemented by using atomic operations and retries on any
+ * conflicts. The disadvantage is that there may be livelocks in
+ * extreme cases. For better scalability, one allocator can be used
+ * for each CPU.
+ *
+ * The lockless operation only works if there is enough memory
+ * available. If new memory is added to the pool a lock has to be
+ * still taken. So any user relying on locklessness has to ensure
+ * that sufficient memory is preallocated.
+ *
+ * The basic atomic operation of this allocator is cmpxchg on long.
+ * On architectures that don't have NMI-safe cmpxchg implementation,
+ * the allocator can NOT be used in NMI handler. So code uses the
+ * allocator in NMI handler should depend on
+ * CONFIG_ARCH_HAVE_NMI_SAFE_CMPXCHG.
*
* This source code is licensed under the GNU General Public License,
* Version 2. See the file COPYING for more details.
@@ -15,7 +33,7 @@
* General purpose special memory pool descriptor.
*/
struct gen_pool {
- rwlock_t lock;
+ spinlock_t lock;
struct list_head chunks; /* list of chunks in this pool */
int min_alloc_order; /* minimum allocation order */
};
@@ -24,8 +42,8 @@
* General purpose special memory pool chunk descriptor.
*/
struct gen_pool_chunk {
- spinlock_t lock;
struct list_head next_chunk; /* next chunk in pool */
+ atomic_t avail;
phys_addr_t phys_addr; /* physical starting address of memory chunk */
unsigned long start_addr; /* starting address of memory chunk */
unsigned long end_addr; /* ending address of memory chunk */
@@ -56,4 +74,8 @@
extern void gen_pool_destroy(struct gen_pool *);
extern unsigned long gen_pool_alloc(struct gen_pool *, size_t);
extern void gen_pool_free(struct gen_pool *, unsigned long, size_t);
+extern void gen_pool_for_each_chunk(struct gen_pool *,
+ void (*)(struct gen_pool *, struct gen_pool_chunk *, void *), void *);
+extern size_t gen_pool_avail(struct gen_pool *);
+extern size_t gen_pool_size(struct gen_pool *);
#endif /* __GENALLOC_H__ */
diff --git a/include/linux/gfp.h b/include/linux/gfp.h
index cb40892..3a76faf 100644
--- a/include/linux/gfp.h
+++ b/include/linux/gfp.h
@@ -92,7 +92,7 @@
*/
#define __GFP_NOTRACK_FALSE_POSITIVE (__GFP_NOTRACK)
-#define __GFP_BITS_SHIFT 23 /* Room for 23 __GFP_FOO bits */
+#define __GFP_BITS_SHIFT 24 /* Room for N __GFP_FOO bits */
#define __GFP_BITS_MASK ((__force gfp_t)((1 << __GFP_BITS_SHIFT) - 1))
/* This equals 0, but use constants in case they ever change */
diff --git a/include/linux/hash.h b/include/linux/hash.h
index 06d25c1..b80506b 100644
--- a/include/linux/hash.h
+++ b/include/linux/hash.h
@@ -63,7 +63,7 @@
return hash >> (32 - bits);
}
-static inline unsigned long hash_ptr(void *ptr, unsigned int bits)
+static inline unsigned long hash_ptr(const void *ptr, unsigned int bits)
{
return hash_long((unsigned long)ptr, bits);
}
diff --git a/include/linux/idr.h b/include/linux/idr.h
index 13a801f..255491c 100644
--- a/include/linux/idr.h
+++ b/include/linux/idr.h
@@ -146,6 +146,10 @@
void ida_destroy(struct ida *ida);
void ida_init(struct ida *ida);
+int ida_simple_get(struct ida *ida, unsigned int start, unsigned int end,
+ gfp_t gfp_mask);
+void ida_simple_remove(struct ida *ida, unsigned int id);
+
void __init idr_init_cache(void);
#endif /* __IDR_H__ */
diff --git a/include/linux/input.h b/include/linux/input.h
index 068784e..a637e78 100644
--- a/include/linux/input.h
+++ b/include/linux/input.h
@@ -438,6 +438,8 @@
#define KEY_WIMAX 246
#define KEY_RFKILL 247 /* Key that controls all radios */
+#define KEY_MICMUTE 248 /* Mute / unmute the microphone */
+
/* Code 255 is reserved for special needs of AT keyboard driver */
#define BTN_MISC 0x100
diff --git a/include/linux/ioport.h b/include/linux/ioport.h
index c2ebfe6..9d57a71 100644
--- a/include/linux/ioport.h
+++ b/include/linux/ioport.h
@@ -162,6 +162,7 @@
resource_size_t,
resource_size_t),
void *alignf_data);
+struct resource *lookup_resource(struct resource *root, resource_size_t start);
int adjust_resource(struct resource *res, resource_size_t start,
resource_size_t size);
resource_size_t resource_alignment(struct resource *res);
diff --git a/include/linux/irq.h b/include/linux/irq.h
index 87a06f3..5951730 100644
--- a/include/linux/irq.h
+++ b/include/linux/irq.h
@@ -23,6 +23,7 @@
#include <linux/errno.h>
#include <linux/topology.h>
#include <linux/wait.h>
+#include <linux/module.h>
#include <asm/irq.h>
#include <asm/ptrace.h>
@@ -547,7 +548,15 @@
return d->msi_desc;
}
-int irq_alloc_descs(int irq, unsigned int from, unsigned int cnt, int node);
+int __irq_alloc_descs(int irq, unsigned int from, unsigned int cnt, int node,
+ struct module *owner);
+
+static inline int irq_alloc_descs(int irq, unsigned int from, unsigned int cnt,
+ int node)
+{
+ return __irq_alloc_descs(irq, from, cnt, node, THIS_MODULE);
+}
+
void irq_free_descs(unsigned int irq, unsigned int cnt);
int irq_reserve_irqs(unsigned int from, unsigned int cnt);
diff --git a/include/linux/irqdesc.h b/include/linux/irqdesc.h
index 2d921b3..150134a 100644
--- a/include/linux/irqdesc.h
+++ b/include/linux/irqdesc.h
@@ -66,6 +66,7 @@
#ifdef CONFIG_PROC_FS
struct proc_dir_entry *dir;
#endif
+ struct module *owner;
const char *name;
} ____cacheline_internodealigned_in_smp;
diff --git a/include/linux/jbd2.h b/include/linux/jbd2.h
index d087c2e..38f307b 100644
--- a/include/linux/jbd2.h
+++ b/include/linux/jbd2.h
@@ -1329,12 +1329,6 @@
#define BUFFER_TRACE2(bh, bh2, info) do {} while (0)
#define JBUFFER_TRACE(jh, info) do {} while (0)
-/*
- * jbd2_dev_to_name is a utility function used by the jbd2 and ext4
- * tracing infrastructure to map a dev_t to a device name.
- */
-extern const char *jbd2_dev_to_name(dev_t device);
-
#endif /* __KERNEL__ */
#endif /* _LINUX_JBD2_H */
diff --git a/include/linux/llist.h b/include/linux/llist.h
new file mode 100644
index 0000000..aa0c8b5
--- /dev/null
+++ b/include/linux/llist.h
@@ -0,0 +1,126 @@
+#ifndef LLIST_H
+#define LLIST_H
+/*
+ * Lock-less NULL terminated single linked list
+ *
+ * If there are multiple producers and multiple consumers, llist_add
+ * can be used in producers and llist_del_all can be used in
+ * consumers. They can work simultaneously without lock. But
+ * llist_del_first can not be used here. Because llist_del_first
+ * depends on list->first->next does not changed if list->first is not
+ * changed during its operation, but llist_del_first, llist_add,
+ * llist_add (or llist_del_all, llist_add, llist_add) sequence in
+ * another consumer may violate that.
+ *
+ * If there are multiple producers and one consumer, llist_add can be
+ * used in producers and llist_del_all or llist_del_first can be used
+ * in the consumer.
+ *
+ * This can be summarized as follow:
+ *
+ * | add | del_first | del_all
+ * add | - | - | -
+ * del_first | | L | L
+ * del_all | | | -
+ *
+ * Where "-" stands for no lock is needed, while "L" stands for lock
+ * is needed.
+ *
+ * The list entries deleted via llist_del_all can be traversed with
+ * traversing function such as llist_for_each etc. But the list
+ * entries can not be traversed safely before deleted from the list.
+ * The order of deleted entries is from the newest to the oldest added
+ * one. If you want to traverse from the oldest to the newest, you
+ * must reverse the order by yourself before traversing.
+ *
+ * The basic atomic operation of this list is cmpxchg on long. On
+ * architectures that don't have NMI-safe cmpxchg implementation, the
+ * list can NOT be used in NMI handler. So code uses the list in NMI
+ * handler should depend on CONFIG_ARCH_HAVE_NMI_SAFE_CMPXCHG.
+ */
+
+struct llist_head {
+ struct llist_node *first;
+};
+
+struct llist_node {
+ struct llist_node *next;
+};
+
+#define LLIST_HEAD_INIT(name) { NULL }
+#define LLIST_HEAD(name) struct llist_head name = LLIST_HEAD_INIT(name)
+
+/**
+ * init_llist_head - initialize lock-less list head
+ * @head: the head for your lock-less list
+ */
+static inline void init_llist_head(struct llist_head *list)
+{
+ list->first = NULL;
+}
+
+/**
+ * llist_entry - get the struct of this entry
+ * @ptr: the &struct llist_node pointer.
+ * @type: the type of the struct this is embedded in.
+ * @member: the name of the llist_node within the struct.
+ */
+#define llist_entry(ptr, type, member) \
+ container_of(ptr, type, member)
+
+/**
+ * llist_for_each - iterate over some deleted entries of a lock-less list
+ * @pos: the &struct llist_node to use as a loop cursor
+ * @node: the first entry of deleted list entries
+ *
+ * In general, some entries of the lock-less list can be traversed
+ * safely only after being deleted from list, so start with an entry
+ * instead of list head.
+ *
+ * If being used on entries deleted from lock-less list directly, the
+ * traverse order is from the newest to the oldest added entry. If
+ * you want to traverse from the oldest to the newest, you must
+ * reverse the order by yourself before traversing.
+ */
+#define llist_for_each(pos, node) \
+ for ((pos) = (node); pos; (pos) = (pos)->next)
+
+/**
+ * llist_for_each_entry - iterate over some deleted entries of lock-less list of given type
+ * @pos: the type * to use as a loop cursor.
+ * @node: the fist entry of deleted list entries.
+ * @member: the name of the llist_node with the struct.
+ *
+ * In general, some entries of the lock-less list can be traversed
+ * safely only after being removed from list, so start with an entry
+ * instead of list head.
+ *
+ * If being used on entries deleted from lock-less list directly, the
+ * traverse order is from the newest to the oldest added entry. If
+ * you want to traverse from the oldest to the newest, you must
+ * reverse the order by yourself before traversing.
+ */
+#define llist_for_each_entry(pos, node, member) \
+ for ((pos) = llist_entry((node), typeof(*(pos)), member); \
+ &(pos)->member != NULL; \
+ (pos) = llist_entry((pos)->member.next, typeof(*(pos)), member))
+
+/**
+ * llist_empty - tests whether a lock-less list is empty
+ * @head: the list to test
+ *
+ * Not guaranteed to be accurate or up to date. Just a quick way to
+ * test whether the list is empty without deleting something from the
+ * list.
+ */
+static inline int llist_empty(const struct llist_head *head)
+{
+ return ACCESS_ONCE(head->first) == NULL;
+}
+
+void llist_add(struct llist_node *new, struct llist_head *head);
+void llist_add_batch(struct llist_node *new_first, struct llist_node *new_last,
+ struct llist_head *head);
+struct llist_node *llist_del_first(struct llist_head *head);
+struct llist_node *llist_del_all(struct llist_head *head);
+#endif /* LLIST_H */
diff --git a/include/linux/memcontrol.h b/include/linux/memcontrol.h
index b966007..3b535db 100644
--- a/include/linux/memcontrol.h
+++ b/include/linux/memcontrol.h
@@ -86,8 +86,6 @@
extern void mem_cgroup_uncharge_page(struct page *page);
extern void mem_cgroup_uncharge_cache_page(struct page *page);
-extern int mem_cgroup_shmem_charge_fallback(struct page *page,
- struct mm_struct *mm, gfp_t gfp_mask);
extern void mem_cgroup_out_of_memory(struct mem_cgroup *mem, gfp_t gfp_mask);
int task_in_mem_cgroup(struct task_struct *task, const struct mem_cgroup *mem);
@@ -225,12 +223,6 @@
{
}
-static inline int mem_cgroup_shmem_charge_fallback(struct page *page,
- struct mm_struct *mm, gfp_t gfp_mask)
-{
- return 0;
-}
-
static inline void mem_cgroup_add_lru_list(struct page *page, int lru)
{
}
diff --git a/include/linux/mfd/aat2870.h b/include/linux/mfd/aat2870.h
new file mode 100644
index 0000000..f7316c2
--- /dev/null
+++ b/include/linux/mfd/aat2870.h
@@ -0,0 +1,181 @@
+/*
+ * linux/include/linux/mfd/aat2870.h
+ *
+ * Copyright (c) 2011, NVIDIA Corporation.
+ * Author: Jin Park <jinyoungp@nvidia.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., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ */
+
+#ifndef __LINUX_MFD_AAT2870_H
+#define __LINUX_MFD_AAT2870_H
+
+#include <linux/debugfs.h>
+#include <linux/i2c.h>
+
+/* Register offsets */
+#define AAT2870_BL_CH_EN 0x00
+#define AAT2870_BLM 0x01
+#define AAT2870_BLS 0x02
+#define AAT2870_BL1 0x03
+#define AAT2870_BL2 0x04
+#define AAT2870_BL3 0x05
+#define AAT2870_BL4 0x06
+#define AAT2870_BL5 0x07
+#define AAT2870_BL6 0x08
+#define AAT2870_BL7 0x09
+#define AAT2870_BL8 0x0A
+#define AAT2870_FLR 0x0B
+#define AAT2870_FM 0x0C
+#define AAT2870_FS 0x0D
+#define AAT2870_ALS_CFG0 0x0E
+#define AAT2870_ALS_CFG1 0x0F
+#define AAT2870_ALS_CFG2 0x10
+#define AAT2870_AMB 0x11
+#define AAT2870_ALS0 0x12
+#define AAT2870_ALS1 0x13
+#define AAT2870_ALS2 0x14
+#define AAT2870_ALS3 0x15
+#define AAT2870_ALS4 0x16
+#define AAT2870_ALS5 0x17
+#define AAT2870_ALS6 0x18
+#define AAT2870_ALS7 0x19
+#define AAT2870_ALS8 0x1A
+#define AAT2870_ALS9 0x1B
+#define AAT2870_ALSA 0x1C
+#define AAT2870_ALSB 0x1D
+#define AAT2870_ALSC 0x1E
+#define AAT2870_ALSD 0x1F
+#define AAT2870_ALSE 0x20
+#define AAT2870_ALSF 0x21
+#define AAT2870_SUB_SET 0x22
+#define AAT2870_SUB_CTRL 0x23
+#define AAT2870_LDO_AB 0x24
+#define AAT2870_LDO_CD 0x25
+#define AAT2870_LDO_EN 0x26
+#define AAT2870_REG_NUM 0x27
+
+/* Device IDs */
+enum aat2870_id {
+ AAT2870_ID_BL,
+ AAT2870_ID_LDOA,
+ AAT2870_ID_LDOB,
+ AAT2870_ID_LDOC,
+ AAT2870_ID_LDOD
+};
+
+/* Backlight channels */
+#define AAT2870_BL_CH1 0x01
+#define AAT2870_BL_CH2 0x02
+#define AAT2870_BL_CH3 0x04
+#define AAT2870_BL_CH4 0x08
+#define AAT2870_BL_CH5 0x10
+#define AAT2870_BL_CH6 0x20
+#define AAT2870_BL_CH7 0x40
+#define AAT2870_BL_CH8 0x80
+#define AAT2870_BL_CH_ALL 0xFF
+
+/* Backlight current magnitude (mA) */
+enum aat2870_current {
+ AAT2870_CURRENT_0_45 = 1,
+ AAT2870_CURRENT_0_90,
+ AAT2870_CURRENT_1_80,
+ AAT2870_CURRENT_2_70,
+ AAT2870_CURRENT_3_60,
+ AAT2870_CURRENT_4_50,
+ AAT2870_CURRENT_5_40,
+ AAT2870_CURRENT_6_30,
+ AAT2870_CURRENT_7_20,
+ AAT2870_CURRENT_8_10,
+ AAT2870_CURRENT_9_00,
+ AAT2870_CURRENT_9_90,
+ AAT2870_CURRENT_10_8,
+ AAT2870_CURRENT_11_7,
+ AAT2870_CURRENT_12_6,
+ AAT2870_CURRENT_13_5,
+ AAT2870_CURRENT_14_4,
+ AAT2870_CURRENT_15_3,
+ AAT2870_CURRENT_16_2,
+ AAT2870_CURRENT_17_1,
+ AAT2870_CURRENT_18_0,
+ AAT2870_CURRENT_18_9,
+ AAT2870_CURRENT_19_8,
+ AAT2870_CURRENT_20_7,
+ AAT2870_CURRENT_21_6,
+ AAT2870_CURRENT_22_5,
+ AAT2870_CURRENT_23_4,
+ AAT2870_CURRENT_24_3,
+ AAT2870_CURRENT_25_2,
+ AAT2870_CURRENT_26_1,
+ AAT2870_CURRENT_27_0,
+ AAT2870_CURRENT_27_9
+};
+
+struct aat2870_register {
+ bool readable;
+ bool writeable;
+ u8 value;
+};
+
+struct aat2870_data {
+ struct device *dev;
+ struct i2c_client *client;
+
+ struct mutex io_lock;
+ struct aat2870_register *reg_cache; /* register cache */
+ int en_pin; /* enable GPIO pin (if < 0, ignore this value) */
+ bool is_enable;
+
+ /* init and uninit for platform specified */
+ int (*init)(struct aat2870_data *aat2870);
+ void (*uninit)(struct aat2870_data *aat2870);
+
+ /* i2c io funcntions */
+ int (*read)(struct aat2870_data *aat2870, u8 addr, u8 *val);
+ int (*write)(struct aat2870_data *aat2870, u8 addr, u8 val);
+ int (*update)(struct aat2870_data *aat2870, u8 addr, u8 mask, u8 val);
+
+ /* for debugfs */
+ struct dentry *dentry_root;
+ struct dentry *dentry_reg;
+};
+
+struct aat2870_subdev_info {
+ int id;
+ const char *name;
+ void *platform_data;
+};
+
+struct aat2870_platform_data {
+ int en_pin; /* enable GPIO pin (if < 0, ignore this value) */
+
+ struct aat2870_subdev_info *subdevs;
+ int num_subdevs;
+
+ /* init and uninit for platform specified */
+ int (*init)(struct aat2870_data *aat2870);
+ void (*uninit)(struct aat2870_data *aat2870);
+};
+
+struct aat2870_bl_platform_data {
+ /* backlight channels, default is AAT2870_BL_CH_ALL */
+ int channels;
+ /* backlight current magnitude, default is AAT2870_CURRENT_27_9 */
+ int max_current;
+ /* maximum brightness, default is 255 */
+ int max_brightness;
+};
+
+#endif /* __LINUX_MFD_AAT2870_H */
diff --git a/include/linux/mfd/ab8500.h b/include/linux/mfd/ab8500.h
index b318430..838c6b4 100644
--- a/include/linux/mfd/ab8500.h
+++ b/include/linux/mfd/ab8500.h
@@ -28,6 +28,7 @@
#define AB8500_INTERRUPT 0xE
#define AB8500_RTC 0xF
#define AB8500_MISC 0x10
+#define AB8500_DEVELOPMENT 0x11
#define AB8500_DEBUG 0x12
#define AB8500_PROD_TEST 0x13
#define AB8500_OTP_EMUL 0x15
@@ -74,13 +75,6 @@
#define AB8500_INT_ACC_DETECT_21DB_F 37
#define AB8500_INT_ACC_DETECT_21DB_R 38
#define AB8500_INT_GP_SW_ADC_CONV_END 39
-#define AB8500_INT_ACC_DETECT_1DB_F 33
-#define AB8500_INT_ACC_DETECT_1DB_R 34
-#define AB8500_INT_ACC_DETECT_22DB_F 35
-#define AB8500_INT_ACC_DETECT_22DB_R 36
-#define AB8500_INT_ACC_DETECT_21DB_F 37
-#define AB8500_INT_ACC_DETECT_21DB_R 38
-#define AB8500_INT_GP_SW_ADC_CONV_END 39
#define AB8500_INT_GPIO6R 40
#define AB8500_INT_GPIO7R 41
#define AB8500_INT_GPIO8R 42
diff --git a/include/linux/mfd/stmpe.h b/include/linux/mfd/stmpe.h
index e762c27..be1af7c 100644
--- a/include/linux/mfd/stmpe.h
+++ b/include/linux/mfd/stmpe.h
@@ -57,6 +57,7 @@
* @irq_lock: IRQ bus lock
* @dev: device, mostly for dev_dbg()
* @i2c: i2c client
+ * @partnum: part number
* @variant: the detected STMPE model number
* @regs: list of addresses of registers which are at different addresses on
* different variants. Indexed by one of STMPE_IDX_*.
@@ -121,6 +122,8 @@
* @norequest_mask: bitmask specifying which GPIOs should _not_ be
* requestable due to different usage (e.g. touch, keypad)
* STMPE_GPIO_NOREQ_* macros can be used here.
+ * @setup: board specific setup callback.
+ * @remove: board specific remove callback
*/
struct stmpe_gpio_platform_data {
int gpio_base;
diff --git a/include/linux/mfd/tps65910.h b/include/linux/mfd/tps65910.h
index 73572c6..82b4c88 100644
--- a/include/linux/mfd/tps65910.h
+++ b/include/linux/mfd/tps65910.h
@@ -791,6 +791,7 @@
void tps65910_gpio_init(struct tps65910 *tps65910, int gpio_base);
int tps65910_irq_init(struct tps65910 *tps65910, int irq,
struct tps65910_platform_data *pdata);
+int tps65910_irq_exit(struct tps65910 *tps65910);
static inline int tps65910_chip_id(struct tps65910 *tps65910)
{
diff --git a/include/linux/mfd/tps65912.h b/include/linux/mfd/tps65912.h
new file mode 100644
index 0000000..aaceab4
--- /dev/null
+++ b/include/linux/mfd/tps65912.h
@@ -0,0 +1,327 @@
+/*
+ * tps65912.h -- TI TPS6591x
+ *
+ * Copyright 2011 Texas Instruments Inc.
+ *
+ * Author: Margarita Olaya <magi@slimlogic.co.uk>
+ *
+ * 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_MFD_TPS65912_H
+#define __LINUX_MFD_TPS65912_H
+
+/* TPS regulator type list */
+#define REGULATOR_LDO 0
+#define REGULATOR_DCDC 1
+
+/*
+ * List of registers for TPS65912
+ */
+
+#define TPS65912_DCDC1_CTRL 0x00
+#define TPS65912_DCDC2_CTRL 0x01
+#define TPS65912_DCDC3_CTRL 0x02
+#define TPS65912_DCDC4_CTRL 0x03
+#define TPS65912_DCDC1_OP 0x04
+#define TPS65912_DCDC1_AVS 0x05
+#define TPS65912_DCDC1_LIMIT 0x06
+#define TPS65912_DCDC2_OP 0x07
+#define TPS65912_DCDC2_AVS 0x08
+#define TPS65912_DCDC2_LIMIT 0x09
+#define TPS65912_DCDC3_OP 0x0A
+#define TPS65912_DCDC3_AVS 0x0B
+#define TPS65912_DCDC3_LIMIT 0x0C
+#define TPS65912_DCDC4_OP 0x0D
+#define TPS65912_DCDC4_AVS 0x0E
+#define TPS65912_DCDC4_LIMIT 0x0F
+#define TPS65912_LDO1_OP 0x10
+#define TPS65912_LDO1_AVS 0x11
+#define TPS65912_LDO1_LIMIT 0x12
+#define TPS65912_LDO2_OP 0x13
+#define TPS65912_LDO2_AVS 0x14
+#define TPS65912_LDO2_LIMIT 0x15
+#define TPS65912_LDO3_OP 0x16
+#define TPS65912_LDO3_AVS 0x17
+#define TPS65912_LDO3_LIMIT 0x18
+#define TPS65912_LDO4_OP 0x19
+#define TPS65912_LDO4_AVS 0x1A
+#define TPS65912_LDO4_LIMIT 0x1B
+#define TPS65912_LDO5 0x1C
+#define TPS65912_LDO6 0x1D
+#define TPS65912_LDO7 0x1E
+#define TPS65912_LDO8 0x1F
+#define TPS65912_LDO9 0x20
+#define TPS65912_LDO10 0x21
+#define TPS65912_THRM 0x22
+#define TPS65912_CLK32OUT 0x23
+#define TPS65912_DEVCTRL 0x24
+#define TPS65912_DEVCTRL2 0x25
+#define TPS65912_I2C_SPI_CFG 0x26
+#define TPS65912_KEEP_ON 0x27
+#define TPS65912_KEEP_ON2 0x28
+#define TPS65912_SET_OFF1 0x29
+#define TPS65912_SET_OFF2 0x2A
+#define TPS65912_DEF_VOLT 0x2B
+#define TPS65912_DEF_VOLT_MAPPING 0x2C
+#define TPS65912_DISCHARGE 0x2D
+#define TPS65912_DISCHARGE2 0x2E
+#define TPS65912_EN1_SET1 0x2F
+#define TPS65912_EN1_SET2 0x30
+#define TPS65912_EN2_SET1 0x31
+#define TPS65912_EN2_SET2 0x32
+#define TPS65912_EN3_SET1 0x33
+#define TPS65912_EN3_SET2 0x34
+#define TPS65912_EN4_SET1 0x35
+#define TPS65912_EN4_SET2 0x36
+#define TPS65912_PGOOD 0x37
+#define TPS65912_PGOOD2 0x38
+#define TPS65912_INT_STS 0x39
+#define TPS65912_INT_MSK 0x3A
+#define TPS65912_INT_STS2 0x3B
+#define TPS65912_INT_MSK2 0x3C
+#define TPS65912_INT_STS3 0x3D
+#define TPS65912_INT_MSK3 0x3E
+#define TPS65912_INT_STS4 0x3F
+#define TPS65912_INT_MSK4 0x40
+#define TPS65912_GPIO1 0x41
+#define TPS65912_GPIO2 0x42
+#define TPS65912_GPIO3 0x43
+#define TPS65912_GPIO4 0x44
+#define TPS65912_GPIO5 0x45
+#define TPS65912_VMON 0x46
+#define TPS65912_LEDA_CTRL1 0x47
+#define TPS65912_LEDA_CTRL2 0x48
+#define TPS65912_LEDA_CTRL3 0x49
+#define TPS65912_LEDA_CTRL4 0x4A
+#define TPS65912_LEDA_CTRL5 0x4B
+#define TPS65912_LEDA_CTRL6 0x4C
+#define TPS65912_LEDA_CTRL7 0x4D
+#define TPS65912_LEDA_CTRL8 0x4E
+#define TPS65912_LEDB_CTRL1 0x4F
+#define TPS65912_LEDB_CTRL2 0x50
+#define TPS65912_LEDB_CTRL3 0x51
+#define TPS65912_LEDB_CTRL4 0x52
+#define TPS65912_LEDB_CTRL5 0x53
+#define TPS65912_LEDB_CTRL6 0x54
+#define TPS65912_LEDB_CTRL7 0x55
+#define TPS65912_LEDB_CTRL8 0x56
+#define TPS65912_LEDC_CTRL1 0x57
+#define TPS65912_LEDC_CTRL2 0x58
+#define TPS65912_LEDC_CTRL3 0x59
+#define TPS65912_LEDC_CTRL4 0x5A
+#define TPS65912_LEDC_CTRL5 0x5B
+#define TPS65912_LEDC_CTRL6 0x5C
+#define TPS65912_LEDC_CTRL7 0x5D
+#define TPS65912_LEDC_CTRL8 0x5E
+#define TPS65912_LED_RAMP_UP_TIME 0x5F
+#define TPS65912_LED_RAMP_DOWN_TIME 0x60
+#define TPS65912_LED_SEQ_EN 0x61
+#define TPS65912_LOADSWITCH 0x62
+#define TPS65912_SPARE 0x63
+#define TPS65912_VERNUM 0x64
+#define TPS6591X_MAX_REGISTER 0x64
+
+/* IRQ Definitions */
+#define TPS65912_IRQ_PWRHOLD_F 0
+#define TPS65912_IRQ_VMON 1
+#define TPS65912_IRQ_PWRON 2
+#define TPS65912_IRQ_PWRON_LP 3
+#define TPS65912_IRQ_PWRHOLD_R 4
+#define TPS65912_IRQ_HOTDIE 5
+#define TPS65912_IRQ_GPIO1_R 6
+#define TPS65912_IRQ_GPIO1_F 7
+#define TPS65912_IRQ_GPIO2_R 8
+#define TPS65912_IRQ_GPIO2_F 9
+#define TPS65912_IRQ_GPIO3_R 10
+#define TPS65912_IRQ_GPIO3_F 11
+#define TPS65912_IRQ_GPIO4_R 12
+#define TPS65912_IRQ_GPIO4_F 13
+#define TPS65912_IRQ_GPIO5_R 14
+#define TPS65912_IRQ_GPIO5_F 15
+#define TPS65912_IRQ_PGOOD_DCDC1 16
+#define TPS65912_IRQ_PGOOD_DCDC2 17
+#define TPS65912_IRQ_PGOOD_DCDC3 18
+#define TPS65912_IRQ_PGOOD_DCDC4 19
+#define TPS65912_IRQ_PGOOD_LDO1 20
+#define TPS65912_IRQ_PGOOD_LDO2 21
+#define TPS65912_IRQ_PGOOD_LDO3 22
+#define TPS65912_IRQ_PGOOD_LDO4 23
+#define TPS65912_IRQ_PGOOD_LDO5 24
+#define TPS65912_IRQ_PGOOD_LDO6 25
+#define TPS65912_IRQ_PGOOD_LDO7 26
+#define TPS65912_IRQ_PGOOD_LD08 27
+#define TPS65912_IRQ_PGOOD_LDO9 28
+#define TPS65912_IRQ_PGOOD_LDO10 29
+
+#define TPS65912_NUM_IRQ 30
+
+/* GPIO 1 and 2 Register Definitions */
+#define GPIO_SLEEP_MASK 0x80
+#define GPIO_SLEEP_SHIFT 7
+#define GPIO_DEB_MASK 0x10
+#define GPIO_DEB_SHIFT 4
+#define GPIO_CFG_MASK 0x04
+#define GPIO_CFG_SHIFT 2
+#define GPIO_STS_MASK 0x02
+#define GPIO_STS_SHIFT 1
+#define GPIO_SET_MASK 0x01
+#define GPIO_SET_SHIFT 0
+
+/* GPIO 3 Register Definitions */
+#define GPIO3_SLEEP_MASK 0x80
+#define GPIO3_SLEEP_SHIFT 7
+#define GPIO3_SEL_MASK 0x40
+#define GPIO3_SEL_SHIFT 6
+#define GPIO3_ODEN_MASK 0x20
+#define GPIO3_ODEN_SHIFT 5
+#define GPIO3_DEB_MASK 0x10
+#define GPIO3_DEB_SHIFT 4
+#define GPIO3_PDEN_MASK 0x08
+#define GPIO3_PDEN_SHIFT 3
+#define GPIO3_CFG_MASK 0x04
+#define GPIO3_CFG_SHIFT 2
+#define GPIO3_STS_MASK 0x02
+#define GPIO3_STS_SHIFT 1
+#define GPIO3_SET_MASK 0x01
+#define GPIO3_SET_SHIFT 0
+
+/* GPIO 4 Register Definitions */
+#define GPIO4_SLEEP_MASK 0x80
+#define GPIO4_SLEEP_SHIFT 7
+#define GPIO4_SEL_MASK 0x40
+#define GPIO4_SEL_SHIFT 6
+#define GPIO4_ODEN_MASK 0x20
+#define GPIO4_ODEN_SHIFT 5
+#define GPIO4_DEB_MASK 0x10
+#define GPIO4_DEB_SHIFT 4
+#define GPIO4_PDEN_MASK 0x08
+#define GPIO4_PDEN_SHIFT 3
+#define GPIO4_CFG_MASK 0x04
+#define GPIO4_CFG_SHIFT 2
+#define GPIO4_STS_MASK 0x02
+#define GPIO4_STS_SHIFT 1
+#define GPIO4_SET_MASK 0x01
+#define GPIO4_SET_SHIFT 0
+
+/* Register THERM (0x80) register.RegisterDescription */
+#define THERM_THERM_HD_MASK 0x20
+#define THERM_THERM_HD_SHIFT 5
+#define THERM_THERM_TS_MASK 0x10
+#define THERM_THERM_TS_SHIFT 4
+#define THERM_THERM_HDSEL_MASK 0x0C
+#define THERM_THERM_HDSEL_SHIFT 2
+#define THERM_RSVD1_MASK 0x02
+#define THERM_RSVD1_SHIFT 1
+#define THERM_THERM_STATE_MASK 0x01
+#define THERM_THERM_STATE_SHIFT 0
+
+/* Register DCDCCTRL1 register.RegisterDescription */
+#define DCDCCTRL_VCON_ENABLE_MASK 0x80
+#define DCDCCTRL_VCON_ENABLE_SHIFT 7
+#define DCDCCTRL_VCON_RANGE1_MASK 0x40
+#define DCDCCTRL_VCON_RANGE1_SHIFT 6
+#define DCDCCTRL_VCON_RANGE0_MASK 0x20
+#define DCDCCTRL_VCON_RANGE0_SHIFT 5
+#define DCDCCTRL_TSTEP2_MASK 0x10
+#define DCDCCTRL_TSTEP2_SHIFT 4
+#define DCDCCTRL_TSTEP1_MASK 0x08
+#define DCDCCTRL_TSTEP1_SHIFT 3
+#define DCDCCTRL_TSTEP0_MASK 0x04
+#define DCDCCTRL_TSTEP0_SHIFT 2
+#define DCDCCTRL_DCDC1_MODE_MASK 0x02
+#define DCDCCTRL_DCDC1_MODE_SHIFT 1
+
+/* Register DCDCCTRL2 and DCDCCTRL3 register.RegisterDescription */
+#define DCDCCTRL_TSTEP2_MASK 0x10
+#define DCDCCTRL_TSTEP2_SHIFT 4
+#define DCDCCTRL_TSTEP1_MASK 0x08
+#define DCDCCTRL_TSTEP1_SHIFT 3
+#define DCDCCTRL_TSTEP0_MASK 0x04
+#define DCDCCTRL_TSTEP0_SHIFT 2
+#define DCDCCTRL_DCDC_MODE_MASK 0x02
+#define DCDCCTRL_DCDC_MODE_SHIFT 1
+#define DCDCCTRL_RSVD0_MASK 0x01
+#define DCDCCTRL_RSVD0_SHIFT 0
+
+/* Register DCDCCTRL4 register.RegisterDescription */
+#define DCDCCTRL_RAMP_TIME_MASK 0x01
+#define DCDCCTRL_RAMP_TIME_SHIFT 0
+
+/* Register DCDCx_AVS */
+#define DCDC_AVS_ENABLE_MASK 0x80
+#define DCDC_AVS_ENABLE_SHIFT 7
+#define DCDC_AVS_ECO_MASK 0x40
+#define DCDC_AVS_ECO_SHIFT 6
+
+/* Register DCDCx_LIMIT */
+#define DCDC_LIMIT_RANGE_MASK 0xC0
+#define DCDC_LIMIT_RANGE_SHIFT 6
+#define DCDC_LIMIT_MAX_SEL_MASK 0x3F
+#define DCDC_LIMIT_MAX_SEL_SHIFT 0
+
+/**
+ * struct tps65912_board
+ * Board platform dat may be used to initialize regulators.
+ */
+struct tps65912_board {
+ int is_dcdc1_avs;
+ int is_dcdc2_avs;
+ int is_dcdc3_avs;
+ int is_dcdc4_avs;
+ int irq;
+ int irq_base;
+ int gpio_base;
+ struct regulator_init_data *tps65912_pmic_init_data;
+};
+
+/**
+ * struct tps65912 - tps65912 sub-driver chip access routines
+ */
+
+struct tps65912 {
+ struct device *dev;
+ /* for read/write acces */
+ struct mutex io_mutex;
+
+ /* For device IO interfaces: I2C or SPI */
+ void *control_data;
+
+ int (*read)(struct tps65912 *tps65912, u8 reg, int size, void *dest);
+ int (*write)(struct tps65912 *tps65912, u8 reg, int size, void *src);
+
+ /* Client devices */
+ struct tps65912_pmic *pmic;
+
+ /* GPIO Handling */
+ struct gpio_chip gpio;
+
+ /* IRQ Handling */
+ struct mutex irq_lock;
+ int chip_irq;
+ int irq_base;
+ int irq_num;
+ u32 irq_mask;
+};
+
+struct tps65912_platform_data {
+ int irq;
+ int irq_base;
+};
+
+unsigned int tps_chip(void);
+
+int tps65912_set_bits(struct tps65912 *tps65912, u8 reg, u8 mask);
+int tps65912_clear_bits(struct tps65912 *tps65912, u8 reg, u8 mask);
+int tps65912_reg_read(struct tps65912 *tps65912, u8 reg);
+int tps65912_reg_write(struct tps65912 *tps65912, u8 reg, u8 val);
+int tps65912_device_init(struct tps65912 *tps65912);
+void tps65912_device_exit(struct tps65912 *tps65912);
+int tps65912_irq_init(struct tps65912 *tps65912, int irq,
+ struct tps65912_platform_data *pdata);
+
+#endif /* __LINUX_MFD_TPS65912_H */
diff --git a/include/linux/mfd/wm831x/core.h b/include/linux/mfd/wm831x/core.h
index 0d515ee..8dda8de 100644
--- a/include/linux/mfd/wm831x/core.h
+++ b/include/linux/mfd/wm831x/core.h
@@ -17,6 +17,7 @@
#include <linux/completion.h>
#include <linux/interrupt.h>
+#include <linux/list.h>
/*
* Register values.
@@ -234,9 +235,111 @@
#define WM831X_ON_PIN_TO_SHIFT 0 /* ON_PIN_TO - [1:0] */
#define WM831X_ON_PIN_TO_WIDTH 2 /* ON_PIN_TO - [1:0] */
+/*
+ * R16528 (0x4090) - Clock Control 1
+ */
+#define WM831X_CLKOUT_ENA 0x8000 /* CLKOUT_ENA */
+#define WM831X_CLKOUT_ENA_MASK 0x8000 /* CLKOUT_ENA */
+#define WM831X_CLKOUT_ENA_SHIFT 15 /* CLKOUT_ENA */
+#define WM831X_CLKOUT_ENA_WIDTH 1 /* CLKOUT_ENA */
+#define WM831X_CLKOUT_OD 0x2000 /* CLKOUT_OD */
+#define WM831X_CLKOUT_OD_MASK 0x2000 /* CLKOUT_OD */
+#define WM831X_CLKOUT_OD_SHIFT 13 /* CLKOUT_OD */
+#define WM831X_CLKOUT_OD_WIDTH 1 /* CLKOUT_OD */
+#define WM831X_CLKOUT_SLOT_MASK 0x0700 /* CLKOUT_SLOT - [10:8] */
+#define WM831X_CLKOUT_SLOT_SHIFT 8 /* CLKOUT_SLOT - [10:8] */
+#define WM831X_CLKOUT_SLOT_WIDTH 3 /* CLKOUT_SLOT - [10:8] */
+#define WM831X_CLKOUT_SLPSLOT_MASK 0x0070 /* CLKOUT_SLPSLOT - [6:4] */
+#define WM831X_CLKOUT_SLPSLOT_SHIFT 4 /* CLKOUT_SLPSLOT - [6:4] */
+#define WM831X_CLKOUT_SLPSLOT_WIDTH 3 /* CLKOUT_SLPSLOT - [6:4] */
+#define WM831X_CLKOUT_SRC 0x0001 /* CLKOUT_SRC */
+#define WM831X_CLKOUT_SRC_MASK 0x0001 /* CLKOUT_SRC */
+#define WM831X_CLKOUT_SRC_SHIFT 0 /* CLKOUT_SRC */
+#define WM831X_CLKOUT_SRC_WIDTH 1 /* CLKOUT_SRC */
+
+/*
+ * R16529 (0x4091) - Clock Control 2
+ */
+#define WM831X_XTAL_INH 0x8000 /* XTAL_INH */
+#define WM831X_XTAL_INH_MASK 0x8000 /* XTAL_INH */
+#define WM831X_XTAL_INH_SHIFT 15 /* XTAL_INH */
+#define WM831X_XTAL_INH_WIDTH 1 /* XTAL_INH */
+#define WM831X_XTAL_ENA 0x2000 /* XTAL_ENA */
+#define WM831X_XTAL_ENA_MASK 0x2000 /* XTAL_ENA */
+#define WM831X_XTAL_ENA_SHIFT 13 /* XTAL_ENA */
+#define WM831X_XTAL_ENA_WIDTH 1 /* XTAL_ENA */
+#define WM831X_XTAL_BKUPENA 0x1000 /* XTAL_BKUPENA */
+#define WM831X_XTAL_BKUPENA_MASK 0x1000 /* XTAL_BKUPENA */
+#define WM831X_XTAL_BKUPENA_SHIFT 12 /* XTAL_BKUPENA */
+#define WM831X_XTAL_BKUPENA_WIDTH 1 /* XTAL_BKUPENA */
+#define WM831X_FLL_AUTO 0x0080 /* FLL_AUTO */
+#define WM831X_FLL_AUTO_MASK 0x0080 /* FLL_AUTO */
+#define WM831X_FLL_AUTO_SHIFT 7 /* FLL_AUTO */
+#define WM831X_FLL_AUTO_WIDTH 1 /* FLL_AUTO */
+#define WM831X_FLL_AUTO_FREQ_MASK 0x0007 /* FLL_AUTO_FREQ - [2:0] */
+#define WM831X_FLL_AUTO_FREQ_SHIFT 0 /* FLL_AUTO_FREQ - [2:0] */
+#define WM831X_FLL_AUTO_FREQ_WIDTH 3 /* FLL_AUTO_FREQ - [2:0] */
+
+/*
+ * R16530 (0x4092) - FLL Control 1
+ */
+#define WM831X_FLL_FRAC 0x0004 /* FLL_FRAC */
+#define WM831X_FLL_FRAC_MASK 0x0004 /* FLL_FRAC */
+#define WM831X_FLL_FRAC_SHIFT 2 /* FLL_FRAC */
+#define WM831X_FLL_FRAC_WIDTH 1 /* FLL_FRAC */
+#define WM831X_FLL_OSC_ENA 0x0002 /* FLL_OSC_ENA */
+#define WM831X_FLL_OSC_ENA_MASK 0x0002 /* FLL_OSC_ENA */
+#define WM831X_FLL_OSC_ENA_SHIFT 1 /* FLL_OSC_ENA */
+#define WM831X_FLL_OSC_ENA_WIDTH 1 /* FLL_OSC_ENA */
+#define WM831X_FLL_ENA 0x0001 /* FLL_ENA */
+#define WM831X_FLL_ENA_MASK 0x0001 /* FLL_ENA */
+#define WM831X_FLL_ENA_SHIFT 0 /* FLL_ENA */
+#define WM831X_FLL_ENA_WIDTH 1 /* FLL_ENA */
+
+/*
+ * R16531 (0x4093) - FLL Control 2
+ */
+#define WM831X_FLL_OUTDIV_MASK 0x3F00 /* FLL_OUTDIV - [13:8] */
+#define WM831X_FLL_OUTDIV_SHIFT 8 /* FLL_OUTDIV - [13:8] */
+#define WM831X_FLL_OUTDIV_WIDTH 6 /* FLL_OUTDIV - [13:8] */
+#define WM831X_FLL_CTRL_RATE_MASK 0x0070 /* FLL_CTRL_RATE - [6:4] */
+#define WM831X_FLL_CTRL_RATE_SHIFT 4 /* FLL_CTRL_RATE - [6:4] */
+#define WM831X_FLL_CTRL_RATE_WIDTH 3 /* FLL_CTRL_RATE - [6:4] */
+#define WM831X_FLL_FRATIO_MASK 0x0007 /* FLL_FRATIO - [2:0] */
+#define WM831X_FLL_FRATIO_SHIFT 0 /* FLL_FRATIO - [2:0] */
+#define WM831X_FLL_FRATIO_WIDTH 3 /* FLL_FRATIO - [2:0] */
+
+/*
+ * R16532 (0x4094) - FLL Control 3
+ */
+#define WM831X_FLL_K_MASK 0xFFFF /* FLL_K - [15:0] */
+#define WM831X_FLL_K_SHIFT 0 /* FLL_K - [15:0] */
+#define WM831X_FLL_K_WIDTH 16 /* FLL_K - [15:0] */
+
+/*
+ * R16533 (0x4095) - FLL Control 4
+ */
+#define WM831X_FLL_N_MASK 0x7FE0 /* FLL_N - [14:5] */
+#define WM831X_FLL_N_SHIFT 5 /* FLL_N - [14:5] */
+#define WM831X_FLL_N_WIDTH 10 /* FLL_N - [14:5] */
+#define WM831X_FLL_GAIN_MASK 0x000F /* FLL_GAIN - [3:0] */
+#define WM831X_FLL_GAIN_SHIFT 0 /* FLL_GAIN - [3:0] */
+#define WM831X_FLL_GAIN_WIDTH 4 /* FLL_GAIN - [3:0] */
+
+/*
+ * R16534 (0x4096) - FLL Control 5
+ */
+#define WM831X_FLL_CLK_REF_DIV_MASK 0x0018 /* FLL_CLK_REF_DIV - [4:3] */
+#define WM831X_FLL_CLK_REF_DIV_SHIFT 3 /* FLL_CLK_REF_DIV - [4:3] */
+#define WM831X_FLL_CLK_REF_DIV_WIDTH 2 /* FLL_CLK_REF_DIV - [4:3] */
+#define WM831X_FLL_CLK_SRC_MASK 0x0003 /* FLL_CLK_SRC - [1:0] */
+#define WM831X_FLL_CLK_SRC_SHIFT 0 /* FLL_CLK_SRC - [1:0] */
+#define WM831X_FLL_CLK_SRC_WIDTH 2 /* FLL_CLK_SRC - [1:0] */
+
struct regulator_dev;
#define WM831X_NUM_IRQ_REGS 5
+#define WM831X_NUM_GPIO_REGS 16
enum wm831x_parent {
WM8310 = 0x8310,
@@ -248,6 +351,12 @@
WM8326 = 0x8326,
};
+struct wm831x;
+enum wm831x_auxadc;
+
+typedef int (*wm831x_auxadc_read_fn)(struct wm831x *wm831x,
+ enum wm831x_auxadc input);
+
struct wm831x {
struct mutex io_lock;
@@ -261,7 +370,7 @@
int irq; /* Our chip IRQ */
struct mutex irq_lock;
- unsigned int irq_base;
+ int irq_base;
int irq_masks_cur[WM831X_NUM_IRQ_REGS]; /* Currently active value */
int irq_masks_cache[WM831X_NUM_IRQ_REGS]; /* Cached hardware value */
@@ -272,8 +381,13 @@
int num_gpio;
+ /* Used by the interrupt controller code to post writes */
+ int gpio_update[WM831X_NUM_GPIO_REGS];
+
struct mutex auxadc_lock;
- struct completion auxadc_done;
+ struct list_head auxadc_pending;
+ u16 auxadc_active;
+ wm831x_auxadc_read_fn auxadc_read;
/* The WM831x has a security key blocking access to certain
* registers. The mutex is taken by the accessors for locking
@@ -300,5 +414,6 @@
int wm831x_device_suspend(struct wm831x *wm831x);
int wm831x_irq_init(struct wm831x *wm831x, int irq);
void wm831x_irq_exit(struct wm831x *wm831x);
+void wm831x_auxadc_init(struct wm831x *wm831x);
#endif
diff --git a/include/linux/mfd/wm831x/pdata.h b/include/linux/mfd/wm831x/pdata.h
index ff42d70..0ba2459 100644
--- a/include/linux/mfd/wm831x/pdata.h
+++ b/include/linux/mfd/wm831x/pdata.h
@@ -120,6 +120,9 @@
/** Put the /IRQ line into CMOS mode */
bool irq_cmos;
+ /** Disable the touchscreen */
+ bool disable_touch;
+
int irq_base;
int gpio_base;
int gpio_defaults[WM831X_GPIO_NUM];
diff --git a/include/linux/mm.h b/include/linux/mm.h
index 3172a1c..7438071 100644
--- a/include/linux/mm.h
+++ b/include/linux/mm.h
@@ -685,7 +685,7 @@
page->flags |= (section & SECTIONS_MASK) << SECTIONS_PGSHIFT;
}
-static inline unsigned long page_to_section(struct page *page)
+static inline unsigned long page_to_section(const struct page *page)
{
return (page->flags >> SECTIONS_PGSHIFT) & SECTIONS_MASK;
}
@@ -720,7 +720,7 @@
static __always_inline void *lowmem_page_address(const struct page *page)
{
- return __va(PFN_PHYS(page_to_pfn((struct page *)page)));
+ return __va(PFN_PHYS(page_to_pfn(page)));
}
#if defined(CONFIG_HIGHMEM) && !defined(WANT_PAGE_VIRTUAL)
@@ -737,7 +737,7 @@
#endif
#if defined(HASHED_PAGE_VIRTUAL)
-void *page_address(struct page *page);
+void *page_address(const struct page *page);
void set_page_address(struct page *page, void *virtual);
void page_address_init(void);
#endif
@@ -962,6 +962,8 @@
#ifdef CONFIG_MMU
extern int handle_mm_fault(struct mm_struct *mm, struct vm_area_struct *vma,
unsigned long address, unsigned int flags);
+extern int fixup_user_fault(struct task_struct *tsk, struct mm_struct *mm,
+ unsigned long address, unsigned int fault_flags);
#else
static inline int handle_mm_fault(struct mm_struct *mm,
struct vm_area_struct *vma, unsigned long address,
@@ -971,6 +973,14 @@
BUG();
return VM_FAULT_SIGBUS;
}
+static inline int fixup_user_fault(struct task_struct *tsk,
+ struct mm_struct *mm, unsigned long address,
+ unsigned int fault_flags)
+{
+ /* should never happen if there's no MMU */
+ BUG();
+ return -EFAULT;
+}
#endif
extern int make_pages_present(unsigned long addr, unsigned long end);
@@ -988,8 +998,6 @@
int get_user_pages_fast(unsigned long start, int nr_pages, int write,
struct page **pages);
struct page *get_dump_page(unsigned long addr);
-extern int fixup_user_fault(struct task_struct *tsk, struct mm_struct *mm,
- unsigned long address, unsigned int fault_flags);
extern int try_to_release_page(struct page * page, gfp_t gfp_mask);
extern void do_invalidatepage(struct page *page, unsigned long offset);
@@ -1600,6 +1608,7 @@
};
extern void memory_failure(unsigned long pfn, int trapno);
extern int __memory_failure(unsigned long pfn, int trapno, int flags);
+extern void memory_failure_queue(unsigned long pfn, int trapno, int flags);
extern int unpoison_memory(unsigned long pfn);
extern int sysctl_memory_failure_early_kill;
extern int sysctl_memory_failure_recovery;
diff --git a/include/linux/mmc/host.h b/include/linux/mmc/host.h
index 0f83858..1d09562 100644
--- a/include/linux/mmc/host.h
+++ b/include/linux/mmc/host.h
@@ -56,8 +56,6 @@
#define MMC_TIMING_UHS_SDR104 4
#define MMC_TIMING_UHS_DDR50 5
- unsigned char ddr; /* dual data rate used */
-
#define MMC_SDR_MODE 0
#define MMC_1_2V_DDR_MODE 1
#define MMC_1_8V_DDR_MODE 2
diff --git a/include/linux/netlink.h b/include/linux/netlink.h
index 2e17c5d..180540a 100644
--- a/include/linux/netlink.h
+++ b/include/linux/netlink.h
@@ -29,7 +29,7 @@
#define MAX_LINKS 32
struct sockaddr_nl {
- sa_family_t nl_family; /* AF_NETLINK */
+ __kernel_sa_family_t nl_family; /* AF_NETLINK */
unsigned short nl_pad; /* zero */
__u32 nl_pid; /* port ID */
__u32 nl_groups; /* multicast groups mask */
diff --git a/include/linux/nfs_fs.h b/include/linux/nfs_fs.h
index b96fb99..eaac770 100644
--- a/include/linux/nfs_fs.h
+++ b/include/linux/nfs_fs.h
@@ -569,12 +569,12 @@
extern int nfs3_proc_setacl(struct inode *inode, int type,
struct posix_acl *acl);
extern int nfs3_proc_set_default_acl(struct inode *dir, struct inode *inode,
- mode_t mode);
+ umode_t mode);
extern void nfs3_forget_cached_acls(struct inode *inode);
#else
static inline int nfs3_proc_set_default_acl(struct inode *dir,
struct inode *inode,
- mode_t mode)
+ umode_t mode)
{
return 0;
}
diff --git a/include/linux/nfs_fs_sb.h b/include/linux/nfs_fs_sb.h
index 82fdfc7..b5479df 100644
--- a/include/linux/nfs_fs_sb.h
+++ b/include/linux/nfs_fs_sb.h
@@ -131,6 +131,7 @@
struct fscache_cookie *fscache; /* superblock cookie */
#endif
+ u32 pnfs_blksize; /* layout_blksize attr */
#ifdef CONFIG_NFS_V4
u32 attr_bitmask[3];/* V4 bitmask representing the set
of attributes supported on this
@@ -145,7 +146,6 @@
filesystem */
struct pnfs_layoutdriver_type *pnfs_curr_ld; /* Active layout driver */
struct rpc_wait_queue roc_rpcwaitq;
- u32 pnfs_blksize; /* layout_blksize attr */
void *pnfs_ld_data; /* per mount point data */
/* the following fields are protected by nfs_client->cl_lock */
diff --git a/include/linux/nfs_xdr.h b/include/linux/nfs_xdr.h
index 569ea5b..abd615d 100644
--- a/include/linux/nfs_xdr.h
+++ b/include/linux/nfs_xdr.h
@@ -773,6 +773,11 @@
struct posix_acl * acl_default;
};
+struct nfs4_string {
+ unsigned int len;
+ char *data;
+};
+
#ifdef CONFIG_NFS_V4
typedef u64 clientid4;
@@ -963,11 +968,6 @@
struct nfs4_sequence_res seq_res;
};
-struct nfs4_string {
- unsigned int len;
- char *data;
-};
-
#define NFS4_PATHNAME_MAXCOMPONENTS 512
struct nfs4_pathname {
unsigned int ncomponents;
diff --git a/include/linux/of.h b/include/linux/of.h
index bd716f8..9180dc5 100644
--- a/include/linux/of.h
+++ b/include/linux/of.h
@@ -196,12 +196,13 @@
const char *name,
int *lenp);
extern int of_property_read_u32_array(const struct device_node *np,
- char *propname,
+ const char *propname,
u32 *out_values,
size_t sz);
-extern int of_property_read_string(struct device_node *np, char *propname,
- const char **out_string);
+extern int of_property_read_string(struct device_node *np,
+ const char *propname,
+ const char **out_string);
extern int of_device_is_compatible(const struct device_node *device,
const char *);
extern int of_device_is_available(const struct device_node *device);
@@ -242,21 +243,30 @@
}
static inline int of_property_read_u32_array(const struct device_node *np,
- char *propname, u32 *out_values, size_t sz)
+ const char *propname,
+ u32 *out_values, size_t sz)
{
return -ENOSYS;
}
static inline int of_property_read_string(struct device_node *np,
- char *propname, const char **out_string)
+ const char *propname,
+ const char **out_string)
{
return -ENOSYS;
}
+static inline const void *of_get_property(const struct device_node *node,
+ const char *name,
+ int *lenp)
+{
+ return NULL;
+}
+
#endif /* CONFIG_OF */
static inline int of_property_read_u32(const struct device_node *np,
- char *propname,
+ const char *propname,
u32 *out_value)
{
return of_property_read_u32_array(np, propname, out_value, 1);
diff --git a/include/linux/of_gpio.h b/include/linux/of_gpio.h
index aec8025..52280a2 100644
--- a/include/linux/of_gpio.h
+++ b/include/linux/of_gpio.h
@@ -57,6 +57,8 @@
extern void of_gpiochip_add(struct gpio_chip *gc);
extern void of_gpiochip_remove(struct gpio_chip *gc);
extern struct gpio_chip *of_node_to_gpiochip(struct device_node *np);
+extern int of_gpio_simple_xlate(struct gpio_chip *gc, struct device_node *np,
+ const void *gpio_spec, u32 *flags);
#else /* CONFIG_OF_GPIO */
@@ -72,6 +74,13 @@
return 0;
}
+static inline int of_gpio_simple_xlate(struct gpio_chip *gc,
+ struct device_node *np,
+ const void *gpio_spec, u32 *flags)
+{
+ return -ENOSYS;
+}
+
static inline void of_gpiochip_add(struct gpio_chip *gc) { }
static inline void of_gpiochip_remove(struct gpio_chip *gc) { }
diff --git a/include/linux/pci.h b/include/linux/pci.h
index f27893b..8c230cb 100644
--- a/include/linux/pci.h
+++ b/include/linux/pci.h
@@ -251,7 +251,8 @@
u8 revision; /* PCI revision, low byte of class word */
u8 hdr_type; /* PCI header type (`multi' flag masked out) */
u8 pcie_cap; /* PCI-E capability offset */
- u8 pcie_type; /* PCI-E device/port type */
+ u8 pcie_type:4; /* PCI-E device/port type */
+ u8 pcie_mpss:3; /* PCI-E Max Payload Size Supported */
u8 rom_base_reg; /* which config register controls the ROM */
u8 pin; /* which interrupt pin this device uses */
@@ -617,6 +618,16 @@
/* these external functions are only available when PCI support is enabled */
#ifdef CONFIG_PCI
+extern void pcie_bus_configure_settings(struct pci_bus *bus, u8 smpss);
+
+enum pcie_bus_config_types {
+ PCIE_BUS_PERFORMANCE,
+ PCIE_BUS_SAFE,
+ PCIE_BUS_PEER2PEER,
+};
+
+extern enum pcie_bus_config_types pcie_bus_config;
+
extern struct bus_type pci_bus_type;
/* Do NOT directly access these two variables, unless you are arch specific pci
@@ -796,10 +807,13 @@
int pcix_set_mmrbc(struct pci_dev *dev, int mmrbc);
int pcie_get_readrq(struct pci_dev *dev);
int pcie_set_readrq(struct pci_dev *dev, int rq);
+int pcie_get_mps(struct pci_dev *dev);
+int pcie_set_mps(struct pci_dev *dev, int mps);
int __pci_reset_function(struct pci_dev *dev);
int pci_reset_function(struct pci_dev *dev);
void pci_update_resource(struct pci_dev *dev, int resno);
int __must_check pci_assign_resource(struct pci_dev *dev, int i);
+int __must_check pci_reassign_resource(struct pci_dev *dev, int i, resource_size_t add_size, resource_size_t align);
int pci_select_bars(struct pci_dev *dev, unsigned long flags);
/* ROM control related routines */
diff --git a/include/linux/pci_ids.h b/include/linux/pci_ids.h
index b00c4ec..ae96bbe 100644
--- a/include/linux/pci_ids.h
+++ b/include/linux/pci_ids.h
@@ -2709,6 +2709,16 @@
#define PCI_DEVICE_ID_INTEL_ICH10_5 0x3a60
#define PCI_DEVICE_ID_INTEL_5_3400_SERIES_LPC_MIN 0x3b00
#define PCI_DEVICE_ID_INTEL_5_3400_SERIES_LPC_MAX 0x3b1f
+#define PCI_DEVICE_ID_INTEL_IOAT_SNB0 0x3c20
+#define PCI_DEVICE_ID_INTEL_IOAT_SNB1 0x3c21
+#define PCI_DEVICE_ID_INTEL_IOAT_SNB2 0x3c22
+#define PCI_DEVICE_ID_INTEL_IOAT_SNB3 0x3c23
+#define PCI_DEVICE_ID_INTEL_IOAT_SNB4 0x3c24
+#define PCI_DEVICE_ID_INTEL_IOAT_SNB5 0x3c25
+#define PCI_DEVICE_ID_INTEL_IOAT_SNB6 0x3c26
+#define PCI_DEVICE_ID_INTEL_IOAT_SNB7 0x3c27
+#define PCI_DEVICE_ID_INTEL_IOAT_SNB8 0x3c2e
+#define PCI_DEVICE_ID_INTEL_IOAT_SNB9 0x3c2f
#define PCI_DEVICE_ID_INTEL_IOAT_SNB 0x402f
#define PCI_DEVICE_ID_INTEL_5100_16 0x65f0
#define PCI_DEVICE_ID_INTEL_5100_21 0x65f5
diff --git a/include/linux/pm_domain.h b/include/linux/pm_domain.h
index 21097cb..f9ec173 100644
--- a/include/linux/pm_domain.h
+++ b/include/linux/pm_domain.h
@@ -72,8 +72,6 @@
extern void pm_genpd_init(struct generic_pm_domain *genpd,
struct dev_power_governor *gov, bool is_off);
extern int pm_genpd_poweron(struct generic_pm_domain *genpd);
-extern void pm_genpd_poweroff_unused(void);
-extern void genpd_queue_power_off_work(struct generic_pm_domain *genpd);
#else
static inline int pm_genpd_add_device(struct generic_pm_domain *genpd,
struct device *dev)
@@ -101,8 +99,14 @@
{
return -ENOSYS;
}
-static inline void pm_genpd_poweroff_unused(void) {}
+#endif
+
+#ifdef CONFIG_PM_GENERIC_DOMAINS_RUNTIME
+extern void genpd_queue_power_off_work(struct generic_pm_domain *genpd);
+extern void pm_genpd_poweroff_unused(void);
+#else
static inline void genpd_queue_power_off_work(struct generic_pm_domain *gpd) {}
+static inline void pm_genpd_poweroff_unused(void) {}
#endif
#endif /* _LINUX_PM_DOMAIN_H */
diff --git a/include/linux/posix_acl.h b/include/linux/posix_acl.h
index 9a53b99..b768110 100644
--- a/include/linux/posix_acl.h
+++ b/include/linux/posix_acl.h
@@ -9,6 +9,7 @@
#define __LINUX_POSIX_ACL_H
#include <linux/slab.h>
+#include <linux/rcupdate.h>
#define ACL_UNDEFINED_ID (-1)
@@ -38,7 +39,10 @@
};
struct posix_acl {
- atomic_t a_refcount;
+ union {
+ atomic_t a_refcount;
+ struct rcu_head a_rcu;
+ };
unsigned int a_count;
struct posix_acl_entry a_entries[0];
};
@@ -65,7 +69,7 @@
posix_acl_release(struct posix_acl *acl)
{
if (acl && atomic_dec_and_test(&acl->a_refcount))
- kfree(acl);
+ kfree_rcu(acl, a_rcu);
}
@@ -75,29 +79,31 @@
extern struct posix_acl *posix_acl_alloc(int, gfp_t);
extern int posix_acl_valid(const struct posix_acl *);
extern int posix_acl_permission(struct inode *, const struct posix_acl *, int);
-extern struct posix_acl *posix_acl_from_mode(mode_t, gfp_t);
-extern int posix_acl_equiv_mode(const struct posix_acl *, mode_t *);
-extern int posix_acl_create(struct posix_acl **, gfp_t, mode_t *);
-extern int posix_acl_chmod(struct posix_acl **, gfp_t, mode_t);
+extern struct posix_acl *posix_acl_from_mode(umode_t, gfp_t);
+extern int posix_acl_equiv_mode(const struct posix_acl *, umode_t *);
+extern int posix_acl_create(struct posix_acl **, gfp_t, umode_t *);
+extern int posix_acl_chmod(struct posix_acl **, gfp_t, umode_t);
extern struct posix_acl *get_posix_acl(struct inode *, int);
extern int set_posix_acl(struct inode *, int, struct posix_acl *);
#ifdef CONFIG_FS_POSIX_ACL
-static inline struct posix_acl *get_cached_acl(struct inode *inode, int type)
+static inline struct posix_acl **acl_by_type(struct inode *inode, int type)
{
- struct posix_acl **p, *acl;
switch (type) {
case ACL_TYPE_ACCESS:
- p = &inode->i_acl;
- break;
+ return &inode->i_acl;
case ACL_TYPE_DEFAULT:
- p = &inode->i_default_acl;
- break;
+ return &inode->i_default_acl;
default:
- return ERR_PTR(-EINVAL);
+ BUG();
}
- acl = ACCESS_ONCE(*p);
+}
+
+static inline struct posix_acl *get_cached_acl(struct inode *inode, int type)
+{
+ struct posix_acl **p = acl_by_type(inode, type);
+ struct posix_acl *acl = ACCESS_ONCE(*p);
if (acl) {
spin_lock(&inode->i_lock);
acl = *p;
@@ -108,41 +114,20 @@
return acl;
}
-static inline int negative_cached_acl(struct inode *inode, int type)
+static inline struct posix_acl *get_cached_acl_rcu(struct inode *inode, int type)
{
- struct posix_acl **p, *acl;
- switch (type) {
- case ACL_TYPE_ACCESS:
- p = &inode->i_acl;
- break;
- case ACL_TYPE_DEFAULT:
- p = &inode->i_default_acl;
- break;
- default:
- BUG();
- }
- acl = ACCESS_ONCE(*p);
- if (acl)
- return 0;
- return 1;
+ return rcu_dereference(*acl_by_type(inode, type));
}
static inline void set_cached_acl(struct inode *inode,
int type,
struct posix_acl *acl)
{
- struct posix_acl *old = NULL;
+ struct posix_acl **p = acl_by_type(inode, type);
+ struct posix_acl *old;
spin_lock(&inode->i_lock);
- switch (type) {
- case ACL_TYPE_ACCESS:
- old = inode->i_acl;
- inode->i_acl = posix_acl_dup(acl);
- break;
- case ACL_TYPE_DEFAULT:
- old = inode->i_default_acl;
- inode->i_default_acl = posix_acl_dup(acl);
- break;
- }
+ old = *p;
+ rcu_assign_pointer(*p, posix_acl_dup(acl));
spin_unlock(&inode->i_lock);
if (old != ACL_NOT_CACHED)
posix_acl_release(old);
@@ -150,18 +135,11 @@
static inline void forget_cached_acl(struct inode *inode, int type)
{
- struct posix_acl *old = NULL;
+ struct posix_acl **p = acl_by_type(inode, type);
+ struct posix_acl *old;
spin_lock(&inode->i_lock);
- switch (type) {
- case ACL_TYPE_ACCESS:
- old = inode->i_acl;
- inode->i_acl = ACL_NOT_CACHED;
- break;
- case ACL_TYPE_DEFAULT:
- old = inode->i_default_acl;
- inode->i_default_acl = ACL_NOT_CACHED;
- break;
- }
+ old = *p;
+ *p = ACL_NOT_CACHED;
spin_unlock(&inode->i_lock);
if (old != ACL_NOT_CACHED)
posix_acl_release(old);
diff --git a/include/linux/pstore.h b/include/linux/pstore.h
index 2455ef2..cc03bbf 100644
--- a/include/linux/pstore.h
+++ b/include/linux/pstore.h
@@ -38,9 +38,12 @@
int (*open)(struct pstore_info *psi);
int (*close)(struct pstore_info *psi);
ssize_t (*read)(u64 *id, enum pstore_type_id *type,
- struct timespec *time);
- u64 (*write)(enum pstore_type_id type, size_t size);
- int (*erase)(u64 id);
+ struct timespec *time, struct pstore_info *psi);
+ u64 (*write)(enum pstore_type_id type, unsigned int part,
+ size_t size, struct pstore_info *psi);
+ int (*erase)(enum pstore_type_id type, u64 id,
+ struct pstore_info *psi);
+ void *data;
};
#ifdef CONFIG_PSTORE
diff --git a/include/linux/radix-tree.h b/include/linux/radix-tree.h
index 23241c2..9d4539c 100644
--- a/include/linux/radix-tree.h
+++ b/include/linux/radix-tree.h
@@ -39,7 +39,15 @@
* when it is shrunk, before we rcu free the node. See shrink code for
* details.
*/
-#define RADIX_TREE_INDIRECT_PTR 1
+#define RADIX_TREE_INDIRECT_PTR 1
+/*
+ * A common use of the radix tree is to store pointers to struct pages;
+ * but shmem/tmpfs needs also to store swap entries in the same tree:
+ * those are marked as exceptional entries to distinguish them.
+ * EXCEPTIONAL_ENTRY tests the bit, EXCEPTIONAL_SHIFT shifts content past it.
+ */
+#define RADIX_TREE_EXCEPTIONAL_ENTRY 2
+#define RADIX_TREE_EXCEPTIONAL_SHIFT 2
#define radix_tree_indirect_to_ptr(ptr) \
radix_tree_indirect_to_ptr((void __force *)(ptr))
@@ -174,6 +182,28 @@
}
/**
+ * radix_tree_exceptional_entry - radix_tree_deref_slot gave exceptional entry?
+ * @arg: value returned by radix_tree_deref_slot
+ * Returns: 0 if well-aligned pointer, non-0 if exceptional entry.
+ */
+static inline int radix_tree_exceptional_entry(void *arg)
+{
+ /* Not unlikely because radix_tree_exception often tested first */
+ return (unsigned long)arg & RADIX_TREE_EXCEPTIONAL_ENTRY;
+}
+
+/**
+ * radix_tree_exception - radix_tree_deref_slot returned either exception?
+ * @arg: value returned by radix_tree_deref_slot
+ * Returns: 0 if well-aligned pointer, non-0 if either kind of exception.
+ */
+static inline int radix_tree_exception(void *arg)
+{
+ return unlikely((unsigned long)arg &
+ (RADIX_TREE_INDIRECT_PTR | RADIX_TREE_EXCEPTIONAL_ENTRY));
+}
+
+/**
* radix_tree_replace_slot - replace item in a slot
* @pslot: pointer to slot, returned by radix_tree_lookup_slot
* @item: new item to store in the slot.
@@ -194,8 +224,8 @@
unsigned int
radix_tree_gang_lookup(struct radix_tree_root *root, void **results,
unsigned long first_index, unsigned int max_items);
-unsigned int
-radix_tree_gang_lookup_slot(struct radix_tree_root *root, void ***results,
+unsigned int radix_tree_gang_lookup_slot(struct radix_tree_root *root,
+ void ***results, unsigned long *indices,
unsigned long first_index, unsigned int max_items);
unsigned long radix_tree_next_hole(struct radix_tree_root *root,
unsigned long index, unsigned long max_scan);
@@ -222,6 +252,7 @@
unsigned long nr_to_tag,
unsigned int fromtag, unsigned int totag);
int radix_tree_tagged(struct radix_tree_root *root, unsigned int tag);
+unsigned long radix_tree_locate_item(struct radix_tree_root *root, void *item);
static inline void radix_tree_preload_end(void)
{
diff --git a/include/linux/random.h b/include/linux/random.h
index ce29a04..d13059f 100644
--- a/include/linux/random.h
+++ b/include/linux/random.h
@@ -57,18 +57,6 @@
extern void get_random_bytes(void *buf, int nbytes);
void generate_random_uuid(unsigned char uuid_out[16]);
-extern __u32 secure_ip_id(__be32 daddr);
-extern __u32 secure_ipv6_id(const __be32 daddr[4]);
-extern u32 secure_ipv4_port_ephemeral(__be32 saddr, __be32 daddr, __be16 dport);
-extern u32 secure_ipv6_port_ephemeral(const __be32 *saddr, const __be32 *daddr,
- __be16 dport);
-extern __u32 secure_tcp_sequence_number(__be32 saddr, __be32 daddr,
- __be16 sport, __be16 dport);
-extern __u32 secure_tcpv6_sequence_number(__be32 *saddr, __be32 *daddr,
- __be16 sport, __be16 dport);
-extern u64 secure_dccp_sequence_number(__be32 saddr, __be32 daddr,
- __be16 sport, __be16 dport);
-
#ifndef MODULE
extern const struct file_operations random_fops, urandom_fops;
#endif
diff --git a/include/linux/regulator/consumer.h b/include/linux/regulator/consumer.h
index 9e87c1c..26f6ea4 100644
--- a/include/linux/regulator/consumer.h
+++ b/include/linux/regulator/consumer.h
@@ -122,6 +122,9 @@
struct regulator_bulk_data {
const char *supply;
struct regulator *consumer;
+
+ /* Internal use */
+ int ret;
};
#if defined(CONFIG_REGULATOR)
diff --git a/include/linux/regulator/driver.h b/include/linux/regulator/driver.h
index 6c433b8..1a80bc7 100644
--- a/include/linux/regulator/driver.h
+++ b/include/linux/regulator/driver.h
@@ -188,18 +188,16 @@
/* lists we belong to */
struct list_head list; /* list of all regulators */
- struct list_head slist; /* list of supplied regulators */
/* lists we own */
struct list_head consumer_list; /* consumers we supply */
- struct list_head supply_list; /* regulators we supply */
struct blocking_notifier_head notifier;
struct mutex mutex; /* consumer lock */
struct module *owner;
struct device dev;
struct regulation_constraints *constraints;
- struct regulator_dev *supply; /* for tree */
+ struct regulator *supply; /* for tree */
void *reg_data; /* regulator_dev data */
diff --git a/include/linux/rtc.h b/include/linux/rtc.h
index b27ebea..93f4d03 100644
--- a/include/linux/rtc.h
+++ b/include/linux/rtc.h
@@ -97,6 +97,9 @@
#define RTC_AF 0x20 /* Alarm interrupt */
#define RTC_UF 0x10 /* Update interrupt for 1Hz RTC */
+
+#define RTC_MAX_FREQ 8192
+
#ifdef __KERNEL__
#include <linux/types.h>
diff --git a/include/linux/sched.h b/include/linux/sched.h
index 20b03bf..4ac2c05 100644
--- a/include/linux/sched.h
+++ b/include/linux/sched.h
@@ -1767,6 +1767,7 @@
#define PF_DUMPCORE 0x00000200 /* dumped core */
#define PF_SIGNALED 0x00000400 /* killed by a signal */
#define PF_MEMALLOC 0x00000800 /* Allocating memory */
+#define PF_NPROC_EXCEEDED 0x00001000 /* set_user noticed that RLIMIT_NPROC was exceeded */
#define PF_USED_MATH 0x00002000 /* if unset the fpu must be initialized before use */
#define PF_FREEZING 0x00004000 /* freeze in progress. do not account to load */
#define PF_NOFREEZE 0x00008000 /* this thread should not be frozen */
diff --git a/include/linux/serial_sci.h b/include/linux/serial_sci.h
index a2afc9f..8bffe9a 100644
--- a/include/linux/serial_sci.h
+++ b/include/linux/serial_sci.h
@@ -8,6 +8,8 @@
* Generic header for SuperH SCI(F) (used by sh/sh64/h8300 and related parts)
*/
+#define SCIx_NOT_SUPPORTED (-1)
+
enum {
SCBRR_ALGO_1, /* ((clk + 16 * bps) / (16 * bps) - 1) */
SCBRR_ALGO_2, /* ((clk + 16 * bps) / (32 * bps) - 1) */
@@ -25,6 +27,28 @@
#define SCSCR_CKE1 (1 << 1)
#define SCSCR_CKE0 (1 << 0)
+/* SCxSR SCI */
+#define SCI_TDRE 0x80
+#define SCI_RDRF 0x40
+#define SCI_ORER 0x20
+#define SCI_FER 0x10
+#define SCI_PER 0x08
+#define SCI_TEND 0x04
+
+#define SCI_DEFAULT_ERROR_MASK (SCI_PER | SCI_FER)
+
+/* SCxSR SCIF */
+#define SCIF_ER 0x0080
+#define SCIF_TEND 0x0040
+#define SCIF_TDFE 0x0020
+#define SCIF_BRK 0x0010
+#define SCIF_FER 0x0008
+#define SCIF_PER 0x0004
+#define SCIF_RDF 0x0002
+#define SCIF_DR 0x0001
+
+#define SCIF_DEFAULT_ERROR_MASK (SCIF_PER | SCIF_FER | SCIF_ER | SCIF_BRK)
+
/* Offsets into the sci_port->irqs array */
enum {
SCIx_ERI_IRQ,
@@ -32,6 +56,24 @@
SCIx_TXI_IRQ,
SCIx_BRI_IRQ,
SCIx_NR_IRQS,
+
+ SCIx_MUX_IRQ = SCIx_NR_IRQS, /* special case */
+};
+
+enum {
+ SCIx_PROBE_REGTYPE,
+
+ SCIx_SCI_REGTYPE,
+ SCIx_IRDA_REGTYPE,
+ SCIx_SCIFA_REGTYPE,
+ SCIx_SCIFB_REGTYPE,
+ SCIx_SH3_SCIF_REGTYPE,
+ SCIx_SH4_SCIF_REGTYPE,
+ SCIx_SH4_SCIF_NO_SCSPTR_REGTYPE,
+ SCIx_SH4_SCIF_FIFODATA_REGTYPE,
+ SCIx_SH7705_SCIF_REGTYPE,
+
+ SCIx_NR_REGTYPES,
};
#define SCIx_IRQ_MUXED(irq) \
@@ -42,8 +84,29 @@
[SCIx_BRI_IRQ] = (irq), \
}
+#define SCIx_IRQ_IS_MUXED(port) \
+ ((port)->cfg->irqs[SCIx_ERI_IRQ] == \
+ (port)->cfg->irqs[SCIx_RXI_IRQ]) || \
+ ((port)->cfg->irqs[SCIx_ERI_IRQ] && \
+ !(port)->cfg->irqs[SCIx_RXI_IRQ])
+/*
+ * SCI register subset common for all port types.
+ * Not all registers will exist on all parts.
+ */
+enum {
+ SCSMR, SCBRR, SCSCR, SCxSR,
+ SCFCR, SCFDR, SCxTDR, SCxRDR,
+ SCLSR, SCTFDR, SCRFDR, SCSPTR,
+
+ SCIx_NR_REGS,
+};
+
struct device;
+struct plat_sci_port_ops {
+ void (*init_pins)(struct uart_port *, unsigned int cflag);
+};
+
/*
* Platform device specific platform_data struct
*/
@@ -56,6 +119,18 @@
unsigned int scbrr_algo_id; /* SCBRR calculation algo */
unsigned int scscr; /* SCSCR initialization */
+ /*
+ * Platform overrides if necessary, defaults otherwise.
+ */
+ int overrun_bit;
+ unsigned int error_mask;
+
+ int port_reg;
+ unsigned char regshift;
+ unsigned char regtype;
+
+ struct plat_sci_port_ops *ops;
+
struct device *dma_dev;
unsigned int dma_slave_tx;
diff --git a/include/linux/sh_clk.h b/include/linux/sh_clk.h
index 9a52f72..3ccf186 100644
--- a/include/linux/sh_clk.h
+++ b/include/linux/sh_clk.h
@@ -147,4 +147,8 @@
int sh_clk_div6_register(struct clk *clks, int nr);
int sh_clk_div6_reparent_register(struct clk *clks, int nr);
+#define CLKDEV_CON_ID(_id, _clk) { .con_id = _id, .clk = _clk }
+#define CLKDEV_DEV_ID(_id, _clk) { .dev_id = _id, .clk = _clk }
+#define CLKDEV_ICK_ID(_cid, _did, _clk) { .con_id = _cid, .dev_id = _did, .clk = _clk }
+
#endif /* __SH_CLOCK_H */
diff --git a/include/linux/sh_dma.h b/include/linux/sh_dma.h
index b08cd4e..cb2dd11 100644
--- a/include/linux/sh_dma.h
+++ b/include/linux/sh_dma.h
@@ -62,6 +62,12 @@
const unsigned int *ts_shift;
int ts_shift_num;
u16 dmaor_init;
+ unsigned int chcr_offset;
+ u32 chcr_ie_bit;
+
+ unsigned int dmaor_is_32bit:1;
+ unsigned int needs_tend_set:1;
+ unsigned int no_dmars:1;
};
/* DMA register */
@@ -71,6 +77,8 @@
#define CHCR 0x0C
#define DMAOR 0x40
+#define TEND 0x18 /* USB-DMAC */
+
/* DMAOR definitions */
#define DMAOR_AE 0x00000004
#define DMAOR_NMIF 0x00000002
diff --git a/include/linux/shmem_fs.h b/include/linux/shmem_fs.h
index aa08fa8..9291ac3 100644
--- a/include/linux/shmem_fs.h
+++ b/include/linux/shmem_fs.h
@@ -8,22 +8,15 @@
/* inode in-kernel data */
-#define SHMEM_NR_DIRECT 16
-
-#define SHMEM_SYMLINK_INLINE_LEN (SHMEM_NR_DIRECT * sizeof(swp_entry_t))
-
struct shmem_inode_info {
spinlock_t lock;
unsigned long flags;
unsigned long alloced; /* data pages alloced to file */
- unsigned long swapped; /* subtotal assigned to swap */
- unsigned long next_index; /* highest alloced index + 1 */
- struct shared_policy policy; /* NUMA memory alloc policy */
- struct page *i_indirect; /* top indirect blocks page */
union {
- swp_entry_t i_direct[SHMEM_NR_DIRECT]; /* first blocks */
- char inline_symlink[SHMEM_SYMLINK_INLINE_LEN];
+ unsigned long swapped; /* subtotal assigned to swap */
+ char *symlink; /* unswappable short symlink */
};
+ struct shared_policy policy; /* NUMA memory alloc policy */
struct list_head swaplist; /* chain of maybes on swap */
struct list_head xattr_list; /* list of shmem_xattr */
struct inode vfs_inode;
@@ -49,7 +42,7 @@
/*
* Functions in mm/shmem.c called directly from elsewhere:
*/
-extern int init_tmpfs(void);
+extern int shmem_init(void);
extern int shmem_fill_super(struct super_block *sb, void *data, int silent);
extern struct file *shmem_file_setup(const char *name,
loff_t size, unsigned long flags);
@@ -59,8 +52,6 @@
pgoff_t index, gfp_t gfp_mask);
extern void shmem_truncate_range(struct inode *inode, loff_t start, loff_t end);
extern int shmem_unuse(swp_entry_t entry, struct page *page);
-extern void mem_cgroup_get_shmem_target(struct inode *inode, pgoff_t pgoff,
- struct page **pagep, swp_entry_t *ent);
static inline struct page *shmem_read_mapping_page(
struct address_space *mapping, pgoff_t index)
diff --git a/include/linux/socket.h b/include/linux/socket.h
index e17f822..d0e77f6 100644
--- a/include/linux/socket.h
+++ b/include/linux/socket.h
@@ -8,8 +8,10 @@
#define _K_SS_ALIGNSIZE (__alignof__ (struct sockaddr *))
/* Implementation specific desired alignment */
+typedef unsigned short __kernel_sa_family_t;
+
struct __kernel_sockaddr_storage {
- unsigned short ss_family; /* address family */
+ __kernel_sa_family_t ss_family; /* address family */
/* Following field(s) are implementation specific */
char __data[_K_SS_MAXSIZE - sizeof(unsigned short)];
/* space to achieve desired size, */
@@ -35,7 +37,7 @@
extern void socket_seq_show(struct seq_file *seq);
#endif
-typedef unsigned short sa_family_t;
+typedef __kernel_sa_family_t sa_family_t;
/*
* 1003.1g requires sa_family_t and that sa_data is char.
diff --git a/include/linux/swapops.h b/include/linux/swapops.h
index cd42e30..2189d3f 100644
--- a/include/linux/swapops.h
+++ b/include/linux/swapops.h
@@ -1,3 +1,8 @@
+#ifndef _LINUX_SWAPOPS_H
+#define _LINUX_SWAPOPS_H
+
+#include <linux/radix-tree.h>
+
/*
* swapcache pages are stored in the swapper_space radix tree. We want to
* get good packing density in that tree, so the index should be dense in
@@ -76,6 +81,22 @@
return __swp_entry_to_pte(arch_entry);
}
+static inline swp_entry_t radix_to_swp_entry(void *arg)
+{
+ swp_entry_t entry;
+
+ entry.val = (unsigned long)arg >> RADIX_TREE_EXCEPTIONAL_SHIFT;
+ return entry;
+}
+
+static inline void *swp_to_radix_entry(swp_entry_t entry)
+{
+ unsigned long value;
+
+ value = entry.val << RADIX_TREE_EXCEPTIONAL_SHIFT;
+ return (void *)(value | RADIX_TREE_EXCEPTIONAL_ENTRY);
+}
+
#ifdef CONFIG_MIGRATION
static inline swp_entry_t make_migration_entry(struct page *page, int write)
{
@@ -169,3 +190,5 @@
return 0;
}
#endif
+
+#endif /* _LINUX_SWAPOPS_H */
diff --git a/include/linux/thermal.h b/include/linux/thermal.h
index d3ec89f..47b4a27 100644
--- a/include/linux/thermal.h
+++ b/include/linux/thermal.h
@@ -85,22 +85,6 @@
((long)t-2732+5)/10 : ((long)t-2732-5)/10)
#define CELSIUS_TO_KELVIN(t) ((t)*10+2732)
-#if defined(CONFIG_THERMAL_HWMON)
-/* thermal zone devices with the same type share one hwmon device */
-struct thermal_hwmon_device {
- char type[THERMAL_NAME_LENGTH];
- struct device *device;
- int count;
- struct list_head tz_list;
- struct list_head node;
-};
-
-struct thermal_hwmon_attr {
- struct device_attribute attr;
- char name[16];
-};
-#endif
-
struct thermal_zone_device {
int id;
char type[THERMAL_NAME_LENGTH];
@@ -120,12 +104,6 @@
struct mutex lock; /* protect cooling devices list */
struct list_head node;
struct delayed_work poll_queue;
-#if defined(CONFIG_THERMAL_HWMON)
- struct list_head hwmon_node;
- struct thermal_hwmon_device *hwmon;
- struct thermal_hwmon_attr temp_input; /* hwmon sys attr */
- struct thermal_hwmon_attr temp_crit; /* hwmon sys attr */
-#endif
};
/* Adding event notification support elements */
#define THERMAL_GENL_FAMILY_NAME "thermal_event"
diff --git a/include/net/cipso_ipv4.h b/include/net/cipso_ipv4.h
index 3b93874..9808877 100644
--- a/include/net/cipso_ipv4.h
+++ b/include/net/cipso_ipv4.h
@@ -8,7 +8,7 @@
* have chosen to adopt the protocol and over the years it has become a
* de-facto standard for labeled networking.
*
- * Author: Paul Moore <paul.moore@hp.com>
+ * Author: Paul Moore <paul@paul-moore.com>
*
*/
diff --git a/include/net/dst.h b/include/net/dst.h
index 29e2557..13d507d 100644
--- a/include/net/dst.h
+++ b/include/net/dst.h
@@ -37,7 +37,7 @@
unsigned long _metrics;
unsigned long expires;
struct dst_entry *path;
- struct neighbour *_neighbour;
+ struct neighbour __rcu *_neighbour;
#ifdef CONFIG_XFRM
struct xfrm_state *xfrm;
#else
@@ -88,12 +88,17 @@
static inline struct neighbour *dst_get_neighbour(struct dst_entry *dst)
{
- return dst->_neighbour;
+ return rcu_dereference(dst->_neighbour);
+}
+
+static inline struct neighbour *dst_get_neighbour_raw(struct dst_entry *dst)
+{
+ return rcu_dereference_raw(dst->_neighbour);
}
static inline void dst_set_neighbour(struct dst_entry *dst, struct neighbour *neigh)
{
- dst->_neighbour = neigh;
+ rcu_assign_pointer(dst->_neighbour, neigh);
}
extern u32 *dst_cow_metrics_generic(struct dst_entry *dst, unsigned long old);
@@ -382,8 +387,12 @@
static inline void dst_confirm(struct dst_entry *dst)
{
if (dst) {
- struct neighbour *n = dst_get_neighbour(dst);
+ struct neighbour *n;
+
+ rcu_read_lock();
+ n = dst_get_neighbour(dst);
neigh_confirm(n);
+ rcu_read_unlock();
}
}
diff --git a/include/net/inet_sock.h b/include/net/inet_sock.h
index caaff5f..b897d6e 100644
--- a/include/net/inet_sock.h
+++ b/include/net/inet_sock.h
@@ -238,7 +238,7 @@
{
__u8 flags = 0;
- if (inet_sk(sk)->transparent)
+ if (inet_sk(sk)->transparent || inet_sk(sk)->hdrincl)
flags |= FLOWI_FLAG_ANYSRC;
if (sk->sk_protocol == IPPROTO_TCP)
flags |= FLOWI_FLAG_PRECOW_METRICS;
diff --git a/include/net/netlabel.h b/include/net/netlabel.h
index f21a16e..f674409 100644
--- a/include/net/netlabel.h
+++ b/include/net/netlabel.h
@@ -4,7 +4,7 @@
* The NetLabel system manages static and dynamic label mappings for network
* protocols such as CIPSO and RIPSO.
*
- * Author: Paul Moore <paul.moore@hp.com>
+ * Author: Paul Moore <paul@paul-moore.com>
*
*/
diff --git a/include/net/secure_seq.h b/include/net/secure_seq.h
new file mode 100644
index 0000000..d97f689
--- /dev/null
+++ b/include/net/secure_seq.h
@@ -0,0 +1,20 @@
+#ifndef _NET_SECURE_SEQ
+#define _NET_SECURE_SEQ
+
+#include <linux/types.h>
+
+extern __u32 secure_ip_id(__be32 daddr);
+extern __u32 secure_ipv6_id(const __be32 daddr[4]);
+extern u32 secure_ipv4_port_ephemeral(__be32 saddr, __be32 daddr, __be16 dport);
+extern u32 secure_ipv6_port_ephemeral(const __be32 *saddr, const __be32 *daddr,
+ __be16 dport);
+extern __u32 secure_tcp_sequence_number(__be32 saddr, __be32 daddr,
+ __be16 sport, __be16 dport);
+extern __u32 secure_tcpv6_sequence_number(__be32 *saddr, __be32 *daddr,
+ __be16 sport, __be16 dport);
+extern u64 secure_dccp_sequence_number(__be32 saddr, __be32 daddr,
+ __be16 sport, __be16 dport);
+extern u64 secure_dccpv6_sequence_number(__be32 *saddr, __be32 *daddr,
+ __be16 sport, __be16 dport);
+
+#endif /* _NET_SECURE_SEQ */
diff --git a/include/scsi/osd_ore.h b/include/scsi/osd_ore.h
new file mode 100644
index 0000000..c5c5e00
--- /dev/null
+++ b/include/scsi/osd_ore.h
@@ -0,0 +1,125 @@
+/*
+ * Copyright (C) 2011
+ * Boaz Harrosh <bharrosh@panasas.com>
+ *
+ * Public Declarations of the ORE API
+ *
+ * This file is part of the ORE (Object Raid Engine) library.
+ *
+ * ORE 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. (GPL v2)
+ *
+ * ORE 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 the ORE; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+#ifndef __ORE_H__
+#define __ORE_H__
+
+#include <scsi/osd_initiator.h>
+#include <scsi/osd_attributes.h>
+#include <scsi/osd_sec.h>
+#include <linux/pnfs_osd_xdr.h>
+
+struct ore_comp {
+ struct osd_obj_id obj;
+ u8 cred[OSD_CAP_LEN];
+};
+
+struct ore_layout {
+ /* Our way of looking at the data_map */
+ unsigned stripe_unit;
+ unsigned mirrors_p1;
+
+ unsigned group_width;
+ u64 group_depth;
+ unsigned group_count;
+};
+
+struct ore_components {
+ unsigned numdevs; /* Num of devices in array */
+ /* If @single_comp == EC_SINGLE_COMP, @comps points to a single
+ * component. else there are @numdevs components
+ */
+ enum EC_COMP_USAGE {
+ EC_SINGLE_COMP = 0, EC_MULTPLE_COMPS = 0xffffffff
+ } single_comp;
+ struct ore_comp *comps;
+ struct osd_dev **ods; /* osd_dev array */
+};
+
+struct ore_io_state;
+typedef void (*ore_io_done_fn)(struct ore_io_state *ios, void *private);
+
+struct ore_io_state {
+ struct kref kref;
+
+ void *private;
+ ore_io_done_fn done;
+
+ struct ore_layout *layout;
+ struct ore_components *comps;
+
+ /* Global read/write IO*/
+ loff_t offset;
+ unsigned long length;
+ void *kern_buff;
+
+ struct page **pages;
+ unsigned nr_pages;
+ unsigned pgbase;
+ unsigned pages_consumed;
+
+ /* Attributes */
+ unsigned in_attr_len;
+ struct osd_attr *in_attr;
+ unsigned out_attr_len;
+ struct osd_attr *out_attr;
+
+ bool reading;
+
+ /* Variable array of size numdevs */
+ unsigned numdevs;
+ struct ore_per_dev_state {
+ struct osd_request *or;
+ struct bio *bio;
+ loff_t offset;
+ unsigned length;
+ unsigned dev;
+ } per_dev[];
+};
+
+static inline unsigned ore_io_state_size(unsigned numdevs)
+{
+ return sizeof(struct ore_io_state) +
+ sizeof(struct ore_per_dev_state) * numdevs;
+}
+
+/* ore.c */
+int ore_get_rw_state(struct ore_layout *layout, struct ore_components *comps,
+ bool is_reading, u64 offset, u64 length,
+ struct ore_io_state **ios);
+int ore_get_io_state(struct ore_layout *layout, struct ore_components *comps,
+ struct ore_io_state **ios);
+void ore_put_io_state(struct ore_io_state *ios);
+
+int ore_check_io(struct ore_io_state *ios, u64 *resid);
+
+int ore_create(struct ore_io_state *ios);
+int ore_remove(struct ore_io_state *ios);
+int ore_write(struct ore_io_state *ios);
+int ore_read(struct ore_io_state *ios);
+int ore_truncate(struct ore_layout *layout, struct ore_components *comps,
+ u64 size);
+
+int extract_attr_from_ios(struct ore_io_state *ios, struct osd_attr *attr);
+
+extern const struct osd_attr g_attr_logical_length;
+
+#endif
diff --git a/include/sound/tlv320aic3x.h b/include/sound/tlv320aic3x.h
index 99e0308..ffd9bc7 100644
--- a/include/sound/tlv320aic3x.h
+++ b/include/sound/tlv320aic3x.h
@@ -1,7 +1,7 @@
/*
* Platform data for Texas Instruments TLV320AIC3x codec
*
- * Author: Jarkko Nikula <jhnikula@gmail.com>
+ * Author: Jarkko Nikula <jarkko.nikula@bitmer.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
diff --git a/include/sound/wm8915.h b/include/sound/wm8996.h
similarity index 63%
rename from include/sound/wm8915.h
rename to include/sound/wm8996.h
index 5817d76..ea4d88f 100644
--- a/include/sound/wm8915.h
+++ b/include/sound/wm8996.h
@@ -1,5 +1,5 @@
/*
- * linux/sound/wm8915.h -- Platform data for WM8915
+ * linux/sound/wm8996.h -- Platform data for WM8996
*
* Copyright 2011 Wolfson Microelectronics. PLC.
*
@@ -8,14 +8,14 @@
* published by the Free Software Foundation.
*/
-#ifndef __LINUX_SND_WM8903_H
-#define __LINUX_SND_WM8903_H
+#ifndef __LINUX_SND_WM8996_H
+#define __LINUX_SND_WM8996_H
-enum wm8915_inmode {
- WM8915_DIFFERRENTIAL_1 = 0, /* IN1xP - IN1xN */
- WM8915_INVERTING = 1, /* IN1xN */
- WM8915_NON_INVERTING = 2, /* IN1xP */
- WM8915_DIFFERENTIAL_2 = 3, /* IN2xP - IN2xP */
+enum wm8996_inmode {
+ WM8996_DIFFERRENTIAL_1 = 0, /* IN1xP - IN1xN */
+ WM8996_INVERTING = 1, /* IN1xN */
+ WM8996_NON_INVERTING = 2, /* IN1xP */
+ WM8996_DIFFERENTIAL_2 = 3, /* IN2xP - IN2xP */
};
/**
@@ -25,23 +25,23 @@
* Configurations are expected to be generated using the ReTune Mobile
* control panel in WISCE - see http://www.wolfsonmicro.com/wisce/
*/
-struct wm8915_retune_mobile_config {
+struct wm8996_retune_mobile_config {
const char *name;
int rate;
u16 regs[20];
};
-#define WM8915_SET_DEFAULT 0x10000
+#define WM8996_SET_DEFAULT 0x10000
-struct wm8915_pdata {
+struct wm8996_pdata {
int irq_flags; /** Set IRQ trigger flags; default active low */
int ldo_ena; /** GPIO for LDO1; -1 for none */
int micdet_def; /** Default MICDET_SRC/HP1FB_SRC/MICD_BIAS */
- enum wm8915_inmode inl_mode;
- enum wm8915_inmode inr_mode;
+ enum wm8996_inmode inl_mode;
+ enum wm8996_inmode inr_mode;
u32 spkmute_seq; /** Value for register 0x802 */
@@ -49,7 +49,7 @@
u32 gpio_default[5];
int num_retune_mobile_cfgs;
- struct wm8915_retune_mobile_config *retune_mobile_cfgs;
+ struct wm8996_retune_mobile_config *retune_mobile_cfgs;
};
#endif
diff --git a/include/trace/events/ext4.h b/include/trace/events/ext4.h
index 6363193..b50a547 100644
--- a/include/trace/events/ext4.h
+++ b/include/trace/events/ext4.h
@@ -23,7 +23,7 @@
TP_STRUCT__entry(
__field( dev_t, dev )
__field( ino_t, ino )
- __field( umode_t, mode )
+ __field( __u16, mode )
__field( uid_t, uid )
__field( gid_t, gid )
__field( __u64, blocks )
@@ -52,7 +52,7 @@
TP_STRUCT__entry(
__field( dev_t, dev )
__field( ino_t, dir )
- __field( umode_t, mode )
+ __field( __u16, mode )
),
TP_fast_assign(
@@ -75,7 +75,7 @@
__field( dev_t, dev )
__field( ino_t, ino )
__field( ino_t, dir )
- __field( umode_t, mode )
+ __field( __u16, mode )
),
TP_fast_assign(
@@ -725,7 +725,7 @@
TP_STRUCT__entry(
__field( dev_t, dev )
__field( ino_t, ino )
- __field( umode_t, mode )
+ __field( __u16, mode )
__field( __u64, block )
__field( unsigned long, count )
__field( int, flags )
@@ -1012,7 +1012,7 @@
TP_STRUCT__entry(
__field( dev_t, dev )
__field( ino_t, ino )
- __field( umode_t, mode )
+ __field( __u16, mode )
__field( int, is_metadata )
__field( __u64, block )
),
@@ -1039,7 +1039,7 @@
TP_STRUCT__entry(
__field( dev_t, dev )
__field( ino_t, ino )
- __field( umode_t, mode )
+ __field( __u16, mode )
__field( __u64, i_blocks )
__field( int, used_blocks )
__field( int, reserved_data_blocks )
@@ -1076,7 +1076,7 @@
TP_STRUCT__entry(
__field( dev_t, dev )
__field( ino_t, ino )
- __field( umode_t, mode )
+ __field( __u16, mode )
__field( __u64, i_blocks )
__field( int, md_needed )
__field( int, reserved_data_blocks )
@@ -1110,7 +1110,7 @@
TP_STRUCT__entry(
__field( dev_t, dev )
__field( ino_t, ino )
- __field( umode_t, mode )
+ __field( __u16, mode )
__field( __u64, i_blocks )
__field( int, freed_blocks )
__field( int, reserved_data_blocks )
@@ -1518,6 +1518,77 @@
(unsigned long) __entry->ino)
);
+TRACE_EVENT(ext4_journal_start,
+ TP_PROTO(struct super_block *sb, int nblocks, unsigned long IP),
+
+ TP_ARGS(sb, nblocks, IP),
+
+ TP_STRUCT__entry(
+ __field( dev_t, dev )
+ __field( int, nblocks )
+ __field(unsigned long, ip )
+ ),
+
+ TP_fast_assign(
+ __entry->dev = sb->s_dev;
+ __entry->nblocks = nblocks;
+ __entry->ip = IP;
+ ),
+
+ TP_printk("dev %d,%d nblocks %d caller %pF",
+ MAJOR(__entry->dev), MINOR(__entry->dev),
+ __entry->nblocks, (void *)__entry->ip)
+);
+
+DECLARE_EVENT_CLASS(ext4__trim,
+ TP_PROTO(struct super_block *sb,
+ ext4_group_t group,
+ ext4_grpblk_t start,
+ ext4_grpblk_t len),
+
+ TP_ARGS(sb, group, start, len),
+
+ TP_STRUCT__entry(
+ __field( int, dev_major )
+ __field( int, dev_minor )
+ __field( __u32, group )
+ __field( int, start )
+ __field( int, len )
+ ),
+
+ TP_fast_assign(
+ __entry->dev_major = MAJOR(sb->s_dev);
+ __entry->dev_minor = MINOR(sb->s_dev);
+ __entry->group = group;
+ __entry->start = start;
+ __entry->len = len;
+ ),
+
+ TP_printk("dev %d,%d group %u, start %d, len %d",
+ __entry->dev_major, __entry->dev_minor,
+ __entry->group, __entry->start, __entry->len)
+);
+
+DEFINE_EVENT(ext4__trim, ext4_trim_extent,
+
+ TP_PROTO(struct super_block *sb,
+ ext4_group_t group,
+ ext4_grpblk_t start,
+ ext4_grpblk_t len),
+
+ TP_ARGS(sb, group, start, len)
+);
+
+DEFINE_EVENT(ext4__trim, ext4_trim_all_free,
+
+ TP_PROTO(struct super_block *sb,
+ ext4_group_t group,
+ ext4_grpblk_t start,
+ ext4_grpblk_t len),
+
+ TP_ARGS(sb, group, start, len)
+);
+
#endif /* _TRACE_EXT4_H */
/* This part must be outside protection */
diff --git a/include/trace/events/jbd2.h b/include/trace/events/jbd2.h
index bf16545..7596441 100644
--- a/include/trace/events/jbd2.h
+++ b/include/trace/events/jbd2.h
@@ -26,8 +26,8 @@
__entry->result = result;
),
- TP_printk("dev %s result %d",
- jbd2_dev_to_name(__entry->dev), __entry->result)
+ TP_printk("dev %d,%d result %d",
+ MAJOR(__entry->dev), MINOR(__entry->dev), __entry->result)
);
DECLARE_EVENT_CLASS(jbd2_commit,
@@ -48,9 +48,9 @@
__entry->transaction = commit_transaction->t_tid;
),
- TP_printk("dev %s transaction %d sync %d",
- jbd2_dev_to_name(__entry->dev), __entry->transaction,
- __entry->sync_commit)
+ TP_printk("dev %d,%d transaction %d sync %d",
+ MAJOR(__entry->dev), MINOR(__entry->dev),
+ __entry->transaction, __entry->sync_commit)
);
DEFINE_EVENT(jbd2_commit, jbd2_start_commit,
@@ -100,9 +100,9 @@
__entry->head = journal->j_tail_sequence;
),
- TP_printk("dev %s transaction %d sync %d head %d",
- jbd2_dev_to_name(__entry->dev), __entry->transaction,
- __entry->sync_commit, __entry->head)
+ TP_printk("dev %d,%d transaction %d sync %d head %d",
+ MAJOR(__entry->dev), MINOR(__entry->dev),
+ __entry->transaction, __entry->sync_commit, __entry->head)
);
TRACE_EVENT(jbd2_submit_inode_data,
@@ -120,8 +120,9 @@
__entry->ino = inode->i_ino;
),
- TP_printk("dev %s ino %lu",
- jbd2_dev_to_name(__entry->dev), (unsigned long) __entry->ino)
+ TP_printk("dev %d,%d ino %lu",
+ MAJOR(__entry->dev), MINOR(__entry->dev),
+ (unsigned long) __entry->ino)
);
TRACE_EVENT(jbd2_run_stats,
@@ -156,9 +157,9 @@
__entry->blocks_logged = stats->rs_blocks_logged;
),
- TP_printk("dev %s tid %lu wait %u running %u locked %u flushing %u "
+ TP_printk("dev %d,%d tid %lu wait %u running %u locked %u flushing %u "
"logging %u handle_count %u blocks %u blocks_logged %u",
- jbd2_dev_to_name(__entry->dev), __entry->tid,
+ MAJOR(__entry->dev), MINOR(__entry->dev), __entry->tid,
jiffies_to_msecs(__entry->wait),
jiffies_to_msecs(__entry->running),
jiffies_to_msecs(__entry->locked),
@@ -192,9 +193,9 @@
__entry->dropped = stats->cs_dropped;
),
- TP_printk("dev %s tid %lu chp_time %u forced_to_close %u "
+ TP_printk("dev %d,%d tid %lu chp_time %u forced_to_close %u "
"written %u dropped %u",
- jbd2_dev_to_name(__entry->dev), __entry->tid,
+ MAJOR(__entry->dev), MINOR(__entry->dev), __entry->tid,
jiffies_to_msecs(__entry->chp_time),
__entry->forced_to_close, __entry->written, __entry->dropped)
);
@@ -222,9 +223,10 @@
__entry->freed = freed;
),
- TP_printk("dev %s from %u to %u offset %lu freed %lu",
- jbd2_dev_to_name(__entry->dev), __entry->tail_sequence,
- __entry->first_tid, __entry->block_nr, __entry->freed)
+ TP_printk("dev %d,%d from %u to %u offset %lu freed %lu",
+ MAJOR(__entry->dev), MINOR(__entry->dev),
+ __entry->tail_sequence, __entry->first_tid,
+ __entry->block_nr, __entry->freed)
);
#endif /* _TRACE_JBD2_H */
diff --git a/include/video/omapdss.h b/include/video/omapdss.h
index 892b97f..3b55ef2 100644
--- a/include/video/omapdss.h
+++ b/include/video/omapdss.h
@@ -21,8 +21,6 @@
#include <linux/list.h>
#include <linux/kobject.h>
#include <linux/device.h>
-#include <linux/platform_device.h>
-#include <asm/atomic.h>
#define DISPC_IRQ_FRAMEDONE (1 << 0)
#define DISPC_IRQ_VSYNC (1 << 1)
@@ -136,12 +134,6 @@
OMAP_DSS_DISPLAY_CAP_TEAR_ELIM = 1 << 1,
};
-enum omap_dss_update_mode {
- OMAP_DSS_UPDATE_DISABLED = 0,
- OMAP_DSS_UPDATE_AUTO,
- OMAP_DSS_UPDATE_MANUAL,
-};
-
enum omap_dss_display_state {
OMAP_DSS_DISPLAY_DISABLED = 0,
OMAP_DSS_DISPLAY_ACTIVE,
@@ -246,7 +238,7 @@
/* Board specific data */
struct omap_dss_board_info {
- int (*get_last_off_on_transaction_id)(struct device *dev);
+ int (*get_context_loss_count)(struct device *dev);
int num_devices;
struct omap_dss_device **devices;
struct omap_dss_device *default_device;
@@ -266,8 +258,6 @@
struct omap_display_platform_data {
struct omap_dss_board_info *board_data;
/* TODO: Additional members to be added when PM is considered */
-
- bool (*opt_clock_available)(const char *clk_role);
};
struct omap_video_timings {
@@ -300,6 +290,12 @@
extern const struct omap_video_timings omap_dss_ntsc_timings;
#endif
+struct omap_dss_cpr_coefs {
+ s16 rr, rg, rb;
+ s16 gr, gg, gb;
+ s16 br, bg, bb;
+};
+
struct omap_overlay_info {
bool enabled;
@@ -359,6 +355,9 @@
bool trans_enabled;
bool alpha_enabled;
+
+ bool cpr_enable;
+ struct omap_dss_cpr_coefs cpr_coefs;
};
struct omap_overlay_manager {
@@ -526,11 +525,6 @@
int (*resume)(struct omap_dss_device *display);
int (*run_test)(struct omap_dss_device *display, int test);
- int (*set_update_mode)(struct omap_dss_device *dssdev,
- enum omap_dss_update_mode);
- enum omap_dss_update_mode (*get_update_mode)(
- struct omap_dss_device *dssdev);
-
int (*update)(struct omap_dss_device *dssdev,
u16 x, u16 y, u16 w, u16 h);
int (*sync)(struct omap_dss_device *dssdev);
diff --git a/init/main.c b/init/main.c
index d7211fa..9c51ee7 100644
--- a/init/main.c
+++ b/init/main.c
@@ -369,9 +369,12 @@
init_idle_bootup_task(current);
preempt_enable_no_resched();
schedule();
- preempt_disable();
+
+ /* At this point, we can enable user mode helper functionality */
+ usermodehelper_enable();
/* Call into cpu_idle with preempt disabled */
+ preempt_disable();
cpu_idle();
}
@@ -715,7 +718,7 @@
{
cpuset_init_smp();
usermodehelper_init();
- init_tmpfs();
+ shmem_init();
driver_init();
init_irq_proc();
do_ctors();
diff --git a/ipc/shm.c b/ipc/shm.c
index 9fb044f3b..02ecf2c 100644
--- a/ipc/shm.c
+++ b/ipc/shm.c
@@ -105,9 +105,16 @@
}
#endif
-void __init shm_init (void)
+static int __init ipc_ns_init(void)
{
shm_init_ns(&init_ipc_ns);
+ return 0;
+}
+
+pure_initcall(ipc_ns_init);
+
+void __init shm_init (void)
+{
ipc_init_proc_interface("sysvipc/shm",
#if BITS_PER_LONG <= 32
" key shmid perms size cpid lpid nattch uid gid cuid cgid atime dtime ctime rss swap\n",
@@ -294,7 +301,7 @@
void shm_destroy_orphaned(struct ipc_namespace *ns)
{
down_write(&shm_ids(ns).rw_mutex);
- if (&shm_ids(ns).in_use)
+ if (shm_ids(ns).in_use)
idr_for_each(&shm_ids(ns).ipcs_idr, &shm_try_destroy_orphaned, ns);
up_write(&shm_ids(ns).rw_mutex);
}
@@ -304,9 +311,12 @@
{
struct ipc_namespace *ns = task->nsproxy->ipc_ns;
+ if (shm_ids(ns).in_use == 0)
+ return;
+
/* Destroy all already created segments, but not mapped yet */
down_write(&shm_ids(ns).rw_mutex);
- if (&shm_ids(ns).in_use)
+ if (shm_ids(ns).in_use)
idr_for_each(&shm_ids(ns).ipcs_idr, &shm_try_destroy_current, ns);
up_write(&shm_ids(ns).rw_mutex);
}
diff --git a/kernel/Makefile b/kernel/Makefile
index d06467f..eca595e 100644
--- a/kernel/Makefile
+++ b/kernel/Makefile
@@ -10,7 +10,7 @@
kthread.o wait.o kfifo.o sys_ni.o posix-cpu-timers.o mutex.o \
hrtimer.o rwsem.o nsproxy.o srcu.o semaphore.o \
notifier.o ksysfs.o pm_qos_params.o sched_clock.o cred.o \
- async.o range.o jump_label.o
+ async.o range.o
obj-y += groups.o
ifdef CONFIG_FUNCTION_TRACER
@@ -107,6 +107,7 @@
obj-$(CONFIG_USER_RETURN_NOTIFIER) += user-return-notifier.o
obj-$(CONFIG_PADATA) += padata.o
obj-$(CONFIG_CRASH_DUMP) += crash_dump.o
+obj-$(CONFIG_JUMP_LABEL) += jump_label.o
ifneq ($(CONFIG_SCHED_OMIT_FRAME_POINTER),y)
# According to Alan Modra <alan@linuxcare.com.au>, the -fno-omit-frame-pointer is
diff --git a/kernel/cred.c b/kernel/cred.c
index 174fa84..8ef31f5 100644
--- a/kernel/cred.c
+++ b/kernel/cred.c
@@ -508,10 +508,8 @@
key_fsgid_changed(task);
/* do it
- * - What if a process setreuid()'s and this brings the
- * new uid over his NPROC rlimit? We can check this now
- * cheaply with the new uid cache, so if it matters
- * we should be checking for it. -DaveM
+ * RLIMIT_NPROC limits on user->processes have already been checked
+ * in set_user().
*/
alter_cred_subscribers(new, 2);
if (new->user != old->user)
diff --git a/kernel/debug/gdbstub.c b/kernel/debug/gdbstub.c
index a11db95..3487248 100644
--- a/kernel/debug/gdbstub.c
+++ b/kernel/debug/gdbstub.c
@@ -42,6 +42,8 @@
/* Our I/O buffers. */
static char remcom_in_buffer[BUFMAX];
static char remcom_out_buffer[BUFMAX];
+static int gdbstub_use_prev_in_buf;
+static int gdbstub_prev_in_buf_pos;
/* Storage for the registers, in GDB format. */
static unsigned long gdb_regs[(NUMREGBYTES +
@@ -58,6 +60,13 @@
int ret = -1;
int i;
+ if (unlikely(gdbstub_use_prev_in_buf)) {
+ if (gdbstub_prev_in_buf_pos < gdbstub_use_prev_in_buf)
+ return remcom_in_buffer[gdbstub_prev_in_buf_pos++];
+ else
+ gdbstub_use_prev_in_buf = 0;
+ }
+
/* poll any additional I/O interfaces that are defined */
while (ret < 0)
for (i = 0; kdb_poll_funcs[i] != NULL; i++) {
@@ -109,7 +118,6 @@
buffer[count] = ch;
count = count + 1;
}
- buffer[count] = 0;
if (ch == '#') {
xmitcsum = hex_to_bin(gdbstub_read_wait()) << 4;
@@ -124,6 +132,7 @@
if (dbg_io_ops->flush)
dbg_io_ops->flush();
}
+ buffer[count] = 0;
} while (checksum != xmitcsum);
}
@@ -1082,12 +1091,11 @@
case 'c':
strcpy(remcom_in_buffer, cmd);
return 0;
- case '?':
- gdb_cmd_status(ks);
- break;
- case '\0':
- strcpy(remcom_out_buffer, "");
- break;
+ case '$':
+ strcpy(remcom_in_buffer, cmd);
+ gdbstub_use_prev_in_buf = strlen(remcom_in_buffer);
+ gdbstub_prev_in_buf_pos = 0;
+ return 0;
}
dbg_io_ops->write_char('+');
put_packet(remcom_out_buffer);
diff --git a/kernel/debug/kdb/kdb_bt.c b/kernel/debug/kdb/kdb_bt.c
index 2f62fe8..7179eac 100644
--- a/kernel/debug/kdb/kdb_bt.c
+++ b/kernel/debug/kdb/kdb_bt.c
@@ -112,9 +112,8 @@
unsigned long addr;
long offset;
- kdbgetintenv("BTARGS", &argcount); /* Arguments to print */
- kdbgetintenv("BTAPROMPT", &btaprompt); /* Prompt after each
- * proc in bta */
+ /* Prompt after each proc in bta */
+ kdbgetintenv("BTAPROMPT", &btaprompt);
if (strcmp(argv[0], "bta") == 0) {
struct task_struct *g, *p;
diff --git a/kernel/debug/kdb/kdb_cmds b/kernel/debug/kdb/kdb_cmds
index 56c88e4..9834ad3 100644
--- a/kernel/debug/kdb/kdb_cmds
+++ b/kernel/debug/kdb/kdb_cmds
@@ -18,16 +18,12 @@
endefcmd
defcmd dumpall "" "First line debugging"
- set BTSYMARG 1
- set BTARGS 9
pid R
-dumpcommon
-bta
endefcmd
defcmd dumpcpu "" "Same as dumpall but only tasks on cpus"
- set BTSYMARG 1
- set BTARGS 9
pid R
-dumpcommon
-btc
diff --git a/kernel/debug/kdb/kdb_debugger.c b/kernel/debug/kdb/kdb_debugger.c
index dd0b1b7..d9ca9aa 100644
--- a/kernel/debug/kdb/kdb_debugger.c
+++ b/kernel/debug/kdb/kdb_debugger.c
@@ -30,6 +30,8 @@
int kdb_poll_idx = 1;
EXPORT_SYMBOL_GPL(kdb_poll_idx);
+static struct kgdb_state *kdb_ks;
+
int kdb_stub(struct kgdb_state *ks)
{
int error = 0;
@@ -39,6 +41,7 @@
kdb_dbtrap_t db_result = KDB_DB_NOBPT;
int i;
+ kdb_ks = ks;
if (KDB_STATE(REENTRY)) {
reason = KDB_REASON_SWITCH;
KDB_STATE_CLEAR(REENTRY);
@@ -123,20 +126,8 @@
KDB_STATE_CLEAR(PAGER);
kdbnearsym_cleanup();
if (error == KDB_CMD_KGDB) {
- if (KDB_STATE(DOING_KGDB) || KDB_STATE(DOING_KGDB2)) {
- /*
- * This inteface glue which allows kdb to transition in into
- * the gdb stub. In order to do this the '?' or '' gdb serial
- * packet response is processed here. And then control is
- * passed to the gdbstub.
- */
- if (KDB_STATE(DOING_KGDB))
- gdbstub_state(ks, "?");
- else
- gdbstub_state(ks, "");
+ if (KDB_STATE(DOING_KGDB))
KDB_STATE_CLEAR(DOING_KGDB);
- KDB_STATE_CLEAR(DOING_KGDB2);
- }
return DBG_PASS_EVENT;
}
kdb_bp_install(ks->linux_regs);
@@ -166,3 +157,7 @@
return kgdb_info[ks->cpu].ret_state;
}
+void kdb_gdb_state_pass(char *buf)
+{
+ gdbstub_state(kdb_ks, buf);
+}
diff --git a/kernel/debug/kdb/kdb_io.c b/kernel/debug/kdb/kdb_io.c
index 96fdaac..4802eb5 100644
--- a/kernel/debug/kdb/kdb_io.c
+++ b/kernel/debug/kdb/kdb_io.c
@@ -31,15 +31,21 @@
int kdb_trap_printk;
-static void kgdb_transition_check(char *buffer)
+static int kgdb_transition_check(char *buffer)
{
- int slen = strlen(buffer);
- if (strncmp(buffer, "$?#3f", slen) != 0 &&
- strncmp(buffer, "$qSupported#37", slen) != 0 &&
- strncmp(buffer, "+$qSupported#37", slen) != 0) {
+ if (buffer[0] != '+' && buffer[0] != '$') {
KDB_STATE_SET(KGDB_TRANS);
kdb_printf("%s", buffer);
+ } else {
+ int slen = strlen(buffer);
+ if (slen > 3 && buffer[slen - 3] == '#') {
+ kdb_gdb_state_pass(buffer);
+ strcpy(buffer, "kgdb");
+ KDB_STATE_SET(DOING_KGDB);
+ return 1;
+ }
}
+ return 0;
}
static int kdb_read_get_key(char *buffer, size_t bufsize)
@@ -251,6 +257,10 @@
case 13: /* enter */
*lastchar++ = '\n';
*lastchar++ = '\0';
+ if (!KDB_STATE(KGDB_TRANS)) {
+ KDB_STATE_SET(KGDB_TRANS);
+ kdb_printf("%s", buffer);
+ }
kdb_printf("\n");
return buffer;
case 4: /* Del */
@@ -382,22 +392,26 @@
* printed characters if we think that
* kgdb is connecting, until the check
* fails */
- if (!KDB_STATE(KGDB_TRANS))
- kgdb_transition_check(buffer);
- else
+ if (!KDB_STATE(KGDB_TRANS)) {
+ if (kgdb_transition_check(buffer))
+ return buffer;
+ } else {
kdb_printf("%c", key);
+ }
}
/* Special escape to kgdb */
if (lastchar - buffer >= 5 &&
strcmp(lastchar - 5, "$?#3f") == 0) {
+ kdb_gdb_state_pass(lastchar - 5);
strcpy(buffer, "kgdb");
KDB_STATE_SET(DOING_KGDB);
return buffer;
}
- if (lastchar - buffer >= 14 &&
- strcmp(lastchar - 14, "$qSupported#37") == 0) {
+ if (lastchar - buffer >= 11 &&
+ strcmp(lastchar - 11, "$qSupported") == 0) {
+ kdb_gdb_state_pass(lastchar - 11);
strcpy(buffer, "kgdb");
- KDB_STATE_SET(DOING_KGDB2);
+ KDB_STATE_SET(DOING_KGDB);
return buffer;
}
}
diff --git a/kernel/debug/kdb/kdb_main.c b/kernel/debug/kdb/kdb_main.c
index be14779..63786e7 100644
--- a/kernel/debug/kdb/kdb_main.c
+++ b/kernel/debug/kdb/kdb_main.c
@@ -145,7 +145,6 @@
#endif
"RADIX=16",
"MDCOUNT=8", /* lines of md output */
- "BTARGS=9", /* 9 possible args in bt */
KDB_PLATFORM_ENV,
"DTABCOUNT=30",
"NOSECT=1",
@@ -172,6 +171,7 @@
(char *)0,
(char *)0,
(char *)0,
+ (char *)0,
};
static const int __nenv = (sizeof(__env) / sizeof(char *));
@@ -1386,7 +1386,7 @@
}
if (result == KDB_CMD_KGDB) {
- if (!(KDB_STATE(DOING_KGDB) || KDB_STATE(DOING_KGDB2)))
+ if (!KDB_STATE(DOING_KGDB))
kdb_printf("Entering please attach debugger "
"or use $D#44+ or $3#33\n");
break;
diff --git a/kernel/debug/kdb/kdb_private.h b/kernel/debug/kdb/kdb_private.h
index 35d69ed..e381d10 100644
--- a/kernel/debug/kdb/kdb_private.h
+++ b/kernel/debug/kdb/kdb_private.h
@@ -21,7 +21,6 @@
#define KDB_CMD_SS (-1003)
#define KDB_CMD_SSB (-1004)
#define KDB_CMD_KGDB (-1005)
-#define KDB_CMD_KGDB2 (-1006)
/* Internal debug flags */
#define KDB_DEBUG_FLAG_BP 0x0002 /* Breakpoint subsystem debug */
@@ -146,7 +145,6 @@
* keyboard on this cpu */
#define KDB_STATE_KEXEC 0x00040000 /* kexec issued */
#define KDB_STATE_DOING_KGDB 0x00080000 /* kgdb enter now issued */
-#define KDB_STATE_DOING_KGDB2 0x00100000 /* kgdb enter now issued */
#define KDB_STATE_KGDB_TRANS 0x00200000 /* Transition to kgdb */
#define KDB_STATE_ARCH 0xff000000 /* Reserved for arch
* specific use */
@@ -218,6 +216,7 @@
extern void kdb_send_sig_info(struct task_struct *p, struct siginfo *info);
extern void kdb_meminfo_proc_show(void);
extern char *kdb_getstr(char *, size_t, char *);
+extern void kdb_gdb_state_pass(char *buf);
/* Defines for kdb_symbol_print */
#define KDB_SP_SPACEB 0x0001 /* Space before string */
diff --git a/kernel/fork.c b/kernel/fork.c
index e7ceaca..8e6b6f4 100644
--- a/kernel/fork.c
+++ b/kernel/fork.c
@@ -1111,6 +1111,7 @@
p->real_cred->user != INIT_USER)
goto bad_fork_free;
}
+ current->flags &= ~PF_NPROC_EXCEEDED;
retval = copy_creds(p, clone_flags);
if (retval < 0)
diff --git a/kernel/futex.c b/kernel/futex.c
index 0a30897..11cbe05 100644
--- a/kernel/futex.c
+++ b/kernel/futex.c
@@ -218,6 +218,8 @@
* @uaddr: virtual address of the futex
* @fshared: 0 for a PROCESS_PRIVATE futex, 1 for PROCESS_SHARED
* @key: address where result is stored.
+ * @rw: mapping needs to be read/write (values: VERIFY_READ,
+ * VERIFY_WRITE)
*
* Returns a negative error code or 0
* The key words are stored in *key on success.
@@ -229,12 +231,12 @@
* lock_page() might sleep, the caller should not hold a spinlock.
*/
static int
-get_futex_key(u32 __user *uaddr, int fshared, union futex_key *key)
+get_futex_key(u32 __user *uaddr, int fshared, union futex_key *key, int rw)
{
unsigned long address = (unsigned long)uaddr;
struct mm_struct *mm = current->mm;
struct page *page, *page_head;
- int err;
+ int err, ro = 0;
/*
* The futex address must be "naturally" aligned.
@@ -262,8 +264,18 @@
again:
err = get_user_pages_fast(address, 1, 1, &page);
+ /*
+ * If write access is not required (eg. FUTEX_WAIT), try
+ * and get read-only access.
+ */
+ if (err == -EFAULT && rw == VERIFY_READ) {
+ err = get_user_pages_fast(address, 1, 0, &page);
+ ro = 1;
+ }
if (err < 0)
return err;
+ else
+ err = 0;
#ifdef CONFIG_TRANSPARENT_HUGEPAGE
page_head = page;
@@ -305,6 +317,13 @@
if (!page_head->mapping) {
unlock_page(page_head);
put_page(page_head);
+ /*
+ * ZERO_PAGE pages don't have a mapping. Avoid a busy loop
+ * trying to find one. RW mapping would have COW'd (and thus
+ * have a mapping) so this page is RO and won't ever change.
+ */
+ if ((page_head == ZERO_PAGE(address)))
+ return -EFAULT;
goto again;
}
@@ -316,6 +335,15 @@
* the object not the particular process.
*/
if (PageAnon(page_head)) {
+ /*
+ * A RO anonymous page will never change and thus doesn't make
+ * sense for futex operations.
+ */
+ if (ro) {
+ err = -EFAULT;
+ goto out;
+ }
+
key->both.offset |= FUT_OFF_MMSHARED; /* ref taken on mm */
key->private.mm = mm;
key->private.address = address;
@@ -327,9 +355,10 @@
get_futex_key_refs(key);
+out:
unlock_page(page_head);
put_page(page_head);
- return 0;
+ return err;
}
static inline void put_futex_key(union futex_key *key)
@@ -940,7 +969,7 @@
if (!bitset)
return -EINVAL;
- ret = get_futex_key(uaddr, flags & FLAGS_SHARED, &key);
+ ret = get_futex_key(uaddr, flags & FLAGS_SHARED, &key, VERIFY_READ);
if (unlikely(ret != 0))
goto out;
@@ -986,10 +1015,10 @@
int ret, op_ret;
retry:
- ret = get_futex_key(uaddr1, flags & FLAGS_SHARED, &key1);
+ ret = get_futex_key(uaddr1, flags & FLAGS_SHARED, &key1, VERIFY_READ);
if (unlikely(ret != 0))
goto out;
- ret = get_futex_key(uaddr2, flags & FLAGS_SHARED, &key2);
+ ret = get_futex_key(uaddr2, flags & FLAGS_SHARED, &key2, VERIFY_WRITE);
if (unlikely(ret != 0))
goto out_put_key1;
@@ -1243,10 +1272,11 @@
pi_state = NULL;
}
- ret = get_futex_key(uaddr1, flags & FLAGS_SHARED, &key1);
+ ret = get_futex_key(uaddr1, flags & FLAGS_SHARED, &key1, VERIFY_READ);
if (unlikely(ret != 0))
goto out;
- ret = get_futex_key(uaddr2, flags & FLAGS_SHARED, &key2);
+ ret = get_futex_key(uaddr2, flags & FLAGS_SHARED, &key2,
+ requeue_pi ? VERIFY_WRITE : VERIFY_READ);
if (unlikely(ret != 0))
goto out_put_key1;
@@ -1790,7 +1820,7 @@
* while the syscall executes.
*/
retry:
- ret = get_futex_key(uaddr, flags & FLAGS_SHARED, &q->key);
+ ret = get_futex_key(uaddr, flags & FLAGS_SHARED, &q->key, VERIFY_READ);
if (unlikely(ret != 0))
return ret;
@@ -1941,7 +1971,7 @@
}
retry:
- ret = get_futex_key(uaddr, flags & FLAGS_SHARED, &q.key);
+ ret = get_futex_key(uaddr, flags & FLAGS_SHARED, &q.key, VERIFY_WRITE);
if (unlikely(ret != 0))
goto out;
@@ -2060,7 +2090,7 @@
if ((uval & FUTEX_TID_MASK) != vpid)
return -EPERM;
- ret = get_futex_key(uaddr, flags & FLAGS_SHARED, &key);
+ ret = get_futex_key(uaddr, flags & FLAGS_SHARED, &key, VERIFY_WRITE);
if (unlikely(ret != 0))
goto out;
@@ -2249,7 +2279,7 @@
debug_rt_mutex_init_waiter(&rt_waiter);
rt_waiter.task = NULL;
- ret = get_futex_key(uaddr2, flags & FLAGS_SHARED, &key2);
+ ret = get_futex_key(uaddr2, flags & FLAGS_SHARED, &key2, VERIFY_WRITE);
if (unlikely(ret != 0))
goto out;
diff --git a/kernel/irq/generic-chip.c b/kernel/irq/generic-chip.c
index 3a2cab4..e38544d 100644
--- a/kernel/irq/generic-chip.c
+++ b/kernel/irq/generic-chip.c
@@ -246,7 +246,7 @@
gc->mask_cache = irq_reg_readl(gc->reg_base + ct->regs.mask);
for (i = gc->irq_base; msk; msk >>= 1, i++) {
- if (!msk & 0x01)
+ if (!(msk & 0x01))
continue;
if (flags & IRQ_GC_INIT_NESTED_LOCK)
@@ -301,7 +301,7 @@
raw_spin_unlock(&gc_lock);
for (; msk; msk >>= 1, i++) {
- if (!msk & 0x01)
+ if (!(msk & 0x01))
continue;
/* Remove handler first. That will mask the irq line */
diff --git a/kernel/irq/irqdesc.c b/kernel/irq/irqdesc.c
index 4c60a50..039b889 100644
--- a/kernel/irq/irqdesc.c
+++ b/kernel/irq/irqdesc.c
@@ -70,7 +70,8 @@
static inline int desc_node(struct irq_desc *desc) { return 0; }
#endif
-static void desc_set_defaults(unsigned int irq, struct irq_desc *desc, int node)
+static void desc_set_defaults(unsigned int irq, struct irq_desc *desc, int node,
+ struct module *owner)
{
int cpu;
@@ -86,6 +87,7 @@
desc->irq_count = 0;
desc->irqs_unhandled = 0;
desc->name = NULL;
+ desc->owner = owner;
for_each_possible_cpu(cpu)
*per_cpu_ptr(desc->kstat_irqs, cpu) = 0;
desc_smp_init(desc, node);
@@ -128,7 +130,7 @@
static inline void free_masks(struct irq_desc *desc) { }
#endif
-static struct irq_desc *alloc_desc(int irq, int node)
+static struct irq_desc *alloc_desc(int irq, int node, struct module *owner)
{
struct irq_desc *desc;
gfp_t gfp = GFP_KERNEL;
@@ -147,7 +149,7 @@
raw_spin_lock_init(&desc->lock);
lockdep_set_class(&desc->lock, &irq_desc_lock_class);
- desc_set_defaults(irq, desc, node);
+ desc_set_defaults(irq, desc, node, owner);
return desc;
@@ -173,13 +175,14 @@
kfree(desc);
}
-static int alloc_descs(unsigned int start, unsigned int cnt, int node)
+static int alloc_descs(unsigned int start, unsigned int cnt, int node,
+ struct module *owner)
{
struct irq_desc *desc;
int i;
for (i = 0; i < cnt; i++) {
- desc = alloc_desc(start + i, node);
+ desc = alloc_desc(start + i, node, owner);
if (!desc)
goto err;
mutex_lock(&sparse_irq_lock);
@@ -227,7 +230,7 @@
nr_irqs = initcnt;
for (i = 0; i < initcnt; i++) {
- desc = alloc_desc(i, node);
+ desc = alloc_desc(i, node, NULL);
set_bit(i, allocated_irqs);
irq_insert_desc(i, desc);
}
@@ -261,7 +264,7 @@
alloc_masks(&desc[i], GFP_KERNEL, node);
raw_spin_lock_init(&desc[i].lock);
lockdep_set_class(&desc[i].lock, &irq_desc_lock_class);
- desc_set_defaults(i, &desc[i], node);
+ desc_set_defaults(i, &desc[i], node, NULL);
}
return arch_early_irq_init();
}
@@ -276,8 +279,16 @@
dynamic_irq_cleanup(irq);
}
-static inline int alloc_descs(unsigned int start, unsigned int cnt, int node)
+static inline int alloc_descs(unsigned int start, unsigned int cnt, int node,
+ struct module *owner)
{
+ u32 i;
+
+ for (i = 0; i < cnt; i++) {
+ struct irq_desc *desc = irq_to_desc(start + i);
+
+ desc->owner = owner;
+ }
return start;
}
@@ -333,11 +344,13 @@
* @from: Start the search from this irq number
* @cnt: Number of consecutive irqs to allocate.
* @node: Preferred node on which the irq descriptor should be allocated
+ * @owner: Owning module (can be NULL)
*
* Returns the first irq number or error code
*/
int __ref
-irq_alloc_descs(int irq, unsigned int from, unsigned int cnt, int node)
+__irq_alloc_descs(int irq, unsigned int from, unsigned int cnt, int node,
+ struct module *owner)
{
int start, ret;
@@ -366,13 +379,13 @@
bitmap_set(allocated_irqs, start, cnt);
mutex_unlock(&sparse_irq_lock);
- return alloc_descs(start, cnt, node);
+ return alloc_descs(start, cnt, node, owner);
err:
mutex_unlock(&sparse_irq_lock);
return ret;
}
-EXPORT_SYMBOL_GPL(irq_alloc_descs);
+EXPORT_SYMBOL_GPL(__irq_alloc_descs);
/**
* irq_reserve_irqs - mark irqs allocated
@@ -440,7 +453,7 @@
unsigned long flags;
raw_spin_lock_irqsave(&desc->lock, flags);
- desc_set_defaults(irq, desc, desc_node(desc));
+ desc_set_defaults(irq, desc, desc_node(desc), NULL);
raw_spin_unlock_irqrestore(&desc->lock, flags);
}
diff --git a/kernel/irq/manage.c b/kernel/irq/manage.c
index 0a7840ae..2e94258 100644
--- a/kernel/irq/manage.c
+++ b/kernel/irq/manage.c
@@ -883,6 +883,8 @@
if (desc->irq_data.chip == &no_irq_chip)
return -ENOSYS;
+ if (!try_module_get(desc->owner))
+ return -ENODEV;
/*
* Some drivers like serial.c use request_irq() heavily,
* so we have to be careful not to interfere with a
@@ -906,8 +908,10 @@
*/
nested = irq_settings_is_nested_thread(desc);
if (nested) {
- if (!new->thread_fn)
- return -EINVAL;
+ if (!new->thread_fn) {
+ ret = -EINVAL;
+ goto out_mput;
+ }
/*
* Replace the primary handler which was provided from
* the driver for non nested interrupt handling by the
@@ -929,8 +933,10 @@
t = kthread_create(irq_thread, new, "irq/%d-%s", irq,
new->name);
- if (IS_ERR(t))
- return PTR_ERR(t);
+ if (IS_ERR(t)) {
+ ret = PTR_ERR(t);
+ goto out_mput;
+ }
/*
* We keep the reference to the task struct even if
* the thread dies to avoid that the interrupt code
@@ -1095,6 +1101,8 @@
kthread_stop(t);
put_task_struct(t);
}
+out_mput:
+ module_put(desc->owner);
return ret;
}
@@ -1203,6 +1211,7 @@
put_task_struct(action->thread);
}
+ module_put(desc->owner);
return action;
}
@@ -1322,6 +1331,7 @@
if (!thread_fn)
return -EINVAL;
handler = irq_default_primary_handler;
+ irqflags |= IRQF_ONESHOT;
}
action = kzalloc(sizeof(struct irqaction), GFP_KERNEL);
diff --git a/kernel/kmod.c b/kernel/kmod.c
index 47613df..ddc7644 100644
--- a/kernel/kmod.c
+++ b/kernel/kmod.c
@@ -274,7 +274,7 @@
* (used for preventing user land processes from being created after the user
* land has been frozen during a system-wide hibernation or suspend operation).
*/
-static int usermodehelper_disabled;
+static int usermodehelper_disabled = 1;
/* Number of helpers running */
static atomic_t running_helpers = ATOMIC_INIT(0);
diff --git a/kernel/lockdep.c b/kernel/lockdep.c
index 3956f51..91d67ce 100644
--- a/kernel/lockdep.c
+++ b/kernel/lockdep.c
@@ -2468,7 +2468,7 @@
BUG_ON(usage_bit >= LOCK_USAGE_STATES);
- if (hlock_class(hlock)->key == &__lockdep_no_validate__)
+ if (hlock_class(hlock)->key == __lockdep_no_validate__.subkeys)
continue;
if (!mark_lock(curr, hlock, usage_bit))
@@ -2485,23 +2485,9 @@
{
struct task_struct *curr = current;
- if (DEBUG_LOCKS_WARN_ON(unlikely(early_boot_irqs_disabled)))
- return;
-
- if (unlikely(curr->hardirqs_enabled)) {
- /*
- * Neither irq nor preemption are disabled here
- * so this is racy by nature but losing one hit
- * in a stat is not a big deal.
- */
- __debug_atomic_inc(redundant_hardirqs_on);
- return;
- }
/* we'll do an OFF -> ON transition: */
curr->hardirqs_enabled = 1;
- if (DEBUG_LOCKS_WARN_ON(current->hardirq_context))
- return;
/*
* We are going to turn hardirqs on, so set the
* usage bit for all held locks:
@@ -2529,9 +2515,25 @@
if (unlikely(!debug_locks || current->lockdep_recursion))
return;
+ if (unlikely(current->hardirqs_enabled)) {
+ /*
+ * Neither irq nor preemption are disabled here
+ * so this is racy by nature but losing one hit
+ * in a stat is not a big deal.
+ */
+ __debug_atomic_inc(redundant_hardirqs_on);
+ return;
+ }
+
if (DEBUG_LOCKS_WARN_ON(!irqs_disabled()))
return;
+ if (DEBUG_LOCKS_WARN_ON(unlikely(early_boot_irqs_disabled)))
+ return;
+
+ if (DEBUG_LOCKS_WARN_ON(current->hardirq_context))
+ return;
+
current->lockdep_recursion = 1;
__trace_hardirqs_on_caller(ip);
current->lockdep_recursion = 0;
@@ -2872,10 +2874,7 @@
void lockdep_init_map(struct lockdep_map *lock, const char *name,
struct lock_class_key *key, int subclass)
{
- int i;
-
- for (i = 0; i < NR_LOCKDEP_CACHING_CLASSES; i++)
- lock->class_cache[i] = NULL;
+ memset(lock, 0, sizeof(*lock));
#ifdef CONFIG_LOCK_STAT
lock->cpu = raw_smp_processor_id();
@@ -3112,7 +3111,13 @@
if (!class)
class = look_up_lock_class(lock, 0);
- if (DEBUG_LOCKS_WARN_ON(!class))
+ /*
+ * If look_up_lock_class() failed to find a class, we're trying
+ * to test if we hold a lock that has never yet been acquired.
+ * Clearly if the lock hasn't been acquired _ever_, we're not
+ * holding it either, so report failure.
+ */
+ if (!class)
return 0;
if (DEBUG_LOCKS_WARN_ON(!hlock->nest_lock))
diff --git a/kernel/power/Kconfig b/kernel/power/Kconfig
index b1914cb9..3744c59 100644
--- a/kernel/power/Kconfig
+++ b/kernel/power/Kconfig
@@ -231,3 +231,7 @@
config PM_GENERIC_DOMAINS
bool
depends on PM
+
+config PM_GENERIC_DOMAINS_RUNTIME
+ def_bool y
+ depends on PM_RUNTIME && PM_GENERIC_DOMAINS
diff --git a/kernel/printk.c b/kernel/printk.c
index 37dff34..836a2ae 100644
--- a/kernel/printk.c
+++ b/kernel/printk.c
@@ -318,8 +318,10 @@
return 0;
/* For historical reasons, accept CAP_SYS_ADMIN too, with a warning */
if (capable(CAP_SYS_ADMIN)) {
- WARN_ONCE(1, "Attempt to access syslog with CAP_SYS_ADMIN "
- "but no CAP_SYSLOG (deprecated).\n");
+ printk_once(KERN_WARNING "%s (%d): "
+ "Attempt to access syslog with CAP_SYS_ADMIN "
+ "but no CAP_SYSLOG (deprecated).\n",
+ current->comm, task_pid_nr(current));
return 0;
}
return -EPERM;
diff --git a/kernel/resource.c b/kernel/resource.c
index 3ff4017..3b3cedc 100644
--- a/kernel/resource.c
+++ b/kernel/resource.c
@@ -553,6 +553,27 @@
EXPORT_SYMBOL(allocate_resource);
+/**
+ * lookup_resource - find an existing resource by a resource start address
+ * @root: root resource descriptor
+ * @start: resource start address
+ *
+ * Returns a pointer to the resource if found, NULL otherwise
+ */
+struct resource *lookup_resource(struct resource *root, resource_size_t start)
+{
+ struct resource *res;
+
+ read_lock(&resource_lock);
+ for (res = root->child; res; res = res->sibling) {
+ if (res->start == start)
+ break;
+ }
+ read_unlock(&resource_lock);
+
+ return res;
+}
+
/*
* Insert a resource into the resource tree. If successful, return NULL,
* otherwise return the conflicting resource (compare to __request_resource())
diff --git a/kernel/sys.c b/kernel/sys.c
index a101ba3..dd948a1 100644
--- a/kernel/sys.c
+++ b/kernel/sys.c
@@ -621,11 +621,18 @@
if (!new_user)
return -EAGAIN;
+ /*
+ * We don't fail in case of NPROC limit excess here because too many
+ * poorly written programs don't check set*uid() return code, assuming
+ * it never fails if called by root. We may still enforce NPROC limit
+ * for programs doing set*uid()+execve() by harmlessly deferring the
+ * failure to the execve() stage.
+ */
if (atomic_read(&new_user->processes) >= rlimit(RLIMIT_NPROC) &&
- new_user != INIT_USER) {
- free_uid(new_user);
- return -EAGAIN;
- }
+ new_user != INIT_USER)
+ current->flags |= PF_NPROC_EXCEEDED;
+ else
+ current->flags &= ~PF_NPROC_EXCEEDED;
free_uid(new->user);
new->user = new_user;
diff --git a/kernel/taskstats.c b/kernel/taskstats.c
index d1db288..e19ce14 100644
--- a/kernel/taskstats.c
+++ b/kernel/taskstats.c
@@ -291,30 +291,28 @@
if (!cpumask_subset(mask, cpu_possible_mask))
return -EINVAL;
- s = NULL;
if (isadd == REGISTER) {
for_each_cpu(cpu, mask) {
- if (!s)
- s = kmalloc_node(sizeof(struct listener),
- GFP_KERNEL, cpu_to_node(cpu));
+ s = kmalloc_node(sizeof(struct listener),
+ GFP_KERNEL, cpu_to_node(cpu));
if (!s)
goto cleanup;
+
s->pid = pid;
- INIT_LIST_HEAD(&s->list);
s->valid = 1;
listeners = &per_cpu(listener_array, cpu);
down_write(&listeners->sem);
- list_for_each_entry_safe(s2, tmp, &listeners->list, list) {
- if (s2->pid == pid)
- goto next_cpu;
+ list_for_each_entry(s2, &listeners->list, list) {
+ if (s2->pid == pid && s2->valid)
+ goto exists;
}
list_add(&s->list, &listeners->list);
s = NULL;
-next_cpu:
+exists:
up_write(&listeners->sem);
+ kfree(s); /* nop if NULL */
}
- kfree(s);
return 0;
}
diff --git a/kernel/trace/Kconfig b/kernel/trace/Kconfig
index 2ad39e5..cd31345 100644
--- a/kernel/trace/Kconfig
+++ b/kernel/trace/Kconfig
@@ -82,7 +82,7 @@
power:power_frequency
This is for userspace compatibility
and will vanish after 5 kernel iterations,
- namely 2.6.41.
+ namely 3.1.
config CONTEXT_SWITCH_TRACER
bool
diff --git a/lib/Kconfig b/lib/Kconfig
index 32f3e5a..6c695ff 100644
--- a/lib/Kconfig
+++ b/lib/Kconfig
@@ -276,4 +276,7 @@
so its calculations are in fixed point. Modules can select this
when they require this function. Module will be called cordic.
+config LLIST
+ bool
+
endmenu
diff --git a/lib/Makefile b/lib/Makefile
index 892f4e2..d5d175c 100644
--- a/lib/Makefile
+++ b/lib/Makefile
@@ -10,7 +10,7 @@
lib-y := ctype.o string.o vsprintf.o cmdline.o \
rbtree.o radix-tree.o dump_stack.o timerqueue.o\
idr.o int_sqrt.o extable.o prio_tree.o \
- sha1.o irq_regs.o reciprocal_div.o argv_split.o \
+ sha1.o md5.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 find_next_bit.o
@@ -115,6 +115,8 @@
obj-$(CONFIG_CORDIC) += cordic.o
+obj-$(CONFIG_LLIST) += llist.o
+
hostprogs-y := gen_crc32table
clean-files := crc32table.h
diff --git a/lib/bitmap.c b/lib/bitmap.c
index 37ef4b0..2f4412e 100644
--- a/lib/bitmap.c
+++ b/lib/bitmap.c
@@ -271,8 +271,6 @@
}
EXPORT_SYMBOL(__bitmap_weight);
-#define BITMAP_FIRST_WORD_MASK(start) (~0UL << ((start) % BITS_PER_LONG))
-
void bitmap_set(unsigned long *map, int start, int nr)
{
unsigned long *p = map + BIT_WORD(start);
diff --git a/lib/fault-inject.c b/lib/fault-inject.c
index 2577b12..f193b77 100644
--- a/lib/fault-inject.c
+++ b/lib/fault-inject.c
@@ -197,21 +197,15 @@
return debugfs_create_file(name, mode, parent, value, &fops_atomic_t);
}
-void cleanup_fault_attr_dentries(struct fault_attr *attr)
-{
- debugfs_remove_recursive(attr->dir);
-}
-
-int init_fault_attr_dentries(struct fault_attr *attr, const char *name)
+struct dentry *fault_create_debugfs_attr(const char *name,
+ struct dentry *parent, struct fault_attr *attr)
{
mode_t mode = S_IFREG | S_IRUSR | S_IWUSR;
struct dentry *dir;
- dir = debugfs_create_dir(name, NULL);
+ dir = debugfs_create_dir(name, parent);
if (!dir)
- return -ENOMEM;
-
- attr->dir = dir;
+ return ERR_PTR(-ENOMEM);
if (!debugfs_create_ul("probability", mode, dir, &attr->probability))
goto fail;
@@ -243,11 +237,11 @@
#endif /* CONFIG_FAULT_INJECTION_STACKTRACE_FILTER */
- return 0;
+ return dir;
fail:
- debugfs_remove_recursive(attr->dir);
+ debugfs_remove_recursive(dir);
- return -ENOMEM;
+ return ERR_PTR(-ENOMEM);
}
#endif /* CONFIG_FAULT_INJECTION_DEBUG_FS */
diff --git a/lib/genalloc.c b/lib/genalloc.c
index 577ddf8..f352cc4 100644
--- a/lib/genalloc.c
+++ b/lib/genalloc.c
@@ -1,8 +1,26 @@
/*
- * Basic general purpose allocator for managing special purpose memory
- * not managed by the regular kmalloc/kfree interface.
- * Uses for this includes on-device special memory, uncached memory
- * etc.
+ * Basic general purpose allocator for managing special purpose
+ * memory, for example, memory that is not managed by the regular
+ * kmalloc/kfree interface. Uses for this includes on-device special
+ * memory, uncached memory etc.
+ *
+ * It is safe to use the allocator in NMI handlers and other special
+ * unblockable contexts that could otherwise deadlock on locks. This
+ * is implemented by using atomic operations and retries on any
+ * conflicts. The disadvantage is that there may be livelocks in
+ * extreme cases. For better scalability, one allocator can be used
+ * for each CPU.
+ *
+ * The lockless operation only works if there is enough memory
+ * available. If new memory is added to the pool a lock has to be
+ * still taken. So any user relying on locklessness has to ensure
+ * that sufficient memory is preallocated.
+ *
+ * The basic atomic operation of this allocator is cmpxchg on long.
+ * On architectures that don't have NMI-safe cmpxchg implementation,
+ * the allocator can NOT be used in NMI handler. So code uses the
+ * allocator in NMI handler should depend on
+ * CONFIG_ARCH_HAVE_NMI_SAFE_CMPXCHG.
*
* Copyright 2005 (C) Jes Sorensen <jes@trained-monkey.org>
*
@@ -13,8 +31,109 @@
#include <linux/slab.h>
#include <linux/module.h>
#include <linux/bitmap.h>
+#include <linux/rculist.h>
+#include <linux/interrupt.h>
#include <linux/genalloc.h>
+static int set_bits_ll(unsigned long *addr, unsigned long mask_to_set)
+{
+ unsigned long val, nval;
+
+ nval = *addr;
+ do {
+ val = nval;
+ if (val & mask_to_set)
+ return -EBUSY;
+ cpu_relax();
+ } while ((nval = cmpxchg(addr, val, val | mask_to_set)) != val);
+
+ return 0;
+}
+
+static int clear_bits_ll(unsigned long *addr, unsigned long mask_to_clear)
+{
+ unsigned long val, nval;
+
+ nval = *addr;
+ do {
+ val = nval;
+ if ((val & mask_to_clear) != mask_to_clear)
+ return -EBUSY;
+ cpu_relax();
+ } while ((nval = cmpxchg(addr, val, val & ~mask_to_clear)) != val);
+
+ return 0;
+}
+
+/*
+ * bitmap_set_ll - set the specified number of bits at the specified position
+ * @map: pointer to a bitmap
+ * @start: a bit position in @map
+ * @nr: number of bits to set
+ *
+ * Set @nr bits start from @start in @map lock-lessly. Several users
+ * can set/clear the same bitmap simultaneously without lock. If two
+ * users set the same bit, one user will return remain bits, otherwise
+ * return 0.
+ */
+static int bitmap_set_ll(unsigned long *map, int start, int nr)
+{
+ unsigned long *p = map + BIT_WORD(start);
+ const int size = start + nr;
+ int bits_to_set = BITS_PER_LONG - (start % BITS_PER_LONG);
+ unsigned long mask_to_set = BITMAP_FIRST_WORD_MASK(start);
+
+ while (nr - bits_to_set >= 0) {
+ if (set_bits_ll(p, mask_to_set))
+ return nr;
+ nr -= bits_to_set;
+ bits_to_set = BITS_PER_LONG;
+ mask_to_set = ~0UL;
+ p++;
+ }
+ if (nr) {
+ mask_to_set &= BITMAP_LAST_WORD_MASK(size);
+ if (set_bits_ll(p, mask_to_set))
+ return nr;
+ }
+
+ return 0;
+}
+
+/*
+ * bitmap_clear_ll - clear the specified number of bits at the specified position
+ * @map: pointer to a bitmap
+ * @start: a bit position in @map
+ * @nr: number of bits to set
+ *
+ * Clear @nr bits start from @start in @map lock-lessly. Several users
+ * can set/clear the same bitmap simultaneously without lock. If two
+ * users clear the same bit, one user will return remain bits,
+ * otherwise return 0.
+ */
+static int bitmap_clear_ll(unsigned long *map, int start, int nr)
+{
+ unsigned long *p = map + BIT_WORD(start);
+ const int size = start + nr;
+ int bits_to_clear = BITS_PER_LONG - (start % BITS_PER_LONG);
+ unsigned long mask_to_clear = BITMAP_FIRST_WORD_MASK(start);
+
+ while (nr - bits_to_clear >= 0) {
+ if (clear_bits_ll(p, mask_to_clear))
+ return nr;
+ nr -= bits_to_clear;
+ bits_to_clear = BITS_PER_LONG;
+ mask_to_clear = ~0UL;
+ p++;
+ }
+ if (nr) {
+ mask_to_clear &= BITMAP_LAST_WORD_MASK(size);
+ if (clear_bits_ll(p, mask_to_clear))
+ return nr;
+ }
+
+ return 0;
+}
/**
* gen_pool_create - create a new special memory pool
@@ -30,7 +149,7 @@
pool = kmalloc_node(sizeof(struct gen_pool), GFP_KERNEL, nid);
if (pool != NULL) {
- rwlock_init(&pool->lock);
+ spin_lock_init(&pool->lock);
INIT_LIST_HEAD(&pool->chunks);
pool->min_alloc_order = min_alloc_order;
}
@@ -63,14 +182,14 @@
if (unlikely(chunk == NULL))
return -ENOMEM;
- spin_lock_init(&chunk->lock);
chunk->phys_addr = phys;
chunk->start_addr = virt;
chunk->end_addr = virt + size;
+ atomic_set(&chunk->avail, size);
- write_lock(&pool->lock);
- list_add(&chunk->next_chunk, &pool->chunks);
- write_unlock(&pool->lock);
+ spin_lock(&pool->lock);
+ list_add_rcu(&chunk->next_chunk, &pool->chunks);
+ spin_unlock(&pool->lock);
return 0;
}
@@ -85,19 +204,19 @@
*/
phys_addr_t gen_pool_virt_to_phys(struct gen_pool *pool, unsigned long addr)
{
- struct list_head *_chunk;
struct gen_pool_chunk *chunk;
+ phys_addr_t paddr = -1;
- read_lock(&pool->lock);
- list_for_each(_chunk, &pool->chunks) {
- chunk = list_entry(_chunk, struct gen_pool_chunk, next_chunk);
-
- if (addr >= chunk->start_addr && addr < chunk->end_addr)
- return chunk->phys_addr + addr - chunk->start_addr;
+ rcu_read_lock();
+ list_for_each_entry_rcu(chunk, &pool->chunks, next_chunk) {
+ if (addr >= chunk->start_addr && addr < chunk->end_addr) {
+ paddr = chunk->phys_addr + (addr - chunk->start_addr);
+ break;
+ }
}
- read_unlock(&pool->lock);
+ rcu_read_unlock();
- return -1;
+ return paddr;
}
EXPORT_SYMBOL(gen_pool_virt_to_phys);
@@ -115,7 +234,6 @@
int order = pool->min_alloc_order;
int bit, end_bit;
-
list_for_each_safe(_chunk, _next_chunk, &pool->chunks) {
chunk = list_entry(_chunk, struct gen_pool_chunk, next_chunk);
list_del(&chunk->next_chunk);
@@ -137,44 +255,50 @@
* @size: number of bytes to allocate from the pool
*
* Allocate the requested number of bytes from the specified pool.
- * Uses a first-fit algorithm.
+ * Uses a first-fit algorithm. Can not be used in NMI handler on
+ * architectures without NMI-safe cmpxchg implementation.
*/
unsigned long gen_pool_alloc(struct gen_pool *pool, size_t size)
{
- struct list_head *_chunk;
struct gen_pool_chunk *chunk;
- unsigned long addr, flags;
+ unsigned long addr = 0;
int order = pool->min_alloc_order;
- int nbits, start_bit, end_bit;
+ int nbits, start_bit = 0, end_bit, remain;
+
+#ifndef CONFIG_ARCH_HAVE_NMI_SAFE_CMPXCHG
+ BUG_ON(in_nmi());
+#endif
if (size == 0)
return 0;
nbits = (size + (1UL << order) - 1) >> order;
-
- read_lock(&pool->lock);
- list_for_each(_chunk, &pool->chunks) {
- chunk = list_entry(_chunk, struct gen_pool_chunk, next_chunk);
+ rcu_read_lock();
+ list_for_each_entry_rcu(chunk, &pool->chunks, next_chunk) {
+ if (size > atomic_read(&chunk->avail))
+ continue;
end_bit = (chunk->end_addr - chunk->start_addr) >> order;
-
- spin_lock_irqsave(&chunk->lock, flags);
- start_bit = bitmap_find_next_zero_area(chunk->bits, end_bit, 0,
- nbits, 0);
- if (start_bit >= end_bit) {
- spin_unlock_irqrestore(&chunk->lock, flags);
+retry:
+ start_bit = bitmap_find_next_zero_area(chunk->bits, end_bit,
+ start_bit, nbits, 0);
+ if (start_bit >= end_bit)
continue;
+ remain = bitmap_set_ll(chunk->bits, start_bit, nbits);
+ if (remain) {
+ remain = bitmap_clear_ll(chunk->bits, start_bit,
+ nbits - remain);
+ BUG_ON(remain);
+ goto retry;
}
addr = chunk->start_addr + ((unsigned long)start_bit << order);
-
- bitmap_set(chunk->bits, start_bit, nbits);
- spin_unlock_irqrestore(&chunk->lock, flags);
- read_unlock(&pool->lock);
- return addr;
+ size = nbits << order;
+ atomic_sub(size, &chunk->avail);
+ break;
}
- read_unlock(&pool->lock);
- return 0;
+ rcu_read_unlock();
+ return addr;
}
EXPORT_SYMBOL(gen_pool_alloc);
@@ -184,33 +308,95 @@
* @addr: starting address of memory to free back to pool
* @size: size in bytes of memory to free
*
- * Free previously allocated special memory back to the specified pool.
+ * Free previously allocated special memory back to the specified
+ * pool. Can not be used in NMI handler on architectures without
+ * NMI-safe cmpxchg implementation.
*/
void gen_pool_free(struct gen_pool *pool, unsigned long addr, size_t size)
{
- struct list_head *_chunk;
struct gen_pool_chunk *chunk;
- unsigned long flags;
int order = pool->min_alloc_order;
- int bit, nbits;
+ int start_bit, nbits, remain;
+
+#ifndef CONFIG_ARCH_HAVE_NMI_SAFE_CMPXCHG
+ BUG_ON(in_nmi());
+#endif
nbits = (size + (1UL << order) - 1) >> order;
-
- read_lock(&pool->lock);
- list_for_each(_chunk, &pool->chunks) {
- chunk = list_entry(_chunk, struct gen_pool_chunk, next_chunk);
-
+ rcu_read_lock();
+ list_for_each_entry_rcu(chunk, &pool->chunks, next_chunk) {
if (addr >= chunk->start_addr && addr < chunk->end_addr) {
BUG_ON(addr + size > chunk->end_addr);
- spin_lock_irqsave(&chunk->lock, flags);
- bit = (addr - chunk->start_addr) >> order;
- while (nbits--)
- __clear_bit(bit++, chunk->bits);
- spin_unlock_irqrestore(&chunk->lock, flags);
- break;
+ start_bit = (addr - chunk->start_addr) >> order;
+ remain = bitmap_clear_ll(chunk->bits, start_bit, nbits);
+ BUG_ON(remain);
+ size = nbits << order;
+ atomic_add(size, &chunk->avail);
+ rcu_read_unlock();
+ return;
}
}
- BUG_ON(nbits > 0);
- read_unlock(&pool->lock);
+ rcu_read_unlock();
+ BUG();
}
EXPORT_SYMBOL(gen_pool_free);
+
+/**
+ * gen_pool_for_each_chunk - call func for every chunk of generic memory pool
+ * @pool: the generic memory pool
+ * @func: func to call
+ * @data: additional data used by @func
+ *
+ * Call @func for every chunk of generic memory pool. The @func is
+ * called with rcu_read_lock held.
+ */
+void gen_pool_for_each_chunk(struct gen_pool *pool,
+ void (*func)(struct gen_pool *pool, struct gen_pool_chunk *chunk, void *data),
+ void *data)
+{
+ struct gen_pool_chunk *chunk;
+
+ rcu_read_lock();
+ list_for_each_entry_rcu(chunk, &(pool)->chunks, next_chunk)
+ func(pool, chunk, data);
+ rcu_read_unlock();
+}
+EXPORT_SYMBOL(gen_pool_for_each_chunk);
+
+/**
+ * gen_pool_avail - get available free space of the pool
+ * @pool: pool to get available free space
+ *
+ * Return available free space of the specified pool.
+ */
+size_t gen_pool_avail(struct gen_pool *pool)
+{
+ struct gen_pool_chunk *chunk;
+ size_t avail = 0;
+
+ rcu_read_lock();
+ list_for_each_entry_rcu(chunk, &pool->chunks, next_chunk)
+ avail += atomic_read(&chunk->avail);
+ rcu_read_unlock();
+ return avail;
+}
+EXPORT_SYMBOL_GPL(gen_pool_avail);
+
+/**
+ * gen_pool_size - get size in bytes of memory managed by the pool
+ * @pool: pool to get size
+ *
+ * Return size in bytes of memory managed by the pool.
+ */
+size_t gen_pool_size(struct gen_pool *pool)
+{
+ struct gen_pool_chunk *chunk;
+ size_t size = 0;
+
+ rcu_read_lock();
+ list_for_each_entry_rcu(chunk, &pool->chunks, next_chunk)
+ size += chunk->end_addr - chunk->start_addr;
+ rcu_read_unlock();
+ return size;
+}
+EXPORT_SYMBOL_GPL(gen_pool_size);
diff --git a/lib/idr.c b/lib/idr.c
index e15502e..db040ce 100644
--- a/lib/idr.c
+++ b/lib/idr.c
@@ -34,8 +34,10 @@
#include <linux/err.h>
#include <linux/string.h>
#include <linux/idr.h>
+#include <linux/spinlock.h>
static struct kmem_cache *idr_layer_cache;
+static DEFINE_SPINLOCK(simple_ida_lock);
static struct idr_layer *get_from_free_list(struct idr *idp)
{
@@ -926,6 +928,71 @@
EXPORT_SYMBOL(ida_destroy);
/**
+ * ida_simple_get - get a new id.
+ * @ida: the (initialized) ida.
+ * @start: the minimum id (inclusive, < 0x8000000)
+ * @end: the maximum id (exclusive, < 0x8000000 or 0)
+ * @gfp_mask: memory allocation flags
+ *
+ * Allocates an id in the range start <= id < end, or returns -ENOSPC.
+ * On memory allocation failure, returns -ENOMEM.
+ *
+ * Use ida_simple_remove() to get rid of an id.
+ */
+int ida_simple_get(struct ida *ida, unsigned int start, unsigned int end,
+ gfp_t gfp_mask)
+{
+ int ret, id;
+ unsigned int max;
+
+ BUG_ON((int)start < 0);
+ BUG_ON((int)end < 0);
+
+ if (end == 0)
+ max = 0x80000000;
+ else {
+ BUG_ON(end < start);
+ max = end - 1;
+ }
+
+again:
+ if (!ida_pre_get(ida, gfp_mask))
+ return -ENOMEM;
+
+ spin_lock(&simple_ida_lock);
+ ret = ida_get_new_above(ida, start, &id);
+ if (!ret) {
+ if (id > max) {
+ ida_remove(ida, id);
+ ret = -ENOSPC;
+ } else {
+ ret = id;
+ }
+ }
+ spin_unlock(&simple_ida_lock);
+
+ if (unlikely(ret == -EAGAIN))
+ goto again;
+
+ return ret;
+}
+EXPORT_SYMBOL(ida_simple_get);
+
+/**
+ * ida_simple_remove - remove an allocated id.
+ * @ida: the (initialized) ida.
+ * @id: the id returned by ida_simple_get.
+ */
+void ida_simple_remove(struct ida *ida, unsigned int id)
+{
+ BUG_ON((int)id < 0);
+ spin_lock(&simple_ida_lock);
+ ida_remove(ida, id);
+ spin_unlock(&simple_ida_lock);
+}
+EXPORT_SYMBOL(ida_simple_remove);
+
+/**
* ida_init - initialize ida handle
* @ida: ida handle
*
diff --git a/lib/llist.c b/lib/llist.c
new file mode 100644
index 0000000..da44572
--- /dev/null
+++ b/lib/llist.c
@@ -0,0 +1,129 @@
+/*
+ * Lock-less NULL terminated single linked list
+ *
+ * The basic atomic operation of this list is cmpxchg on long. On
+ * architectures that don't have NMI-safe cmpxchg implementation, the
+ * list can NOT be used in NMI handler. So code uses the list in NMI
+ * handler should depend on CONFIG_ARCH_HAVE_NMI_SAFE_CMPXCHG.
+ *
+ * Copyright 2010,2011 Intel Corp.
+ * Author: Huang Ying <ying.huang@intel.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/kernel.h>
+#include <linux/module.h>
+#include <linux/interrupt.h>
+#include <linux/llist.h>
+
+#include <asm/system.h>
+
+/**
+ * llist_add - add a new entry
+ * @new: new entry to be added
+ * @head: the head for your lock-less list
+ */
+void llist_add(struct llist_node *new, struct llist_head *head)
+{
+ struct llist_node *entry, *old_entry;
+
+#ifndef CONFIG_ARCH_HAVE_NMI_SAFE_CMPXCHG
+ BUG_ON(in_nmi());
+#endif
+
+ entry = head->first;
+ do {
+ old_entry = entry;
+ new->next = entry;
+ cpu_relax();
+ } while ((entry = cmpxchg(&head->first, old_entry, new)) != old_entry);
+}
+EXPORT_SYMBOL_GPL(llist_add);
+
+/**
+ * llist_add_batch - add several linked entries in batch
+ * @new_first: first entry in batch to be added
+ * @new_last: last entry in batch to be added
+ * @head: the head for your lock-less list
+ */
+void llist_add_batch(struct llist_node *new_first, struct llist_node *new_last,
+ struct llist_head *head)
+{
+ struct llist_node *entry, *old_entry;
+
+#ifndef CONFIG_ARCH_HAVE_NMI_SAFE_CMPXCHG
+ BUG_ON(in_nmi());
+#endif
+
+ entry = head->first;
+ do {
+ old_entry = entry;
+ new_last->next = entry;
+ cpu_relax();
+ } while ((entry = cmpxchg(&head->first, old_entry, new_first)) != old_entry);
+}
+EXPORT_SYMBOL_GPL(llist_add_batch);
+
+/**
+ * llist_del_first - delete the first entry of lock-less list
+ * @head: the head for your lock-less list
+ *
+ * If list is empty, return NULL, otherwise, return the first entry
+ * deleted, this is the newest added one.
+ *
+ * Only one llist_del_first user can be used simultaneously with
+ * multiple llist_add users without lock. Because otherwise
+ * llist_del_first, llist_add, llist_add (or llist_del_all, llist_add,
+ * llist_add) sequence in another user may change @head->first->next,
+ * but keep @head->first. If multiple consumers are needed, please
+ * use llist_del_all or use lock between consumers.
+ */
+struct llist_node *llist_del_first(struct llist_head *head)
+{
+ struct llist_node *entry, *old_entry, *next;
+
+#ifndef CONFIG_ARCH_HAVE_NMI_SAFE_CMPXCHG
+ BUG_ON(in_nmi());
+#endif
+
+ entry = head->first;
+ do {
+ if (entry == NULL)
+ return NULL;
+ old_entry = entry;
+ next = entry->next;
+ cpu_relax();
+ } while ((entry = cmpxchg(&head->first, old_entry, next)) != old_entry);
+
+ return entry;
+}
+EXPORT_SYMBOL_GPL(llist_del_first);
+
+/**
+ * llist_del_all - delete all entries from lock-less list
+ * @head: the head of lock-less list to delete all entries
+ *
+ * If list is empty, return NULL, otherwise, delete all entries and
+ * return the pointer to the first entry. The order of entries
+ * deleted is from the newest to the oldest added one.
+ */
+struct llist_node *llist_del_all(struct llist_head *head)
+{
+#ifndef CONFIG_ARCH_HAVE_NMI_SAFE_CMPXCHG
+ BUG_ON(in_nmi());
+#endif
+
+ return xchg(&head->first, NULL);
+}
+EXPORT_SYMBOL_GPL(llist_del_all);
diff --git a/lib/md5.c b/lib/md5.c
new file mode 100644
index 0000000..c777180
--- /dev/null
+++ b/lib/md5.c
@@ -0,0 +1,95 @@
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/cryptohash.h>
+
+#define F1(x, y, z) (z ^ (x & (y ^ z)))
+#define F2(x, y, z) F1(z, x, y)
+#define F3(x, y, z) (x ^ y ^ z)
+#define F4(x, y, z) (y ^ (x | ~z))
+
+#define MD5STEP(f, w, x, y, z, in, s) \
+ (w += f(x, y, z) + in, w = (w<<s | w>>(32-s)) + x)
+
+void md5_transform(__u32 *hash, __u32 const *in)
+{
+ u32 a, b, c, d;
+
+ a = hash[0];
+ b = hash[1];
+ c = hash[2];
+ d = hash[3];
+
+ MD5STEP(F1, a, b, c, d, in[0] + 0xd76aa478, 7);
+ MD5STEP(F1, d, a, b, c, in[1] + 0xe8c7b756, 12);
+ MD5STEP(F1, c, d, a, b, in[2] + 0x242070db, 17);
+ MD5STEP(F1, b, c, d, a, in[3] + 0xc1bdceee, 22);
+ MD5STEP(F1, a, b, c, d, in[4] + 0xf57c0faf, 7);
+ MD5STEP(F1, d, a, b, c, in[5] + 0x4787c62a, 12);
+ MD5STEP(F1, c, d, a, b, in[6] + 0xa8304613, 17);
+ MD5STEP(F1, b, c, d, a, in[7] + 0xfd469501, 22);
+ MD5STEP(F1, a, b, c, d, in[8] + 0x698098d8, 7);
+ MD5STEP(F1, d, a, b, c, in[9] + 0x8b44f7af, 12);
+ MD5STEP(F1, c, d, a, b, in[10] + 0xffff5bb1, 17);
+ MD5STEP(F1, b, c, d, a, in[11] + 0x895cd7be, 22);
+ MD5STEP(F1, a, b, c, d, in[12] + 0x6b901122, 7);
+ MD5STEP(F1, d, a, b, c, in[13] + 0xfd987193, 12);
+ MD5STEP(F1, c, d, a, b, in[14] + 0xa679438e, 17);
+ MD5STEP(F1, b, c, d, a, in[15] + 0x49b40821, 22);
+
+ MD5STEP(F2, a, b, c, d, in[1] + 0xf61e2562, 5);
+ MD5STEP(F2, d, a, b, c, in[6] + 0xc040b340, 9);
+ MD5STEP(F2, c, d, a, b, in[11] + 0x265e5a51, 14);
+ MD5STEP(F2, b, c, d, a, in[0] + 0xe9b6c7aa, 20);
+ MD5STEP(F2, a, b, c, d, in[5] + 0xd62f105d, 5);
+ MD5STEP(F2, d, a, b, c, in[10] + 0x02441453, 9);
+ MD5STEP(F2, c, d, a, b, in[15] + 0xd8a1e681, 14);
+ MD5STEP(F2, b, c, d, a, in[4] + 0xe7d3fbc8, 20);
+ MD5STEP(F2, a, b, c, d, in[9] + 0x21e1cde6, 5);
+ MD5STEP(F2, d, a, b, c, in[14] + 0xc33707d6, 9);
+ MD5STEP(F2, c, d, a, b, in[3] + 0xf4d50d87, 14);
+ MD5STEP(F2, b, c, d, a, in[8] + 0x455a14ed, 20);
+ MD5STEP(F2, a, b, c, d, in[13] + 0xa9e3e905, 5);
+ MD5STEP(F2, d, a, b, c, in[2] + 0xfcefa3f8, 9);
+ MD5STEP(F2, c, d, a, b, in[7] + 0x676f02d9, 14);
+ MD5STEP(F2, b, c, d, a, in[12] + 0x8d2a4c8a, 20);
+
+ MD5STEP(F3, a, b, c, d, in[5] + 0xfffa3942, 4);
+ MD5STEP(F3, d, a, b, c, in[8] + 0x8771f681, 11);
+ MD5STEP(F3, c, d, a, b, in[11] + 0x6d9d6122, 16);
+ MD5STEP(F3, b, c, d, a, in[14] + 0xfde5380c, 23);
+ MD5STEP(F3, a, b, c, d, in[1] + 0xa4beea44, 4);
+ MD5STEP(F3, d, a, b, c, in[4] + 0x4bdecfa9, 11);
+ MD5STEP(F3, c, d, a, b, in[7] + 0xf6bb4b60, 16);
+ MD5STEP(F3, b, c, d, a, in[10] + 0xbebfbc70, 23);
+ MD5STEP(F3, a, b, c, d, in[13] + 0x289b7ec6, 4);
+ MD5STEP(F3, d, a, b, c, in[0] + 0xeaa127fa, 11);
+ MD5STEP(F3, c, d, a, b, in[3] + 0xd4ef3085, 16);
+ MD5STEP(F3, b, c, d, a, in[6] + 0x04881d05, 23);
+ MD5STEP(F3, a, b, c, d, in[9] + 0xd9d4d039, 4);
+ MD5STEP(F3, d, a, b, c, in[12] + 0xe6db99e5, 11);
+ MD5STEP(F3, c, d, a, b, in[15] + 0x1fa27cf8, 16);
+ MD5STEP(F3, b, c, d, a, in[2] + 0xc4ac5665, 23);
+
+ MD5STEP(F4, a, b, c, d, in[0] + 0xf4292244, 6);
+ MD5STEP(F4, d, a, b, c, in[7] + 0x432aff97, 10);
+ MD5STEP(F4, c, d, a, b, in[14] + 0xab9423a7, 15);
+ MD5STEP(F4, b, c, d, a, in[5] + 0xfc93a039, 21);
+ MD5STEP(F4, a, b, c, d, in[12] + 0x655b59c3, 6);
+ MD5STEP(F4, d, a, b, c, in[3] + 0x8f0ccc92, 10);
+ MD5STEP(F4, c, d, a, b, in[10] + 0xffeff47d, 15);
+ MD5STEP(F4, b, c, d, a, in[1] + 0x85845dd1, 21);
+ MD5STEP(F4, a, b, c, d, in[8] + 0x6fa87e4f, 6);
+ MD5STEP(F4, d, a, b, c, in[15] + 0xfe2ce6e0, 10);
+ MD5STEP(F4, c, d, a, b, in[6] + 0xa3014314, 15);
+ MD5STEP(F4, b, c, d, a, in[13] + 0x4e0811a1, 21);
+ MD5STEP(F4, a, b, c, d, in[4] + 0xf7537e82, 6);
+ MD5STEP(F4, d, a, b, c, in[11] + 0xbd3af235, 10);
+ MD5STEP(F4, c, d, a, b, in[2] + 0x2ad7d2bb, 15);
+ MD5STEP(F4, b, c, d, a, in[9] + 0xeb86d391, 21);
+
+ hash[0] += a;
+ hash[1] += b;
+ hash[2] += c;
+ hash[3] += d;
+}
+EXPORT_SYMBOL(md5_transform);
diff --git a/lib/radix-tree.c b/lib/radix-tree.c
index 7ea2e03..a2f9da5 100644
--- a/lib/radix-tree.c
+++ b/lib/radix-tree.c
@@ -823,8 +823,8 @@
EXPORT_SYMBOL(radix_tree_prev_hole);
static unsigned int
-__lookup(struct radix_tree_node *slot, void ***results, unsigned long index,
- unsigned int max_items, unsigned long *next_index)
+__lookup(struct radix_tree_node *slot, void ***results, unsigned long *indices,
+ unsigned long index, unsigned int max_items, unsigned long *next_index)
{
unsigned int nr_found = 0;
unsigned int shift, height;
@@ -857,12 +857,16 @@
/* Bottom level: grab some items */
for (i = index & RADIX_TREE_MAP_MASK; i < RADIX_TREE_MAP_SIZE; i++) {
- index++;
if (slot->slots[i]) {
- results[nr_found++] = &(slot->slots[i]);
- if (nr_found == max_items)
+ results[nr_found] = &(slot->slots[i]);
+ if (indices)
+ indices[nr_found] = index;
+ if (++nr_found == max_items) {
+ index++;
goto out;
+ }
}
+ index++;
}
out:
*next_index = index;
@@ -918,8 +922,8 @@
if (cur_index > max_index)
break;
- slots_found = __lookup(node, (void ***)results + ret, cur_index,
- max_items - ret, &next_index);
+ slots_found = __lookup(node, (void ***)results + ret, NULL,
+ cur_index, max_items - ret, &next_index);
nr_found = 0;
for (i = 0; i < slots_found; i++) {
struct radix_tree_node *slot;
@@ -944,6 +948,7 @@
* radix_tree_gang_lookup_slot - perform multiple slot lookup on radix tree
* @root: radix tree root
* @results: where the results of the lookup are placed
+ * @indices: where their indices should be placed (but usually NULL)
* @first_index: start the lookup from this key
* @max_items: place up to this many items at *results
*
@@ -958,7 +963,8 @@
* protection, radix_tree_deref_slot may fail requiring a retry.
*/
unsigned int
-radix_tree_gang_lookup_slot(struct radix_tree_root *root, void ***results,
+radix_tree_gang_lookup_slot(struct radix_tree_root *root,
+ void ***results, unsigned long *indices,
unsigned long first_index, unsigned int max_items)
{
unsigned long max_index;
@@ -974,6 +980,8 @@
if (first_index > 0)
return 0;
results[0] = (void **)&root->rnode;
+ if (indices)
+ indices[0] = 0;
return 1;
}
node = indirect_to_ptr(node);
@@ -987,8 +995,9 @@
if (cur_index > max_index)
break;
- slots_found = __lookup(node, results + ret, cur_index,
- max_items - ret, &next_index);
+ slots_found = __lookup(node, results + ret,
+ indices ? indices + ret : NULL,
+ cur_index, max_items - ret, &next_index);
ret += slots_found;
if (next_index == 0)
break;
@@ -1194,6 +1203,98 @@
}
EXPORT_SYMBOL(radix_tree_gang_lookup_tag_slot);
+#if defined(CONFIG_SHMEM) && defined(CONFIG_SWAP)
+#include <linux/sched.h> /* for cond_resched() */
+
+/*
+ * This linear search is at present only useful to shmem_unuse_inode().
+ */
+static unsigned long __locate(struct radix_tree_node *slot, void *item,
+ unsigned long index, unsigned long *found_index)
+{
+ unsigned int shift, height;
+ unsigned long i;
+
+ height = slot->height;
+ shift = (height-1) * RADIX_TREE_MAP_SHIFT;
+
+ for ( ; height > 1; height--) {
+ i = (index >> shift) & RADIX_TREE_MAP_MASK;
+ for (;;) {
+ if (slot->slots[i] != NULL)
+ break;
+ index &= ~((1UL << shift) - 1);
+ index += 1UL << shift;
+ if (index == 0)
+ goto out; /* 32-bit wraparound */
+ i++;
+ if (i == RADIX_TREE_MAP_SIZE)
+ goto out;
+ }
+
+ shift -= RADIX_TREE_MAP_SHIFT;
+ slot = rcu_dereference_raw(slot->slots[i]);
+ if (slot == NULL)
+ goto out;
+ }
+
+ /* Bottom level: check items */
+ for (i = 0; i < RADIX_TREE_MAP_SIZE; i++) {
+ if (slot->slots[i] == item) {
+ *found_index = index + i;
+ index = 0;
+ goto out;
+ }
+ }
+ index += RADIX_TREE_MAP_SIZE;
+out:
+ return index;
+}
+
+/**
+ * radix_tree_locate_item - search through radix tree for item
+ * @root: radix tree root
+ * @item: item to be found
+ *
+ * Returns index where item was found, or -1 if not found.
+ * Caller must hold no lock (since this time-consuming function needs
+ * to be preemptible), and must check afterwards if item is still there.
+ */
+unsigned long radix_tree_locate_item(struct radix_tree_root *root, void *item)
+{
+ struct radix_tree_node *node;
+ unsigned long max_index;
+ unsigned long cur_index = 0;
+ unsigned long found_index = -1;
+
+ do {
+ rcu_read_lock();
+ node = rcu_dereference_raw(root->rnode);
+ if (!radix_tree_is_indirect_ptr(node)) {
+ rcu_read_unlock();
+ if (node == item)
+ found_index = 0;
+ break;
+ }
+
+ node = indirect_to_ptr(node);
+ max_index = radix_tree_maxindex(node->height);
+ if (cur_index > max_index)
+ break;
+
+ cur_index = __locate(node, item, cur_index, &found_index);
+ rcu_read_unlock();
+ cond_resched();
+ } while (cur_index != 0 && cur_index <= max_index);
+
+ return found_index;
+}
+#else
+unsigned long radix_tree_locate_item(struct radix_tree_root *root, void *item)
+{
+ return -1;
+}
+#endif /* CONFIG_SHMEM && CONFIG_SWAP */
/**
* radix_tree_shrink - shrink height of a radix tree to minimal
diff --git a/lib/sha1.c b/lib/sha1.c
index 4c45fd5..f33271d 100644
--- a/lib/sha1.c
+++ b/lib/sha1.c
@@ -1,31 +1,72 @@
/*
- * SHA transform algorithm, originally taken from code written by
- * Peter Gutmann, and placed in the public domain.
+ * SHA1 routine optimized to do word accesses rather than byte accesses,
+ * and to avoid unnecessary copies into the context array.
+ *
+ * This was based on the git SHA1 implementation.
*/
#include <linux/kernel.h>
#include <linux/module.h>
-#include <linux/cryptohash.h>
+#include <linux/bitops.h>
+#include <asm/unaligned.h>
-/* The SHA f()-functions. */
+/*
+ * If you have 32 registers or more, the compiler can (and should)
+ * try to change the array[] accesses into registers. However, on
+ * machines with less than ~25 registers, that won't really work,
+ * and at least gcc will make an unholy mess of it.
+ *
+ * So to avoid that mess which just slows things down, we force
+ * the stores to memory to actually happen (we might be better off
+ * with a 'W(t)=(val);asm("":"+m" (W(t))' there instead, as
+ * suggested by Artur Skawina - that will also make gcc unable to
+ * try to do the silly "optimize away loads" part because it won't
+ * see what the value will be).
+ *
+ * Ben Herrenschmidt reports that on PPC, the C version comes close
+ * to the optimized asm with this (ie on PPC you don't want that
+ * 'volatile', since there are lots of registers).
+ *
+ * On ARM we get the best code generation by forcing a full memory barrier
+ * between each SHA_ROUND, otherwise gcc happily get wild with spilling and
+ * the stack frame size simply explode and performance goes down the drain.
+ */
-#define f1(x,y,z) (z ^ (x & (y ^ z))) /* x ? y : z */
-#define f2(x,y,z) (x ^ y ^ z) /* XOR */
-#define f3(x,y,z) ((x & y) + (z & (x ^ y))) /* majority */
+#ifdef CONFIG_X86
+ #define setW(x, val) (*(volatile __u32 *)&W(x) = (val))
+#elif defined(CONFIG_ARM)
+ #define setW(x, val) do { W(x) = (val); __asm__("":::"memory"); } while (0)
+#else
+ #define setW(x, val) (W(x) = (val))
+#endif
-/* The SHA Mysterious Constants */
+/* This "rolls" over the 512-bit array */
+#define W(x) (array[(x)&15])
-#define K1 0x5A827999L /* Rounds 0-19: sqrt(2) * 2^30 */
-#define K2 0x6ED9EBA1L /* Rounds 20-39: sqrt(3) * 2^30 */
-#define K3 0x8F1BBCDCL /* Rounds 40-59: sqrt(5) * 2^30 */
-#define K4 0xCA62C1D6L /* Rounds 60-79: sqrt(10) * 2^30 */
+/*
+ * Where do we get the source from? The first 16 iterations get it from
+ * the input data, the next mix it from the 512-bit array.
+ */
+#define SHA_SRC(t) get_unaligned_be32((__u32 *)data + t)
+#define SHA_MIX(t) rol32(W(t+13) ^ W(t+8) ^ W(t+2) ^ W(t), 1)
+
+#define SHA_ROUND(t, input, fn, constant, A, B, C, D, E) do { \
+ __u32 TEMP = input(t); setW(t, TEMP); \
+ E += TEMP + rol32(A,5) + (fn) + (constant); \
+ B = ror32(B, 2); } while (0)
+
+#define T_0_15(t, A, B, C, D, E) SHA_ROUND(t, SHA_SRC, (((C^D)&B)^D) , 0x5a827999, A, B, C, D, E )
+#define T_16_19(t, A, B, C, D, E) SHA_ROUND(t, SHA_MIX, (((C^D)&B)^D) , 0x5a827999, A, B, C, D, E )
+#define T_20_39(t, A, B, C, D, E) SHA_ROUND(t, SHA_MIX, (B^C^D) , 0x6ed9eba1, A, B, C, D, E )
+#define T_40_59(t, A, B, C, D, E) SHA_ROUND(t, SHA_MIX, ((B&C)+(D&(B^C))) , 0x8f1bbcdc, A, B, C, D, E )
+#define T_60_79(t, A, B, C, D, E) SHA_ROUND(t, SHA_MIX, (B^C^D) , 0xca62c1d6, A, B, C, D, E )
/**
* sha_transform - single block SHA1 transform
*
* @digest: 160 bit digest to update
* @data: 512 bits of data to hash
- * @W: 80 words of workspace (see note)
+ * @array: 16 words of workspace (see note)
*
* This function generates a SHA1 digest for a single 512-bit block.
* Be warned, it does not handle padding and message digest, do not
@@ -36,47 +77,111 @@
* to clear the workspace. This is left to the caller to avoid
* unnecessary clears between chained hashing operations.
*/
-void sha_transform(__u32 *digest, const char *in, __u32 *W)
+void sha_transform(__u32 *digest, const char *data, __u32 *array)
{
- __u32 a, b, c, d, e, t, i;
+ __u32 A, B, C, D, E;
- for (i = 0; i < 16; i++)
- W[i] = be32_to_cpu(((const __be32 *)in)[i]);
+ A = digest[0];
+ B = digest[1];
+ C = digest[2];
+ D = digest[3];
+ E = digest[4];
- for (i = 0; i < 64; i++)
- W[i+16] = rol32(W[i+13] ^ W[i+8] ^ W[i+2] ^ W[i], 1);
+ /* Round 1 - iterations 0-16 take their input from 'data' */
+ T_0_15( 0, A, B, C, D, E);
+ T_0_15( 1, E, A, B, C, D);
+ T_0_15( 2, D, E, A, B, C);
+ T_0_15( 3, C, D, E, A, B);
+ T_0_15( 4, B, C, D, E, A);
+ T_0_15( 5, A, B, C, D, E);
+ T_0_15( 6, E, A, B, C, D);
+ T_0_15( 7, D, E, A, B, C);
+ T_0_15( 8, C, D, E, A, B);
+ T_0_15( 9, B, C, D, E, A);
+ T_0_15(10, A, B, C, D, E);
+ T_0_15(11, E, A, B, C, D);
+ T_0_15(12, D, E, A, B, C);
+ T_0_15(13, C, D, E, A, B);
+ T_0_15(14, B, C, D, E, A);
+ T_0_15(15, A, B, C, D, E);
- a = digest[0];
- b = digest[1];
- c = digest[2];
- d = digest[3];
- e = digest[4];
+ /* Round 1 - tail. Input from 512-bit mixing array */
+ T_16_19(16, E, A, B, C, D);
+ T_16_19(17, D, E, A, B, C);
+ T_16_19(18, C, D, E, A, B);
+ T_16_19(19, B, C, D, E, A);
- for (i = 0; i < 20; i++) {
- t = f1(b, c, d) + K1 + rol32(a, 5) + e + W[i];
- e = d; d = c; c = rol32(b, 30); b = a; a = t;
- }
+ /* Round 2 */
+ T_20_39(20, A, B, C, D, E);
+ T_20_39(21, E, A, B, C, D);
+ T_20_39(22, D, E, A, B, C);
+ T_20_39(23, C, D, E, A, B);
+ T_20_39(24, B, C, D, E, A);
+ T_20_39(25, A, B, C, D, E);
+ T_20_39(26, E, A, B, C, D);
+ T_20_39(27, D, E, A, B, C);
+ T_20_39(28, C, D, E, A, B);
+ T_20_39(29, B, C, D, E, A);
+ T_20_39(30, A, B, C, D, E);
+ T_20_39(31, E, A, B, C, D);
+ T_20_39(32, D, E, A, B, C);
+ T_20_39(33, C, D, E, A, B);
+ T_20_39(34, B, C, D, E, A);
+ T_20_39(35, A, B, C, D, E);
+ T_20_39(36, E, A, B, C, D);
+ T_20_39(37, D, E, A, B, C);
+ T_20_39(38, C, D, E, A, B);
+ T_20_39(39, B, C, D, E, A);
- for (; i < 40; i ++) {
- t = f2(b, c, d) + K2 + rol32(a, 5) + e + W[i];
- e = d; d = c; c = rol32(b, 30); b = a; a = t;
- }
+ /* Round 3 */
+ T_40_59(40, A, B, C, D, E);
+ T_40_59(41, E, A, B, C, D);
+ T_40_59(42, D, E, A, B, C);
+ T_40_59(43, C, D, E, A, B);
+ T_40_59(44, B, C, D, E, A);
+ T_40_59(45, A, B, C, D, E);
+ T_40_59(46, E, A, B, C, D);
+ T_40_59(47, D, E, A, B, C);
+ T_40_59(48, C, D, E, A, B);
+ T_40_59(49, B, C, D, E, A);
+ T_40_59(50, A, B, C, D, E);
+ T_40_59(51, E, A, B, C, D);
+ T_40_59(52, D, E, A, B, C);
+ T_40_59(53, C, D, E, A, B);
+ T_40_59(54, B, C, D, E, A);
+ T_40_59(55, A, B, C, D, E);
+ T_40_59(56, E, A, B, C, D);
+ T_40_59(57, D, E, A, B, C);
+ T_40_59(58, C, D, E, A, B);
+ T_40_59(59, B, C, D, E, A);
- for (; i < 60; i ++) {
- t = f3(b, c, d) + K3 + rol32(a, 5) + e + W[i];
- e = d; d = c; c = rol32(b, 30); b = a; a = t;
- }
+ /* Round 4 */
+ T_60_79(60, A, B, C, D, E);
+ T_60_79(61, E, A, B, C, D);
+ T_60_79(62, D, E, A, B, C);
+ T_60_79(63, C, D, E, A, B);
+ T_60_79(64, B, C, D, E, A);
+ T_60_79(65, A, B, C, D, E);
+ T_60_79(66, E, A, B, C, D);
+ T_60_79(67, D, E, A, B, C);
+ T_60_79(68, C, D, E, A, B);
+ T_60_79(69, B, C, D, E, A);
+ T_60_79(70, A, B, C, D, E);
+ T_60_79(71, E, A, B, C, D);
+ T_60_79(72, D, E, A, B, C);
+ T_60_79(73, C, D, E, A, B);
+ T_60_79(74, B, C, D, E, A);
+ T_60_79(75, A, B, C, D, E);
+ T_60_79(76, E, A, B, C, D);
+ T_60_79(77, D, E, A, B, C);
+ T_60_79(78, C, D, E, A, B);
+ T_60_79(79, B, C, D, E, A);
- for (; i < 80; i ++) {
- t = f2(b, c, d) + K4 + rol32(a, 5) + e + W[i];
- e = d; d = c; c = rol32(b, 30); b = a; a = t;
- }
-
- digest[0] += a;
- digest[1] += b;
- digest[2] += c;
- digest[3] += d;
- digest[4] += e;
+ digest[0] += A;
+ digest[1] += B;
+ digest[2] += C;
+ digest[3] += D;
+ digest[4] += E;
}
EXPORT_SYMBOL(sha_transform);
@@ -92,4 +197,3 @@
buf[3] = 0x10325476;
buf[4] = 0xc3d2e1f0;
}
-
diff --git a/mm/failslab.c b/mm/failslab.c
index 1ce58c2..0dd7b8f 100644
--- a/mm/failslab.c
+++ b/mm/failslab.c
@@ -34,23 +34,23 @@
#ifdef CONFIG_FAULT_INJECTION_DEBUG_FS
static int __init failslab_debugfs_init(void)
{
+ struct dentry *dir;
mode_t mode = S_IFREG | S_IRUSR | S_IWUSR;
- int err;
- err = init_fault_attr_dentries(&failslab.attr, "failslab");
- if (err)
- return err;
+ dir = fault_create_debugfs_attr("failslab", NULL, &failslab.attr);
+ if (IS_ERR(dir))
+ return PTR_ERR(dir);
- if (!debugfs_create_bool("ignore-gfp-wait", mode, failslab.attr.dir,
+ if (!debugfs_create_bool("ignore-gfp-wait", mode, dir,
&failslab.ignore_gfp_wait))
goto fail;
- if (!debugfs_create_bool("cache-filter", mode, failslab.attr.dir,
+ if (!debugfs_create_bool("cache-filter", mode, dir,
&failslab.cache_filter))
goto fail;
return 0;
fail:
- cleanup_fault_attr_dentries(&failslab.attr);
+ debugfs_remove_recursive(dir);
return -ENOMEM;
}
diff --git a/mm/filemap.c b/mm/filemap.c
index 867d402..645a080 100644
--- a/mm/filemap.c
+++ b/mm/filemap.c
@@ -33,7 +33,6 @@
#include <linux/cpuset.h>
#include <linux/hardirq.h> /* for BUG_ON(!in_atomic()) only */
#include <linux/memcontrol.h>
-#include <linux/mm_inline.h> /* for page_is_file_cache() */
#include <linux/cleancache.h>
#include "internal.h"
@@ -462,6 +461,7 @@
int error;
VM_BUG_ON(!PageLocked(page));
+ VM_BUG_ON(PageSwapBacked(page));
error = mem_cgroup_cache_charge(page, current->mm,
gfp_mask & GFP_RECLAIM_MASK);
@@ -479,8 +479,6 @@
if (likely(!error)) {
mapping->nrpages++;
__inc_zone_page_state(page, NR_FILE_PAGES);
- if (PageSwapBacked(page))
- __inc_zone_page_state(page, NR_SHMEM);
spin_unlock_irq(&mapping->tree_lock);
} else {
page->mapping = NULL;
@@ -502,22 +500,9 @@
{
int ret;
- /*
- * Splice_read and readahead add shmem/tmpfs pages into the page cache
- * before shmem_readpage has a chance to mark them as SwapBacked: they
- * need to go on the anon lru below, and mem_cgroup_cache_charge
- * (called in add_to_page_cache) needs to know where they're going too.
- */
- if (mapping_cap_swap_backed(mapping))
- SetPageSwapBacked(page);
-
ret = add_to_page_cache(page, mapping, offset, gfp_mask);
- if (ret == 0) {
- if (page_is_file_cache(page))
- lru_cache_add_file(page);
- else
- lru_cache_add_anon(page);
- }
+ if (ret == 0)
+ lru_cache_add_file(page);
return ret;
}
EXPORT_SYMBOL_GPL(add_to_page_cache_lru);
@@ -714,9 +699,16 @@
page = radix_tree_deref_slot(pagep);
if (unlikely(!page))
goto out;
- if (radix_tree_deref_retry(page))
- goto repeat;
-
+ if (radix_tree_exception(page)) {
+ if (radix_tree_deref_retry(page))
+ goto repeat;
+ /*
+ * Otherwise, shmem/tmpfs must be storing a swap entry
+ * here as an exceptional entry: so return it without
+ * attempting to raise page count.
+ */
+ goto out;
+ }
if (!page_cache_get_speculative(page))
goto repeat;
@@ -753,7 +745,7 @@
repeat:
page = find_get_page(mapping, offset);
- if (page) {
+ if (page && !radix_tree_exception(page)) {
lock_page(page);
/* Has the page been truncated? */
if (unlikely(page->mapping != mapping)) {
@@ -840,7 +832,7 @@
rcu_read_lock();
restart:
nr_found = radix_tree_gang_lookup_slot(&mapping->page_tree,
- (void ***)pages, start, nr_pages);
+ (void ***)pages, NULL, start, nr_pages);
ret = 0;
for (i = 0; i < nr_found; i++) {
struct page *page;
@@ -849,13 +841,22 @@
if (unlikely(!page))
continue;
- /*
- * This can only trigger when the entry at index 0 moves out
- * of or back to the root: none yet gotten, safe to restart.
- */
- if (radix_tree_deref_retry(page)) {
- WARN_ON(start | i);
- goto restart;
+ if (radix_tree_exception(page)) {
+ if (radix_tree_deref_retry(page)) {
+ /*
+ * Transient condition which can only trigger
+ * when entry at index 0 moves out of or back
+ * to root: none yet gotten, safe to restart.
+ */
+ WARN_ON(start | i);
+ goto restart;
+ }
+ /*
+ * Otherwise, shmem/tmpfs must be storing a swap entry
+ * here as an exceptional entry: so skip over it -
+ * we only reach this from invalidate_mapping_pages().
+ */
+ continue;
}
if (!page_cache_get_speculative(page))
@@ -903,7 +904,7 @@
rcu_read_lock();
restart:
nr_found = radix_tree_gang_lookup_slot(&mapping->page_tree,
- (void ***)pages, index, nr_pages);
+ (void ***)pages, NULL, index, nr_pages);
ret = 0;
for (i = 0; i < nr_found; i++) {
struct page *page;
@@ -912,12 +913,22 @@
if (unlikely(!page))
continue;
- /*
- * This can only trigger when the entry at index 0 moves out
- * of or back to the root: none yet gotten, safe to restart.
- */
- if (radix_tree_deref_retry(page))
- goto restart;
+ if (radix_tree_exception(page)) {
+ if (radix_tree_deref_retry(page)) {
+ /*
+ * Transient condition which can only trigger
+ * when entry at index 0 moves out of or back
+ * to root: none yet gotten, safe to restart.
+ */
+ goto restart;
+ }
+ /*
+ * Otherwise, shmem/tmpfs must be storing a swap entry
+ * here as an exceptional entry: so stop looking for
+ * contiguous pages.
+ */
+ break;
+ }
if (!page_cache_get_speculative(page))
goto repeat;
@@ -977,12 +988,21 @@
if (unlikely(!page))
continue;
- /*
- * This can only trigger when the entry at index 0 moves out
- * of or back to the root: none yet gotten, safe to restart.
- */
- if (radix_tree_deref_retry(page))
- goto restart;
+ if (radix_tree_exception(page)) {
+ if (radix_tree_deref_retry(page)) {
+ /*
+ * Transient condition which can only trigger
+ * when entry at index 0 moves out of or back
+ * to root: none yet gotten, safe to restart.
+ */
+ goto restart;
+ }
+ /*
+ * This function is never used on a shmem/tmpfs
+ * mapping, so a swap entry won't be found here.
+ */
+ BUG();
+ }
if (!page_cache_get_speculative(page))
goto repeat;
diff --git a/mm/highmem.c b/mm/highmem.c
index 693394d..5ef672c 100644
--- a/mm/highmem.c
+++ b/mm/highmem.c
@@ -326,7 +326,7 @@
spinlock_t lock; /* Protect this bucket's list */
} ____cacheline_aligned_in_smp page_address_htable[1<<PA_HASH_ORDER];
-static struct page_address_slot *page_slot(struct page *page)
+static struct page_address_slot *page_slot(const struct page *page)
{
return &page_address_htable[hash_ptr(page, PA_HASH_ORDER)];
}
@@ -337,7 +337,7 @@
*
* Returns the page's virtual address.
*/
-void *page_address(struct page *page)
+void *page_address(const struct page *page)
{
unsigned long flags;
void *ret;
diff --git a/mm/memcontrol.c b/mm/memcontrol.c
index 5f84d235..930de94 100644
--- a/mm/memcontrol.c
+++ b/mm/memcontrol.c
@@ -35,7 +35,6 @@
#include <linux/limits.h>
#include <linux/mutex.h>
#include <linux/rbtree.h>
-#include <linux/shmem_fs.h>
#include <linux/slab.h>
#include <linux/swap.h>
#include <linux/swapops.h>
@@ -2092,6 +2091,7 @@
#define FLUSHING_CACHED_CHARGE (0)
};
static DEFINE_PER_CPU(struct memcg_stock_pcp, memcg_stock);
+static DEFINE_MUTEX(percpu_charge_mutex);
/*
* Try to consume stocked charge on this cpu. If success, one page is consumed
@@ -2198,8 +2198,7 @@
for_each_online_cpu(cpu) {
struct memcg_stock_pcp *stock = &per_cpu(memcg_stock, cpu);
- if (mem_cgroup_same_or_subtree(root_mem, stock->cached) &&
- test_bit(FLUSHING_CACHED_CHARGE, &stock->flags))
+ if (test_bit(FLUSHING_CACHED_CHARGE, &stock->flags))
flush_work(&stock->work);
}
out:
@@ -2214,14 +2213,22 @@
*/
static void drain_all_stock_async(struct mem_cgroup *root_mem)
{
+ /*
+ * If someone calls draining, avoid adding more kworker runs.
+ */
+ if (!mutex_trylock(&percpu_charge_mutex))
+ return;
drain_all_stock(root_mem, false);
+ mutex_unlock(&percpu_charge_mutex);
}
/* This is a synchronous drain interface. */
static void drain_all_stock_sync(struct mem_cgroup *root_mem)
{
/* called when force_empty is called */
+ mutex_lock(&percpu_charge_mutex);
drain_all_stock(root_mem, true);
+ mutex_unlock(&percpu_charge_mutex);
}
/*
@@ -2873,30 +2880,6 @@
return 0;
if (PageCompound(page))
return 0;
- /*
- * Corner case handling. This is called from add_to_page_cache()
- * in usual. But some FS (shmem) precharges this page before calling it
- * and call add_to_page_cache() with GFP_NOWAIT.
- *
- * For GFP_NOWAIT case, the page may be pre-charged before calling
- * add_to_page_cache(). (See shmem.c) check it here and avoid to call
- * charge twice. (It works but has to pay a bit larger cost.)
- * And when the page is SwapCache, it should take swap information
- * into account. This is under lock_page() now.
- */
- if (!(gfp_mask & __GFP_WAIT)) {
- struct page_cgroup *pc;
-
- pc = lookup_page_cgroup(page);
- if (!pc)
- return 0;
- lock_page_cgroup(pc);
- if (PageCgroupUsed(pc)) {
- unlock_page_cgroup(pc);
- return 0;
- }
- unlock_page_cgroup(pc);
- }
if (unlikely(!mm))
mm = &init_mm;
@@ -3486,31 +3469,6 @@
cgroup_release_and_wakeup_rmdir(&mem->css);
}
-/*
- * A call to try to shrink memory usage on charge failure at shmem's swapin.
- * Calling hierarchical_reclaim is not enough because we should update
- * last_oom_jiffies to prevent pagefault_out_of_memory from invoking global OOM.
- * Moreover considering hierarchy, we should reclaim from the mem_over_limit,
- * not from the memcg which this page would be charged to.
- * try_charge_swapin does all of these works properly.
- */
-int mem_cgroup_shmem_charge_fallback(struct page *page,
- struct mm_struct *mm,
- gfp_t gfp_mask)
-{
- struct mem_cgroup *mem;
- int ret;
-
- if (mem_cgroup_disabled())
- return 0;
-
- ret = mem_cgroup_try_charge_swapin(mm, page, gfp_mask, &mem);
- if (!ret)
- mem_cgroup_cancel_charge_swapin(mem); /* it does !mem check */
-
- return ret;
-}
-
#ifdef CONFIG_DEBUG_VM
static struct page_cgroup *lookup_page_cgroup_used(struct page *page)
{
@@ -5330,15 +5288,17 @@
pgoff = pte_to_pgoff(ptent);
/* page is moved even if it's not RSS of this task(page-faulted). */
- if (!mapping_cap_swap_backed(mapping)) { /* normal file */
- page = find_get_page(mapping, pgoff);
- } else { /* shmem/tmpfs file. we should take account of swap too. */
- swp_entry_t ent;
- mem_cgroup_get_shmem_target(inode, pgoff, &page, &ent);
- if (do_swap_account)
- entry->val = ent.val;
- }
+ page = find_get_page(mapping, pgoff);
+#ifdef CONFIG_SWAP
+ /* shmem/tmpfs may report page out on swap: account for that too. */
+ if (radix_tree_exceptional_entry(page)) {
+ swp_entry_t swap = radix_to_swp_entry(page);
+ if (do_swap_account)
+ *entry = swap;
+ page = find_get_page(&swapper_space, swap.val);
+ }
+#endif
return page;
}
diff --git a/mm/memory-failure.c b/mm/memory-failure.c
index 740c4f5..2b43ba0 100644
--- a/mm/memory-failure.c
+++ b/mm/memory-failure.c
@@ -53,6 +53,7 @@
#include <linux/hugetlb.h>
#include <linux/memory_hotplug.h>
#include <linux/mm_inline.h>
+#include <linux/kfifo.h>
#include "internal.h"
int sysctl_memory_failure_early_kill __read_mostly = 0;
@@ -1178,6 +1179,97 @@
__memory_failure(pfn, trapno, 0);
}
+#define MEMORY_FAILURE_FIFO_ORDER 4
+#define MEMORY_FAILURE_FIFO_SIZE (1 << MEMORY_FAILURE_FIFO_ORDER)
+
+struct memory_failure_entry {
+ unsigned long pfn;
+ int trapno;
+ int flags;
+};
+
+struct memory_failure_cpu {
+ DECLARE_KFIFO(fifo, struct memory_failure_entry,
+ MEMORY_FAILURE_FIFO_SIZE);
+ spinlock_t lock;
+ struct work_struct work;
+};
+
+static DEFINE_PER_CPU(struct memory_failure_cpu, memory_failure_cpu);
+
+/**
+ * memory_failure_queue - Schedule handling memory failure of a page.
+ * @pfn: Page Number of the corrupted page
+ * @trapno: Trap number reported in the signal to user space.
+ * @flags: Flags for memory failure handling
+ *
+ * This function is called by the low level hardware error handler
+ * when it detects hardware memory corruption of a page. It schedules
+ * the recovering of error page, including dropping pages, killing
+ * processes etc.
+ *
+ * The function is primarily of use for corruptions that
+ * happen outside the current execution context (e.g. when
+ * detected by a background scrubber)
+ *
+ * Can run in IRQ context.
+ */
+void memory_failure_queue(unsigned long pfn, int trapno, int flags)
+{
+ struct memory_failure_cpu *mf_cpu;
+ unsigned long proc_flags;
+ struct memory_failure_entry entry = {
+ .pfn = pfn,
+ .trapno = trapno,
+ .flags = flags,
+ };
+
+ mf_cpu = &get_cpu_var(memory_failure_cpu);
+ spin_lock_irqsave(&mf_cpu->lock, proc_flags);
+ if (kfifo_put(&mf_cpu->fifo, &entry))
+ schedule_work_on(smp_processor_id(), &mf_cpu->work);
+ else
+ pr_err("Memory failure: buffer overflow when queuing memory failure at 0x%#lx\n",
+ pfn);
+ spin_unlock_irqrestore(&mf_cpu->lock, proc_flags);
+ put_cpu_var(memory_failure_cpu);
+}
+EXPORT_SYMBOL_GPL(memory_failure_queue);
+
+static void memory_failure_work_func(struct work_struct *work)
+{
+ struct memory_failure_cpu *mf_cpu;
+ struct memory_failure_entry entry = { 0, };
+ unsigned long proc_flags;
+ int gotten;
+
+ mf_cpu = &__get_cpu_var(memory_failure_cpu);
+ for (;;) {
+ spin_lock_irqsave(&mf_cpu->lock, proc_flags);
+ gotten = kfifo_get(&mf_cpu->fifo, &entry);
+ spin_unlock_irqrestore(&mf_cpu->lock, proc_flags);
+ if (!gotten)
+ break;
+ __memory_failure(entry.pfn, entry.trapno, entry.flags);
+ }
+}
+
+static int __init memory_failure_init(void)
+{
+ struct memory_failure_cpu *mf_cpu;
+ int cpu;
+
+ for_each_possible_cpu(cpu) {
+ mf_cpu = &per_cpu(memory_failure_cpu, cpu);
+ spin_lock_init(&mf_cpu->lock);
+ INIT_KFIFO(mf_cpu->fifo);
+ INIT_WORK(&mf_cpu->work, memory_failure_work_func);
+ }
+
+ return 0;
+}
+core_initcall(memory_failure_init);
+
/**
* unpoison_memory - Unpoison a previously poisoned page
* @pfn: Page number of the to be unpoisoned page
diff --git a/mm/mincore.c b/mm/mincore.c
index a4e6b9d..636a868 100644
--- a/mm/mincore.c
+++ b/mm/mincore.c
@@ -69,12 +69,15 @@
* file will not get a swp_entry_t in its pte, but rather it is like
* any other file mapping (ie. marked !present and faulted in with
* tmpfs's .fault). So swapped out tmpfs mappings are tested here.
- *
- * However when tmpfs moves the page from pagecache and into swapcache,
- * it is still in core, but the find_get_page below won't find it.
- * No big deal, but make a note of it.
*/
page = find_get_page(mapping, pgoff);
+#ifdef CONFIG_SWAP
+ /* shmem/tmpfs may return swap: account for swapcache page too. */
+ if (radix_tree_exceptional_entry(page)) {
+ swp_entry_t swap = radix_to_swp_entry(page);
+ page = find_get_page(&swapper_space, swap.val);
+ }
+#endif
if (page) {
present = PageUptodate(page);
page_cache_release(page);
diff --git a/mm/oom_kill.c b/mm/oom_kill.c
index eafff89..626303b5 100644
--- a/mm/oom_kill.c
+++ b/mm/oom_kill.c
@@ -303,7 +303,7 @@
do_each_thread(g, p) {
unsigned int points;
- if (!p->mm)
+ if (p->exit_state)
continue;
if (oom_unkillable_task(p, mem, nodemask))
continue;
@@ -319,6 +319,8 @@
*/
if (test_tsk_thread_flag(p, TIF_MEMDIE))
return ERR_PTR(-1UL);
+ if (!p->mm)
+ continue;
if (p->flags & PF_EXITING) {
/*
diff --git a/mm/page_alloc.c b/mm/page_alloc.c
index 1dbcf88..6e8ecb6 100644
--- a/mm/page_alloc.c
+++ b/mm/page_alloc.c
@@ -1409,14 +1409,11 @@
{
mode_t mode = S_IFREG | S_IRUSR | S_IWUSR;
struct dentry *dir;
- int err;
- err = init_fault_attr_dentries(&fail_page_alloc.attr,
- "fail_page_alloc");
- if (err)
- return err;
-
- dir = fail_page_alloc.attr.dir;
+ dir = fault_create_debugfs_attr("fail_page_alloc", NULL,
+ &fail_page_alloc.attr);
+ if (IS_ERR(dir))
+ return PTR_ERR(dir);
if (!debugfs_create_bool("ignore-gfp-wait", mode, dir,
&fail_page_alloc.ignore_gfp_wait))
@@ -1430,7 +1427,7 @@
return 0;
fail:
- cleanup_fault_attr_dentries(&fail_page_alloc.attr);
+ debugfs_remove_recursive(dir);
return -ENOMEM;
}
diff --git a/mm/shmem.c b/mm/shmem.c
index 5cc21f8..32f6763 100644
--- a/mm/shmem.c
+++ b/mm/shmem.c
@@ -6,7 +6,8 @@
* 2000-2001 Christoph Rohland
* 2000-2001 SAP AG
* 2002 Red Hat Inc.
- * Copyright (C) 2002-2005 Hugh Dickins.
+ * Copyright (C) 2002-2011 Hugh Dickins.
+ * Copyright (C) 2011 Google Inc.
* Copyright (C) 2002-2005 VERITAS Software Corporation.
* Copyright (C) 2004 Andi Kleen, SuSE Labs
*
@@ -28,7 +29,6 @@
#include <linux/file.h>
#include <linux/mm.h>
#include <linux/module.h>
-#include <linux/percpu_counter.h>
#include <linux/swap.h>
static struct vfsmount *shm_mnt;
@@ -51,6 +51,8 @@
#include <linux/shmem_fs.h>
#include <linux/writeback.h>
#include <linux/blkdev.h>
+#include <linux/pagevec.h>
+#include <linux/percpu_counter.h>
#include <linux/splice.h>
#include <linux/security.h>
#include <linux/swapops.h>
@@ -63,43 +65,17 @@
#include <linux/magic.h>
#include <asm/uaccess.h>
-#include <asm/div64.h>
#include <asm/pgtable.h>
-/*
- * The maximum size of a shmem/tmpfs file is limited by the maximum size of
- * its triple-indirect swap vector - see illustration at shmem_swp_entry().
- *
- * With 4kB page size, maximum file size is just over 2TB on a 32-bit kernel,
- * but one eighth of that on a 64-bit kernel. With 8kB page size, maximum
- * file size is just over 4TB on a 64-bit kernel, but 16TB on a 32-bit kernel,
- * MAX_LFS_FILESIZE being then more restrictive than swap vector layout.
- *
- * We use / and * instead of shifts in the definitions below, so that the swap
- * vector can be tested with small even values (e.g. 20) for ENTRIES_PER_PAGE.
- */
-#define ENTRIES_PER_PAGE (PAGE_CACHE_SIZE/sizeof(unsigned long))
-#define ENTRIES_PER_PAGEPAGE ((unsigned long long)ENTRIES_PER_PAGE*ENTRIES_PER_PAGE)
-
-#define SHMSWP_MAX_INDEX (SHMEM_NR_DIRECT + (ENTRIES_PER_PAGEPAGE/2) * (ENTRIES_PER_PAGE+1))
-#define SHMSWP_MAX_BYTES (SHMSWP_MAX_INDEX << PAGE_CACHE_SHIFT)
-
-#define SHMEM_MAX_BYTES min_t(unsigned long long, SHMSWP_MAX_BYTES, MAX_LFS_FILESIZE)
-#define SHMEM_MAX_INDEX ((unsigned long)((SHMEM_MAX_BYTES+1) >> PAGE_CACHE_SHIFT))
-
#define BLOCKS_PER_PAGE (PAGE_CACHE_SIZE/512)
#define VM_ACCT(size) (PAGE_CACHE_ALIGN(size) >> PAGE_SHIFT)
-/* info->flags needs VM_flags to handle pagein/truncate races efficiently */
-#define SHMEM_PAGEIN VM_READ
-#define SHMEM_TRUNCATE VM_WRITE
-
-/* Definition to limit shmem_truncate's steps between cond_rescheds */
-#define LATENCY_LIMIT 64
-
/* Pretend that each entry is of this size in directory's i_size */
#define BOGO_DIRENT_SIZE 20
+/* Symlink up to this size is kmalloc'ed instead of using a swappable page */
+#define SHORT_SYMLINK_LEN 128
+
struct shmem_xattr {
struct list_head list; /* anchored by shmem_inode_info->xattr_list */
char *name; /* xattr name */
@@ -107,7 +83,7 @@
char value[0];
};
-/* Flag allocation requirements to shmem_getpage and shmem_swp_alloc */
+/* Flag allocation requirements to shmem_getpage */
enum sgp_type {
SGP_READ, /* don't exceed i_size, don't allocate page */
SGP_CACHE, /* don't exceed i_size, may allocate page */
@@ -137,56 +113,6 @@
mapping_gfp_mask(inode->i_mapping), fault_type);
}
-static inline struct page *shmem_dir_alloc(gfp_t gfp_mask)
-{
- /*
- * The above definition of ENTRIES_PER_PAGE, and the use of
- * BLOCKS_PER_PAGE on indirect pages, assume PAGE_CACHE_SIZE:
- * might be reconsidered if it ever diverges from PAGE_SIZE.
- *
- * Mobility flags are masked out as swap vectors cannot move
- */
- return alloc_pages((gfp_mask & ~GFP_MOVABLE_MASK) | __GFP_ZERO,
- PAGE_CACHE_SHIFT-PAGE_SHIFT);
-}
-
-static inline void shmem_dir_free(struct page *page)
-{
- __free_pages(page, PAGE_CACHE_SHIFT-PAGE_SHIFT);
-}
-
-static struct page **shmem_dir_map(struct page *page)
-{
- return (struct page **)kmap_atomic(page, KM_USER0);
-}
-
-static inline void shmem_dir_unmap(struct page **dir)
-{
- kunmap_atomic(dir, KM_USER0);
-}
-
-static swp_entry_t *shmem_swp_map(struct page *page)
-{
- return (swp_entry_t *)kmap_atomic(page, KM_USER1);
-}
-
-static inline void shmem_swp_balance_unmap(void)
-{
- /*
- * When passing a pointer to an i_direct entry, to code which
- * also handles indirect entries and so will shmem_swp_unmap,
- * we must arrange for the preempt count to remain in balance.
- * What kmap_atomic of a lowmem page does depends on config
- * and architecture, so pretend to kmap_atomic some lowmem page.
- */
- (void) kmap_atomic(ZERO_PAGE(0), KM_USER1);
-}
-
-static inline void shmem_swp_unmap(swp_entry_t *entry)
-{
- kunmap_atomic(entry, KM_USER1);
-}
-
static inline struct shmem_sb_info *SHMEM_SB(struct super_block *sb)
{
return sb->s_fs_info;
@@ -244,15 +170,6 @@
static LIST_HEAD(shmem_swaplist);
static DEFINE_MUTEX(shmem_swaplist_mutex);
-static void shmem_free_blocks(struct inode *inode, long pages)
-{
- struct shmem_sb_info *sbinfo = SHMEM_SB(inode->i_sb);
- if (sbinfo->max_blocks) {
- percpu_counter_add(&sbinfo->used_blocks, -pages);
- inode->i_blocks -= pages*BLOCKS_PER_PAGE;
- }
-}
-
static int shmem_reserve_inode(struct super_block *sb)
{
struct shmem_sb_info *sbinfo = SHMEM_SB(sb);
@@ -279,7 +196,7 @@
}
/**
- * shmem_recalc_inode - recalculate the size of an inode
+ * shmem_recalc_inode - recalculate the block usage of an inode
* @inode: inode to recalc
*
* We have to calculate the free blocks since the mm can drop
@@ -297,474 +214,297 @@
freed = info->alloced - info->swapped - inode->i_mapping->nrpages;
if (freed > 0) {
+ struct shmem_sb_info *sbinfo = SHMEM_SB(inode->i_sb);
+ if (sbinfo->max_blocks)
+ percpu_counter_add(&sbinfo->used_blocks, -freed);
info->alloced -= freed;
+ inode->i_blocks -= freed * BLOCKS_PER_PAGE;
shmem_unacct_blocks(info->flags, freed);
- shmem_free_blocks(inode, freed);
}
}
-/**
- * shmem_swp_entry - find the swap vector position in the info structure
- * @info: info structure for the inode
- * @index: index of the page to find
- * @page: optional page to add to the structure. Has to be preset to
- * all zeros
- *
- * If there is no space allocated yet it will return NULL when
- * page is NULL, else it will use the page for the needed block,
- * setting it to NULL on return to indicate that it has been used.
- *
- * The swap vector is organized the following way:
- *
- * There are SHMEM_NR_DIRECT entries directly stored in the
- * shmem_inode_info structure. So small files do not need an addional
- * allocation.
- *
- * For pages with index > SHMEM_NR_DIRECT there is the pointer
- * i_indirect which points to a page which holds in the first half
- * doubly indirect blocks, in the second half triple indirect blocks:
- *
- * For an artificial ENTRIES_PER_PAGE = 4 this would lead to the
- * following layout (for SHMEM_NR_DIRECT == 16):
- *
- * i_indirect -> dir --> 16-19
- * | +-> 20-23
- * |
- * +-->dir2 --> 24-27
- * | +-> 28-31
- * | +-> 32-35
- * | +-> 36-39
- * |
- * +-->dir3 --> 40-43
- * +-> 44-47
- * +-> 48-51
- * +-> 52-55
+/*
+ * Replace item expected in radix tree by a new item, while holding tree lock.
*/
-static swp_entry_t *shmem_swp_entry(struct shmem_inode_info *info, unsigned long index, struct page **page)
+static int shmem_radix_tree_replace(struct address_space *mapping,
+ pgoff_t index, void *expected, void *replacement)
{
- unsigned long offset;
- struct page **dir;
- struct page *subdir;
+ void **pslot;
+ void *item = NULL;
- if (index < SHMEM_NR_DIRECT) {
- shmem_swp_balance_unmap();
- return info->i_direct+index;
- }
- if (!info->i_indirect) {
- if (page) {
- info->i_indirect = *page;
- *page = NULL;
+ VM_BUG_ON(!expected);
+ pslot = radix_tree_lookup_slot(&mapping->page_tree, index);
+ if (pslot)
+ item = radix_tree_deref_slot_protected(pslot,
+ &mapping->tree_lock);
+ if (item != expected)
+ return -ENOENT;
+ if (replacement)
+ radix_tree_replace_slot(pslot, replacement);
+ else
+ radix_tree_delete(&mapping->page_tree, index);
+ return 0;
+}
+
+/*
+ * Like add_to_page_cache_locked, but error if expected item has gone.
+ */
+static int shmem_add_to_page_cache(struct page *page,
+ struct address_space *mapping,
+ pgoff_t index, gfp_t gfp, void *expected)
+{
+ int error = 0;
+
+ VM_BUG_ON(!PageLocked(page));
+ VM_BUG_ON(!PageSwapBacked(page));
+
+ if (!expected)
+ error = radix_tree_preload(gfp & GFP_RECLAIM_MASK);
+ if (!error) {
+ page_cache_get(page);
+ page->mapping = mapping;
+ page->index = index;
+
+ spin_lock_irq(&mapping->tree_lock);
+ if (!expected)
+ error = radix_tree_insert(&mapping->page_tree,
+ index, page);
+ else
+ error = shmem_radix_tree_replace(mapping, index,
+ expected, page);
+ if (!error) {
+ mapping->nrpages++;
+ __inc_zone_page_state(page, NR_FILE_PAGES);
+ __inc_zone_page_state(page, NR_SHMEM);
+ spin_unlock_irq(&mapping->tree_lock);
+ } else {
+ page->mapping = NULL;
+ spin_unlock_irq(&mapping->tree_lock);
+ page_cache_release(page);
}
- return NULL; /* need another page */
+ if (!expected)
+ radix_tree_preload_end();
}
+ if (error)
+ mem_cgroup_uncharge_cache_page(page);
+ return error;
+}
- index -= SHMEM_NR_DIRECT;
- offset = index % ENTRIES_PER_PAGE;
- index /= ENTRIES_PER_PAGE;
- dir = shmem_dir_map(info->i_indirect);
+/*
+ * Like delete_from_page_cache, but substitutes swap for page.
+ */
+static void shmem_delete_from_page_cache(struct page *page, void *radswap)
+{
+ struct address_space *mapping = page->mapping;
+ int error;
- if (index >= ENTRIES_PER_PAGE/2) {
- index -= ENTRIES_PER_PAGE/2;
- dir += ENTRIES_PER_PAGE/2 + index/ENTRIES_PER_PAGE;
- index %= ENTRIES_PER_PAGE;
- subdir = *dir;
- if (!subdir) {
- if (page) {
- *dir = *page;
- *page = NULL;
+ spin_lock_irq(&mapping->tree_lock);
+ error = shmem_radix_tree_replace(mapping, page->index, page, radswap);
+ page->mapping = NULL;
+ mapping->nrpages--;
+ __dec_zone_page_state(page, NR_FILE_PAGES);
+ __dec_zone_page_state(page, NR_SHMEM);
+ spin_unlock_irq(&mapping->tree_lock);
+ page_cache_release(page);
+ BUG_ON(error);
+}
+
+/*
+ * Like find_get_pages, but collecting swap entries as well as pages.
+ */
+static unsigned shmem_find_get_pages_and_swap(struct address_space *mapping,
+ pgoff_t start, unsigned int nr_pages,
+ struct page **pages, pgoff_t *indices)
+{
+ unsigned int i;
+ unsigned int ret;
+ unsigned int nr_found;
+
+ rcu_read_lock();
+restart:
+ nr_found = radix_tree_gang_lookup_slot(&mapping->page_tree,
+ (void ***)pages, indices, start, nr_pages);
+ ret = 0;
+ for (i = 0; i < nr_found; i++) {
+ struct page *page;
+repeat:
+ page = radix_tree_deref_slot((void **)pages[i]);
+ if (unlikely(!page))
+ continue;
+ if (radix_tree_exception(page)) {
+ if (radix_tree_deref_retry(page))
+ goto restart;
+ /*
+ * Otherwise, we must be storing a swap entry
+ * here as an exceptional entry: so return it
+ * without attempting to raise page count.
+ */
+ goto export;
+ }
+ if (!page_cache_get_speculative(page))
+ goto repeat;
+
+ /* Has the page moved? */
+ if (unlikely(page != *((void **)pages[i]))) {
+ page_cache_release(page);
+ goto repeat;
+ }
+export:
+ indices[ret] = indices[i];
+ pages[ret] = page;
+ ret++;
+ }
+ if (unlikely(!ret && nr_found))
+ goto restart;
+ rcu_read_unlock();
+ return ret;
+}
+
+/*
+ * Remove swap entry from radix tree, free the swap and its page cache.
+ */
+static int shmem_free_swap(struct address_space *mapping,
+ pgoff_t index, void *radswap)
+{
+ int error;
+
+ spin_lock_irq(&mapping->tree_lock);
+ error = shmem_radix_tree_replace(mapping, index, radswap, NULL);
+ spin_unlock_irq(&mapping->tree_lock);
+ if (!error)
+ free_swap_and_cache(radix_to_swp_entry(radswap));
+ return error;
+}
+
+/*
+ * Pagevec may contain swap entries, so shuffle up pages before releasing.
+ */
+static void shmem_pagevec_release(struct pagevec *pvec)
+{
+ int i, j;
+
+ for (i = 0, j = 0; i < pagevec_count(pvec); i++) {
+ struct page *page = pvec->pages[i];
+ if (!radix_tree_exceptional_entry(page))
+ pvec->pages[j++] = page;
+ }
+ pvec->nr = j;
+ pagevec_release(pvec);
+}
+
+/*
+ * Remove range of pages and swap entries from radix tree, and free them.
+ */
+void shmem_truncate_range(struct inode *inode, loff_t lstart, loff_t lend)
+{
+ struct address_space *mapping = inode->i_mapping;
+ struct shmem_inode_info *info = SHMEM_I(inode);
+ pgoff_t start = (lstart + PAGE_CACHE_SIZE - 1) >> PAGE_CACHE_SHIFT;
+ unsigned partial = lstart & (PAGE_CACHE_SIZE - 1);
+ pgoff_t end = (lend >> PAGE_CACHE_SHIFT);
+ struct pagevec pvec;
+ pgoff_t indices[PAGEVEC_SIZE];
+ long nr_swaps_freed = 0;
+ pgoff_t index;
+ int i;
+
+ BUG_ON((lend & (PAGE_CACHE_SIZE - 1)) != (PAGE_CACHE_SIZE - 1));
+
+ pagevec_init(&pvec, 0);
+ index = start;
+ while (index <= end) {
+ pvec.nr = shmem_find_get_pages_and_swap(mapping, index,
+ min(end - index, (pgoff_t)PAGEVEC_SIZE - 1) + 1,
+ pvec.pages, indices);
+ if (!pvec.nr)
+ break;
+ mem_cgroup_uncharge_start();
+ for (i = 0; i < pagevec_count(&pvec); i++) {
+ struct page *page = pvec.pages[i];
+
+ index = indices[i];
+ if (index > end)
+ break;
+
+ if (radix_tree_exceptional_entry(page)) {
+ nr_swaps_freed += !shmem_free_swap(mapping,
+ index, page);
+ continue;
}
- shmem_dir_unmap(dir);
- return NULL; /* need another page */
+
+ if (!trylock_page(page))
+ continue;
+ if (page->mapping == mapping) {
+ VM_BUG_ON(PageWriteback(page));
+ truncate_inode_page(mapping, page);
+ }
+ unlock_page(page);
}
- shmem_dir_unmap(dir);
- dir = shmem_dir_map(subdir);
+ shmem_pagevec_release(&pvec);
+ mem_cgroup_uncharge_end();
+ cond_resched();
+ index++;
}
- dir += index;
- subdir = *dir;
- if (!subdir) {
- if (!page || !(subdir = *page)) {
- shmem_dir_unmap(dir);
- return NULL; /* need a page */
+ if (partial) {
+ struct page *page = NULL;
+ shmem_getpage(inode, start - 1, &page, SGP_READ, NULL);
+ if (page) {
+ zero_user_segment(page, partial, PAGE_CACHE_SIZE);
+ set_page_dirty(page);
+ unlock_page(page);
+ page_cache_release(page);
}
- *dir = subdir;
- *page = NULL;
}
- shmem_dir_unmap(dir);
- return shmem_swp_map(subdir) + offset;
-}
-static void shmem_swp_set(struct shmem_inode_info *info, swp_entry_t *entry, unsigned long value)
-{
- long incdec = value? 1: -1;
-
- entry->val = value;
- info->swapped += incdec;
- if ((unsigned long)(entry - info->i_direct) >= SHMEM_NR_DIRECT) {
- struct page *page = kmap_atomic_to_page(entry);
- set_page_private(page, page_private(page) + incdec);
- }
-}
-
-/**
- * shmem_swp_alloc - get the position of the swap entry for the page.
- * @info: info structure for the inode
- * @index: index of the page to find
- * @sgp: check and recheck i_size? skip allocation?
- * @gfp: gfp mask to use for any page allocation
- *
- * If the entry does not exist, allocate it.
- */
-static swp_entry_t *shmem_swp_alloc(struct shmem_inode_info *info,
- unsigned long index, enum sgp_type sgp, gfp_t gfp)
-{
- struct inode *inode = &info->vfs_inode;
- struct shmem_sb_info *sbinfo = SHMEM_SB(inode->i_sb);
- struct page *page = NULL;
- swp_entry_t *entry;
-
- if (sgp != SGP_WRITE &&
- ((loff_t) index << PAGE_CACHE_SHIFT) >= i_size_read(inode))
- return ERR_PTR(-EINVAL);
-
- while (!(entry = shmem_swp_entry(info, index, &page))) {
- if (sgp == SGP_READ)
- return shmem_swp_map(ZERO_PAGE(0));
- /*
- * Test used_blocks against 1 less max_blocks, since we have 1 data
- * page (and perhaps indirect index pages) yet to allocate:
- * a waste to allocate index if we cannot allocate data.
- */
- if (sbinfo->max_blocks) {
- if (percpu_counter_compare(&sbinfo->used_blocks,
- sbinfo->max_blocks - 1) >= 0)
- return ERR_PTR(-ENOSPC);
- percpu_counter_inc(&sbinfo->used_blocks);
- inode->i_blocks += BLOCKS_PER_PAGE;
+ index = start;
+ for ( ; ; ) {
+ cond_resched();
+ pvec.nr = shmem_find_get_pages_and_swap(mapping, index,
+ min(end - index, (pgoff_t)PAGEVEC_SIZE - 1) + 1,
+ pvec.pages, indices);
+ if (!pvec.nr) {
+ if (index == start)
+ break;
+ index = start;
+ continue;
}
-
- spin_unlock(&info->lock);
- page = shmem_dir_alloc(gfp);
- spin_lock(&info->lock);
-
- if (!page) {
- shmem_free_blocks(inode, 1);
- return ERR_PTR(-ENOMEM);
- }
- if (sgp != SGP_WRITE &&
- ((loff_t) index << PAGE_CACHE_SHIFT) >= i_size_read(inode)) {
- entry = ERR_PTR(-EINVAL);
+ if (index == start && indices[0] > end) {
+ shmem_pagevec_release(&pvec);
break;
}
- if (info->next_index <= index)
- info->next_index = index + 1;
- }
- if (page) {
- /* another task gave its page, or truncated the file */
- shmem_free_blocks(inode, 1);
- shmem_dir_free(page);
- }
- if (info->next_index <= index && !IS_ERR(entry))
- info->next_index = index + 1;
- return entry;
-}
+ mem_cgroup_uncharge_start();
+ for (i = 0; i < pagevec_count(&pvec); i++) {
+ struct page *page = pvec.pages[i];
-/**
- * shmem_free_swp - free some swap entries in a directory
- * @dir: pointer to the directory
- * @edir: pointer after last entry of the directory
- * @punch_lock: pointer to spinlock when needed for the holepunch case
- */
-static int shmem_free_swp(swp_entry_t *dir, swp_entry_t *edir,
- spinlock_t *punch_lock)
-{
- spinlock_t *punch_unlock = NULL;
- swp_entry_t *ptr;
- int freed = 0;
+ index = indices[i];
+ if (index > end)
+ break;
- for (ptr = dir; ptr < edir; ptr++) {
- if (ptr->val) {
- if (unlikely(punch_lock)) {
- punch_unlock = punch_lock;
- punch_lock = NULL;
- spin_lock(punch_unlock);
- if (!ptr->val)
- continue;
+ if (radix_tree_exceptional_entry(page)) {
+ nr_swaps_freed += !shmem_free_swap(mapping,
+ index, page);
+ continue;
}
- free_swap_and_cache(*ptr);
- *ptr = (swp_entry_t){0};
- freed++;
- }
- }
- if (punch_unlock)
- spin_unlock(punch_unlock);
- return freed;
-}
-static int shmem_map_and_free_swp(struct page *subdir, int offset,
- int limit, struct page ***dir, spinlock_t *punch_lock)
-{
- swp_entry_t *ptr;
- int freed = 0;
-
- ptr = shmem_swp_map(subdir);
- for (; offset < limit; offset += LATENCY_LIMIT) {
- int size = limit - offset;
- if (size > LATENCY_LIMIT)
- size = LATENCY_LIMIT;
- freed += shmem_free_swp(ptr+offset, ptr+offset+size,
- punch_lock);
- if (need_resched()) {
- shmem_swp_unmap(ptr);
- if (*dir) {
- shmem_dir_unmap(*dir);
- *dir = NULL;
+ lock_page(page);
+ if (page->mapping == mapping) {
+ VM_BUG_ON(PageWriteback(page));
+ truncate_inode_page(mapping, page);
}
- cond_resched();
- ptr = shmem_swp_map(subdir);
+ unlock_page(page);
}
- }
- shmem_swp_unmap(ptr);
- return freed;
-}
-
-static void shmem_free_pages(struct list_head *next)
-{
- struct page *page;
- int freed = 0;
-
- do {
- page = container_of(next, struct page, lru);
- next = next->next;
- shmem_dir_free(page);
- freed++;
- if (freed >= LATENCY_LIMIT) {
- cond_resched();
- freed = 0;
- }
- } while (next);
-}
-
-void shmem_truncate_range(struct inode *inode, loff_t start, loff_t end)
-{
- struct shmem_inode_info *info = SHMEM_I(inode);
- unsigned long idx;
- unsigned long size;
- unsigned long limit;
- unsigned long stage;
- unsigned long diroff;
- struct page **dir;
- struct page *topdir;
- struct page *middir;
- struct page *subdir;
- swp_entry_t *ptr;
- LIST_HEAD(pages_to_free);
- long nr_pages_to_free = 0;
- long nr_swaps_freed = 0;
- int offset;
- int freed;
- int punch_hole;
- spinlock_t *needs_lock;
- spinlock_t *punch_lock;
- unsigned long upper_limit;
-
- truncate_inode_pages_range(inode->i_mapping, start, end);
-
- inode->i_ctime = inode->i_mtime = CURRENT_TIME;
- idx = (start + PAGE_CACHE_SIZE - 1) >> PAGE_CACHE_SHIFT;
- if (idx >= info->next_index)
- return;
-
- spin_lock(&info->lock);
- info->flags |= SHMEM_TRUNCATE;
- if (likely(end == (loff_t) -1)) {
- limit = info->next_index;
- upper_limit = SHMEM_MAX_INDEX;
- info->next_index = idx;
- needs_lock = NULL;
- punch_hole = 0;
- } else {
- if (end + 1 >= inode->i_size) { /* we may free a little more */
- limit = (inode->i_size + PAGE_CACHE_SIZE - 1) >>
- PAGE_CACHE_SHIFT;
- upper_limit = SHMEM_MAX_INDEX;
- } else {
- limit = (end + 1) >> PAGE_CACHE_SHIFT;
- upper_limit = limit;
- }
- needs_lock = &info->lock;
- punch_hole = 1;
- }
-
- topdir = info->i_indirect;
- if (topdir && idx <= SHMEM_NR_DIRECT && !punch_hole) {
- info->i_indirect = NULL;
- nr_pages_to_free++;
- list_add(&topdir->lru, &pages_to_free);
- }
- spin_unlock(&info->lock);
-
- if (info->swapped && idx < SHMEM_NR_DIRECT) {
- ptr = info->i_direct;
- size = limit;
- if (size > SHMEM_NR_DIRECT)
- size = SHMEM_NR_DIRECT;
- nr_swaps_freed = shmem_free_swp(ptr+idx, ptr+size, needs_lock);
- }
-
- /*
- * If there are no indirect blocks or we are punching a hole
- * below indirect blocks, nothing to be done.
- */
- if (!topdir || limit <= SHMEM_NR_DIRECT)
- goto done2;
-
- /*
- * The truncation case has already dropped info->lock, and we're safe
- * because i_size and next_index have already been lowered, preventing
- * access beyond. But in the punch_hole case, we still need to take
- * the lock when updating the swap directory, because there might be
- * racing accesses by shmem_getpage(SGP_CACHE), shmem_unuse_inode or
- * shmem_writepage. However, whenever we find we can remove a whole
- * directory page (not at the misaligned start or end of the range),
- * we first NULLify its pointer in the level above, and then have no
- * need to take the lock when updating its contents: needs_lock and
- * punch_lock (either pointing to info->lock or NULL) manage this.
- */
-
- upper_limit -= SHMEM_NR_DIRECT;
- limit -= SHMEM_NR_DIRECT;
- idx = (idx > SHMEM_NR_DIRECT)? (idx - SHMEM_NR_DIRECT): 0;
- offset = idx % ENTRIES_PER_PAGE;
- idx -= offset;
-
- dir = shmem_dir_map(topdir);
- stage = ENTRIES_PER_PAGEPAGE/2;
- if (idx < ENTRIES_PER_PAGEPAGE/2) {
- middir = topdir;
- diroff = idx/ENTRIES_PER_PAGE;
- } else {
- dir += ENTRIES_PER_PAGE/2;
- dir += (idx - ENTRIES_PER_PAGEPAGE/2)/ENTRIES_PER_PAGEPAGE;
- while (stage <= idx)
- stage += ENTRIES_PER_PAGEPAGE;
- middir = *dir;
- if (*dir) {
- diroff = ((idx - ENTRIES_PER_PAGEPAGE/2) %
- ENTRIES_PER_PAGEPAGE) / ENTRIES_PER_PAGE;
- if (!diroff && !offset && upper_limit >= stage) {
- if (needs_lock) {
- spin_lock(needs_lock);
- *dir = NULL;
- spin_unlock(needs_lock);
- needs_lock = NULL;
- } else
- *dir = NULL;
- nr_pages_to_free++;
- list_add(&middir->lru, &pages_to_free);
- }
- shmem_dir_unmap(dir);
- dir = shmem_dir_map(middir);
- } else {
- diroff = 0;
- offset = 0;
- idx = stage;
- }
- }
-
- for (; idx < limit; idx += ENTRIES_PER_PAGE, diroff++) {
- if (unlikely(idx == stage)) {
- shmem_dir_unmap(dir);
- dir = shmem_dir_map(topdir) +
- ENTRIES_PER_PAGE/2 + idx/ENTRIES_PER_PAGEPAGE;
- while (!*dir) {
- dir++;
- idx += ENTRIES_PER_PAGEPAGE;
- if (idx >= limit)
- goto done1;
- }
- stage = idx + ENTRIES_PER_PAGEPAGE;
- middir = *dir;
- if (punch_hole)
- needs_lock = &info->lock;
- if (upper_limit >= stage) {
- if (needs_lock) {
- spin_lock(needs_lock);
- *dir = NULL;
- spin_unlock(needs_lock);
- needs_lock = NULL;
- } else
- *dir = NULL;
- nr_pages_to_free++;
- list_add(&middir->lru, &pages_to_free);
- }
- shmem_dir_unmap(dir);
- cond_resched();
- dir = shmem_dir_map(middir);
- diroff = 0;
- }
- punch_lock = needs_lock;
- subdir = dir[diroff];
- if (subdir && !offset && upper_limit-idx >= ENTRIES_PER_PAGE) {
- if (needs_lock) {
- spin_lock(needs_lock);
- dir[diroff] = NULL;
- spin_unlock(needs_lock);
- punch_lock = NULL;
- } else
- dir[diroff] = NULL;
- nr_pages_to_free++;
- list_add(&subdir->lru, &pages_to_free);
- }
- if (subdir && page_private(subdir) /* has swap entries */) {
- size = limit - idx;
- if (size > ENTRIES_PER_PAGE)
- size = ENTRIES_PER_PAGE;
- freed = shmem_map_and_free_swp(subdir,
- offset, size, &dir, punch_lock);
- if (!dir)
- dir = shmem_dir_map(middir);
- nr_swaps_freed += freed;
- if (offset || punch_lock) {
- spin_lock(&info->lock);
- set_page_private(subdir,
- page_private(subdir) - freed);
- spin_unlock(&info->lock);
- } else
- BUG_ON(page_private(subdir) != freed);
- }
- offset = 0;
- }
-done1:
- shmem_dir_unmap(dir);
-done2:
- if (inode->i_mapping->nrpages && (info->flags & SHMEM_PAGEIN)) {
- /*
- * Call truncate_inode_pages again: racing shmem_unuse_inode
- * may have swizzled a page in from swap since
- * truncate_pagecache or generic_delete_inode did it, before we
- * lowered next_index. Also, though shmem_getpage checks
- * i_size before adding to cache, no recheck after: so fix the
- * narrow window there too.
- */
- truncate_inode_pages_range(inode->i_mapping, start, end);
+ shmem_pagevec_release(&pvec);
+ mem_cgroup_uncharge_end();
+ index++;
}
spin_lock(&info->lock);
- info->flags &= ~SHMEM_TRUNCATE;
info->swapped -= nr_swaps_freed;
- if (nr_pages_to_free)
- shmem_free_blocks(inode, nr_pages_to_free);
shmem_recalc_inode(inode);
spin_unlock(&info->lock);
- /*
- * Empty swap vector directory pages to be freed?
- */
- if (!list_empty(&pages_to_free)) {
- pages_to_free.prev->next = NULL;
- shmem_free_pages(pages_to_free.next);
- }
+ inode->i_ctime = inode->i_mtime = CURRENT_TIME;
}
EXPORT_SYMBOL_GPL(shmem_truncate_range);
@@ -780,37 +520,7 @@
if (S_ISREG(inode->i_mode) && (attr->ia_valid & ATTR_SIZE)) {
loff_t oldsize = inode->i_size;
loff_t newsize = attr->ia_size;
- struct page *page = NULL;
- if (newsize < oldsize) {
- /*
- * If truncating down to a partial page, then
- * if that page is already allocated, hold it
- * in memory until the truncation is over, so
- * truncate_partial_page cannot miss it were
- * it assigned to swap.
- */
- if (newsize & (PAGE_CACHE_SIZE-1)) {
- (void) shmem_getpage(inode,
- newsize >> PAGE_CACHE_SHIFT,
- &page, SGP_READ, NULL);
- if (page)
- unlock_page(page);
- }
- /*
- * Reset SHMEM_PAGEIN flag so that shmem_truncate can
- * detect if any pages might have been added to cache
- * after truncate_inode_pages. But we needn't bother
- * if it's being fully truncated to zero-length: the
- * nrpages check is efficient enough in that case.
- */
- if (newsize) {
- struct shmem_inode_info *info = SHMEM_I(inode);
- spin_lock(&info->lock);
- info->flags &= ~SHMEM_PAGEIN;
- spin_unlock(&info->lock);
- }
- }
if (newsize != oldsize) {
i_size_write(inode, newsize);
inode->i_ctime = inode->i_mtime = CURRENT_TIME;
@@ -822,8 +532,6 @@
/* unmap again to remove racily COWed private pages */
unmap_mapping_range(inode->i_mapping, holebegin, 0, 1);
}
- if (page)
- page_cache_release(page);
}
setattr_copy(inode, attr);
@@ -848,7 +556,8 @@
list_del_init(&info->swaplist);
mutex_unlock(&shmem_swaplist_mutex);
}
- }
+ } else
+ kfree(info->symlink);
list_for_each_entry_safe(xattr, nxattr, &info->xattr_list, list) {
kfree(xattr->name);
@@ -859,106 +568,27 @@
end_writeback(inode);
}
-static inline int shmem_find_swp(swp_entry_t entry, swp_entry_t *dir, swp_entry_t *edir)
+/*
+ * If swap found in inode, free it and move page from swapcache to filecache.
+ */
+static int shmem_unuse_inode(struct shmem_inode_info *info,
+ swp_entry_t swap, struct page *page)
{
- swp_entry_t *ptr;
-
- for (ptr = dir; ptr < edir; ptr++) {
- if (ptr->val == entry.val)
- return ptr - dir;
- }
- return -1;
-}
-
-static int shmem_unuse_inode(struct shmem_inode_info *info, swp_entry_t entry, struct page *page)
-{
- struct address_space *mapping;
- unsigned long idx;
- unsigned long size;
- unsigned long limit;
- unsigned long stage;
- struct page **dir;
- struct page *subdir;
- swp_entry_t *ptr;
- int offset;
+ struct address_space *mapping = info->vfs_inode.i_mapping;
+ void *radswap;
+ pgoff_t index;
int error;
- idx = 0;
- ptr = info->i_direct;
- spin_lock(&info->lock);
- if (!info->swapped) {
- list_del_init(&info->swaplist);
- goto lost2;
- }
- limit = info->next_index;
- size = limit;
- if (size > SHMEM_NR_DIRECT)
- size = SHMEM_NR_DIRECT;
- offset = shmem_find_swp(entry, ptr, ptr+size);
- if (offset >= 0) {
- shmem_swp_balance_unmap();
- goto found;
- }
- if (!info->i_indirect)
- goto lost2;
-
- dir = shmem_dir_map(info->i_indirect);
- stage = SHMEM_NR_DIRECT + ENTRIES_PER_PAGEPAGE/2;
-
- for (idx = SHMEM_NR_DIRECT; idx < limit; idx += ENTRIES_PER_PAGE, dir++) {
- if (unlikely(idx == stage)) {
- shmem_dir_unmap(dir-1);
- if (cond_resched_lock(&info->lock)) {
- /* check it has not been truncated */
- if (limit > info->next_index) {
- limit = info->next_index;
- if (idx >= limit)
- goto lost2;
- }
- }
- dir = shmem_dir_map(info->i_indirect) +
- ENTRIES_PER_PAGE/2 + idx/ENTRIES_PER_PAGEPAGE;
- while (!*dir) {
- dir++;
- idx += ENTRIES_PER_PAGEPAGE;
- if (idx >= limit)
- goto lost1;
- }
- stage = idx + ENTRIES_PER_PAGEPAGE;
- subdir = *dir;
- shmem_dir_unmap(dir);
- dir = shmem_dir_map(subdir);
- }
- subdir = *dir;
- if (subdir && page_private(subdir)) {
- ptr = shmem_swp_map(subdir);
- size = limit - idx;
- if (size > ENTRIES_PER_PAGE)
- size = ENTRIES_PER_PAGE;
- offset = shmem_find_swp(entry, ptr, ptr+size);
- shmem_swp_unmap(ptr);
- if (offset >= 0) {
- shmem_dir_unmap(dir);
- ptr = shmem_swp_map(subdir);
- goto found;
- }
- }
- }
-lost1:
- shmem_dir_unmap(dir-1);
-lost2:
- spin_unlock(&info->lock);
- return 0;
-found:
- idx += offset;
- ptr += offset;
+ radswap = swp_to_radix_entry(swap);
+ index = radix_tree_locate_item(&mapping->page_tree, radswap);
+ if (index == -1)
+ return 0;
/*
* Move _head_ to start search for next from here.
* But be careful: shmem_evict_inode checks list_empty without taking
* mutex, and there's an instant in list_move_tail when info->swaplist
- * would appear empty, if it were the only one on shmem_swaplist. We
- * could avoid doing it if inode NULL; or use this minor optimization.
+ * would appear empty, if it were the only one on shmem_swaplist.
*/
if (shmem_swaplist.next != &info->swaplist)
list_move_tail(&shmem_swaplist, &info->swaplist);
@@ -968,29 +598,34 @@
* but also to hold up shmem_evict_inode(): so inode cannot be freed
* beneath us (pagelock doesn't help until the page is in pagecache).
*/
- mapping = info->vfs_inode.i_mapping;
- error = add_to_page_cache_locked(page, mapping, idx, GFP_NOWAIT);
+ error = shmem_add_to_page_cache(page, mapping, index,
+ GFP_NOWAIT, radswap);
/* which does mem_cgroup_uncharge_cache_page on error */
if (error != -ENOMEM) {
+ /*
+ * Truncation and eviction use free_swap_and_cache(), which
+ * only does trylock page: if we raced, best clean up here.
+ */
delete_from_swap_cache(page);
set_page_dirty(page);
- info->flags |= SHMEM_PAGEIN;
- shmem_swp_set(info, ptr, 0);
- swap_free(entry);
+ if (!error) {
+ spin_lock(&info->lock);
+ info->swapped--;
+ spin_unlock(&info->lock);
+ swap_free(swap);
+ }
error = 1; /* not an error, but entry was found */
}
- shmem_swp_unmap(ptr);
- spin_unlock(&info->lock);
return error;
}
/*
- * shmem_unuse() search for an eventually swapped out shmem page.
+ * Search through swapped inodes to find and replace swap by page.
*/
-int shmem_unuse(swp_entry_t entry, struct page *page)
+int shmem_unuse(swp_entry_t swap, struct page *page)
{
- struct list_head *p, *next;
+ struct list_head *this, *next;
struct shmem_inode_info *info;
int found = 0;
int error;
@@ -999,32 +634,25 @@
* Charge page using GFP_KERNEL while we can wait, before taking
* the shmem_swaplist_mutex which might hold up shmem_writepage().
* Charged back to the user (not to caller) when swap account is used.
- * add_to_page_cache() will be called with GFP_NOWAIT.
*/
error = mem_cgroup_cache_charge(page, current->mm, GFP_KERNEL);
if (error)
goto out;
- /*
- * Try to preload while we can wait, to not make a habit of
- * draining atomic reserves; but don't latch on to this cpu,
- * it's okay if sometimes we get rescheduled after this.
- */
- error = radix_tree_preload(GFP_KERNEL);
- if (error)
- goto uncharge;
- radix_tree_preload_end();
+ /* No radix_tree_preload: swap entry keeps a place for page in tree */
mutex_lock(&shmem_swaplist_mutex);
- list_for_each_safe(p, next, &shmem_swaplist) {
- info = list_entry(p, struct shmem_inode_info, swaplist);
- found = shmem_unuse_inode(info, entry, page);
+ list_for_each_safe(this, next, &shmem_swaplist) {
+ info = list_entry(this, struct shmem_inode_info, swaplist);
+ if (info->swapped)
+ found = shmem_unuse_inode(info, swap, page);
+ else
+ list_del_init(&info->swaplist);
cond_resched();
if (found)
break;
}
mutex_unlock(&shmem_swaplist_mutex);
-uncharge:
if (!found)
mem_cgroup_uncharge_cache_page(page);
if (found < 0)
@@ -1041,10 +669,10 @@
static int shmem_writepage(struct page *page, struct writeback_control *wbc)
{
struct shmem_inode_info *info;
- swp_entry_t *entry, swap;
struct address_space *mapping;
- unsigned long index;
struct inode *inode;
+ swp_entry_t swap;
+ pgoff_t index;
BUG_ON(!PageLocked(page));
mapping = page->mapping;
@@ -1073,50 +701,32 @@
/*
* Add inode to shmem_unuse()'s list of swapped-out inodes,
- * if it's not already there. Do it now because we cannot take
- * mutex while holding spinlock, and must do so before the page
- * is moved to swap cache, when its pagelock no longer protects
+ * if it's not already there. Do it now before the page is
+ * moved to swap cache, when its pagelock no longer protects
* the inode from eviction. But don't unlock the mutex until
- * we've taken the spinlock, because shmem_unuse_inode() will
- * prune a !swapped inode from the swaplist under both locks.
+ * we've incremented swapped, because shmem_unuse_inode() will
+ * prune a !swapped inode from the swaplist under this mutex.
*/
mutex_lock(&shmem_swaplist_mutex);
if (list_empty(&info->swaplist))
list_add_tail(&info->swaplist, &shmem_swaplist);
- spin_lock(&info->lock);
- mutex_unlock(&shmem_swaplist_mutex);
-
- if (index >= info->next_index) {
- BUG_ON(!(info->flags & SHMEM_TRUNCATE));
- goto unlock;
- }
- entry = shmem_swp_entry(info, index, NULL);
- if (entry->val) {
- WARN_ON_ONCE(1); /* Still happens? Tell us about it! */
- free_swap_and_cache(*entry);
- shmem_swp_set(info, entry, 0);
- }
- shmem_recalc_inode(inode);
-
if (add_to_swap_cache(page, swap, GFP_ATOMIC) == 0) {
- delete_from_page_cache(page);
- shmem_swp_set(info, entry, swap.val);
- shmem_swp_unmap(entry);
swap_shmem_alloc(swap);
+ shmem_delete_from_page_cache(page, swp_to_radix_entry(swap));
+
+ spin_lock(&info->lock);
+ info->swapped++;
+ shmem_recalc_inode(inode);
spin_unlock(&info->lock);
+
+ mutex_unlock(&shmem_swaplist_mutex);
BUG_ON(page_mapped(page));
swap_writepage(page, wbc);
return 0;
}
- shmem_swp_unmap(entry);
-unlock:
- spin_unlock(&info->lock);
- /*
- * add_to_swap_cache() doesn't return -EEXIST, so we can safely
- * clear SWAP_HAS_CACHE flag.
- */
+ mutex_unlock(&shmem_swaplist_mutex);
swapcache_free(swap, NULL);
redirty:
set_page_dirty(page);
@@ -1153,35 +763,33 @@
}
#endif /* CONFIG_TMPFS */
-static struct page *shmem_swapin(swp_entry_t entry, gfp_t gfp,
- struct shmem_inode_info *info, unsigned long idx)
+static struct page *shmem_swapin(swp_entry_t swap, gfp_t gfp,
+ struct shmem_inode_info *info, pgoff_t index)
{
struct mempolicy mpol, *spol;
struct vm_area_struct pvma;
- struct page *page;
spol = mpol_cond_copy(&mpol,
- mpol_shared_policy_lookup(&info->policy, idx));
+ mpol_shared_policy_lookup(&info->policy, index));
/* Create a pseudo vma that just contains the policy */
pvma.vm_start = 0;
- pvma.vm_pgoff = idx;
+ pvma.vm_pgoff = index;
pvma.vm_ops = NULL;
pvma.vm_policy = spol;
- page = swapin_readahead(entry, gfp, &pvma, 0);
- return page;
+ return swapin_readahead(swap, gfp, &pvma, 0);
}
static struct page *shmem_alloc_page(gfp_t gfp,
- struct shmem_inode_info *info, unsigned long idx)
+ struct shmem_inode_info *info, pgoff_t index)
{
struct vm_area_struct pvma;
/* Create a pseudo vma that just contains the policy */
pvma.vm_start = 0;
- pvma.vm_pgoff = idx;
+ pvma.vm_pgoff = index;
pvma.vm_ops = NULL;
- pvma.vm_policy = mpol_shared_policy_lookup(&info->policy, idx);
+ pvma.vm_policy = mpol_shared_policy_lookup(&info->policy, index);
/*
* alloc_page_vma() will drop the shared policy reference
@@ -1190,19 +798,19 @@
}
#else /* !CONFIG_NUMA */
#ifdef CONFIG_TMPFS
-static inline void shmem_show_mpol(struct seq_file *seq, struct mempolicy *p)
+static inline void shmem_show_mpol(struct seq_file *seq, struct mempolicy *mpol)
{
}
#endif /* CONFIG_TMPFS */
-static inline struct page *shmem_swapin(swp_entry_t entry, gfp_t gfp,
- struct shmem_inode_info *info, unsigned long idx)
+static inline struct page *shmem_swapin(swp_entry_t swap, gfp_t gfp,
+ struct shmem_inode_info *info, pgoff_t index)
{
- return swapin_readahead(entry, gfp, NULL, 0);
+ return swapin_readahead(swap, gfp, NULL, 0);
}
static inline struct page *shmem_alloc_page(gfp_t gfp,
- struct shmem_inode_info *info, unsigned long idx)
+ struct shmem_inode_info *info, pgoff_t index)
{
return alloc_page(gfp);
}
@@ -1222,243 +830,190 @@
* vm. If we swap it in we mark it dirty since we also free the swap
* entry since a page cannot live in both the swap and page cache
*/
-static int shmem_getpage_gfp(struct inode *inode, pgoff_t idx,
+static int shmem_getpage_gfp(struct inode *inode, pgoff_t index,
struct page **pagep, enum sgp_type sgp, gfp_t gfp, int *fault_type)
{
struct address_space *mapping = inode->i_mapping;
- struct shmem_inode_info *info = SHMEM_I(inode);
+ struct shmem_inode_info *info;
struct shmem_sb_info *sbinfo;
struct page *page;
- struct page *prealloc_page = NULL;
- swp_entry_t *entry;
swp_entry_t swap;
int error;
- int ret;
+ int once = 0;
- if (idx >= SHMEM_MAX_INDEX)
+ if (index > (MAX_LFS_FILESIZE >> PAGE_CACHE_SHIFT))
return -EFBIG;
repeat:
- page = find_lock_page(mapping, idx);
- if (page) {
+ swap.val = 0;
+ page = find_lock_page(mapping, index);
+ if (radix_tree_exceptional_entry(page)) {
+ swap = radix_to_swp_entry(page);
+ page = NULL;
+ }
+
+ if (sgp != SGP_WRITE &&
+ ((loff_t)index << PAGE_CACHE_SHIFT) >= i_size_read(inode)) {
+ error = -EINVAL;
+ goto failed;
+ }
+
+ if (page || (sgp == SGP_READ && !swap.val)) {
/*
* Once we can get the page lock, it must be uptodate:
* if there were an error in reading back from swap,
* the page would not be inserted into the filecache.
*/
- BUG_ON(!PageUptodate(page));
- goto done;
+ BUG_ON(page && !PageUptodate(page));
+ *pagep = page;
+ return 0;
}
/*
- * Try to preload while we can wait, to not make a habit of
- * draining atomic reserves; but don't latch on to this cpu.
+ * Fast cache lookup did not find it:
+ * bring it back from swap or allocate.
*/
- error = radix_tree_preload(gfp & GFP_RECLAIM_MASK);
- if (error)
- goto out;
- radix_tree_preload_end();
-
- if (sgp != SGP_READ && !prealloc_page) {
- prealloc_page = shmem_alloc_page(gfp, info, idx);
- if (prealloc_page) {
- SetPageSwapBacked(prealloc_page);
- if (mem_cgroup_cache_charge(prealloc_page,
- current->mm, GFP_KERNEL)) {
- page_cache_release(prealloc_page);
- prealloc_page = NULL;
- }
- }
- }
-
- spin_lock(&info->lock);
- shmem_recalc_inode(inode);
- entry = shmem_swp_alloc(info, idx, sgp, gfp);
- if (IS_ERR(entry)) {
- spin_unlock(&info->lock);
- error = PTR_ERR(entry);
- goto out;
- }
- swap = *entry;
+ info = SHMEM_I(inode);
+ sbinfo = SHMEM_SB(inode->i_sb);
if (swap.val) {
/* Look it up and read it in.. */
page = lookup_swap_cache(swap);
if (!page) {
- shmem_swp_unmap(entry);
- spin_unlock(&info->lock);
/* here we actually do the io */
if (fault_type)
*fault_type |= VM_FAULT_MAJOR;
- page = shmem_swapin(swap, gfp, info, idx);
+ page = shmem_swapin(swap, gfp, info, index);
if (!page) {
- spin_lock(&info->lock);
- entry = shmem_swp_alloc(info, idx, sgp, gfp);
- if (IS_ERR(entry))
- error = PTR_ERR(entry);
- else {
- if (entry->val == swap.val)
- error = -ENOMEM;
- shmem_swp_unmap(entry);
- }
- spin_unlock(&info->lock);
- if (error)
- goto out;
- goto repeat;
+ error = -ENOMEM;
+ goto failed;
}
- wait_on_page_locked(page);
- page_cache_release(page);
- goto repeat;
}
/* We have to do this with page locked to prevent races */
- if (!trylock_page(page)) {
- shmem_swp_unmap(entry);
- spin_unlock(&info->lock);
- wait_on_page_locked(page);
- page_cache_release(page);
- goto repeat;
- }
- if (PageWriteback(page)) {
- shmem_swp_unmap(entry);
- spin_unlock(&info->lock);
- wait_on_page_writeback(page);
- unlock_page(page);
- page_cache_release(page);
- goto repeat;
- }
+ lock_page(page);
if (!PageUptodate(page)) {
- shmem_swp_unmap(entry);
- spin_unlock(&info->lock);
- unlock_page(page);
- page_cache_release(page);
error = -EIO;
- goto out;
+ goto failed;
+ }
+ wait_on_page_writeback(page);
+
+ /* Someone may have already done it for us */
+ if (page->mapping) {
+ if (page->mapping == mapping &&
+ page->index == index)
+ goto done;
+ error = -EEXIST;
+ goto failed;
}
- error = add_to_page_cache_locked(page, mapping,
- idx, GFP_NOWAIT);
- if (error) {
- shmem_swp_unmap(entry);
- spin_unlock(&info->lock);
- if (error == -ENOMEM) {
- /*
- * reclaim from proper memory cgroup and
- * call memcg's OOM if needed.
- */
- error = mem_cgroup_shmem_charge_fallback(
- page, current->mm, gfp);
- if (error) {
- unlock_page(page);
- page_cache_release(page);
- goto out;
- }
- }
- unlock_page(page);
- page_cache_release(page);
- goto repeat;
- }
+ error = mem_cgroup_cache_charge(page, current->mm,
+ gfp & GFP_RECLAIM_MASK);
+ if (!error)
+ error = shmem_add_to_page_cache(page, mapping, index,
+ gfp, swp_to_radix_entry(swap));
+ if (error)
+ goto failed;
- info->flags |= SHMEM_PAGEIN;
- shmem_swp_set(info, entry, 0);
- shmem_swp_unmap(entry);
- delete_from_swap_cache(page);
+ spin_lock(&info->lock);
+ info->swapped--;
+ shmem_recalc_inode(inode);
spin_unlock(&info->lock);
+
+ delete_from_swap_cache(page);
set_page_dirty(page);
swap_free(swap);
- } else if (sgp == SGP_READ) {
- shmem_swp_unmap(entry);
- page = find_get_page(mapping, idx);
- if (page && !trylock_page(page)) {
- spin_unlock(&info->lock);
- wait_on_page_locked(page);
- page_cache_release(page);
- goto repeat;
+ } else {
+ if (shmem_acct_block(info->flags)) {
+ error = -ENOSPC;
+ goto failed;
}
- spin_unlock(&info->lock);
-
- } else if (prealloc_page) {
- shmem_swp_unmap(entry);
- sbinfo = SHMEM_SB(inode->i_sb);
if (sbinfo->max_blocks) {
if (percpu_counter_compare(&sbinfo->used_blocks,
- sbinfo->max_blocks) >= 0 ||
- shmem_acct_block(info->flags))
- goto nospace;
+ sbinfo->max_blocks) >= 0) {
+ error = -ENOSPC;
+ goto unacct;
+ }
percpu_counter_inc(&sbinfo->used_blocks);
- inode->i_blocks += BLOCKS_PER_PAGE;
- } else if (shmem_acct_block(info->flags))
- goto nospace;
-
- page = prealloc_page;
- prealloc_page = NULL;
-
- entry = shmem_swp_alloc(info, idx, sgp, gfp);
- if (IS_ERR(entry))
- error = PTR_ERR(entry);
- else {
- swap = *entry;
- shmem_swp_unmap(entry);
- }
- ret = error || swap.val;
- if (ret)
- mem_cgroup_uncharge_cache_page(page);
- else
- ret = add_to_page_cache_lru(page, mapping,
- idx, GFP_NOWAIT);
- /*
- * At add_to_page_cache_lru() failure,
- * uncharge will be done automatically.
- */
- if (ret) {
- shmem_unacct_blocks(info->flags, 1);
- shmem_free_blocks(inode, 1);
- spin_unlock(&info->lock);
- page_cache_release(page);
- if (error)
- goto out;
- goto repeat;
}
- info->flags |= SHMEM_PAGEIN;
+ page = shmem_alloc_page(gfp, info, index);
+ if (!page) {
+ error = -ENOMEM;
+ goto decused;
+ }
+
+ SetPageSwapBacked(page);
+ __set_page_locked(page);
+ error = mem_cgroup_cache_charge(page, current->mm,
+ gfp & GFP_RECLAIM_MASK);
+ if (!error)
+ error = shmem_add_to_page_cache(page, mapping, index,
+ gfp, NULL);
+ if (error)
+ goto decused;
+ lru_cache_add_anon(page);
+
+ spin_lock(&info->lock);
info->alloced++;
+ inode->i_blocks += BLOCKS_PER_PAGE;
+ shmem_recalc_inode(inode);
spin_unlock(&info->lock);
+
clear_highpage(page);
flush_dcache_page(page);
SetPageUptodate(page);
if (sgp == SGP_DIRTY)
set_page_dirty(page);
-
- } else {
- spin_unlock(&info->lock);
- error = -ENOMEM;
- goto out;
}
done:
- *pagep = page;
- error = 0;
-out:
- if (prealloc_page) {
- mem_cgroup_uncharge_cache_page(prealloc_page);
- page_cache_release(prealloc_page);
+ /* Perhaps the file has been truncated since we checked */
+ if (sgp != SGP_WRITE &&
+ ((loff_t)index << PAGE_CACHE_SHIFT) >= i_size_read(inode)) {
+ error = -EINVAL;
+ goto trunc;
}
- return error;
+ *pagep = page;
+ return 0;
-nospace:
/*
- * Perhaps the page was brought in from swap between find_lock_page
- * and taking info->lock? We allow for that at add_to_page_cache_lru,
- * but must also avoid reporting a spurious ENOSPC while working on a
- * full tmpfs.
+ * Error recovery.
*/
- page = find_get_page(mapping, idx);
+trunc:
+ ClearPageDirty(page);
+ delete_from_page_cache(page);
+ spin_lock(&info->lock);
+ info->alloced--;
+ inode->i_blocks -= BLOCKS_PER_PAGE;
spin_unlock(&info->lock);
+decused:
+ if (sbinfo->max_blocks)
+ percpu_counter_add(&sbinfo->used_blocks, -1);
+unacct:
+ shmem_unacct_blocks(info->flags, 1);
+failed:
+ if (swap.val && error != -EINVAL) {
+ struct page *test = find_get_page(mapping, index);
+ if (test && !radix_tree_exceptional_entry(test))
+ page_cache_release(test);
+ /* Have another try if the entry has changed */
+ if (test != swp_to_radix_entry(swap))
+ error = -EEXIST;
+ }
if (page) {
+ unlock_page(page);
page_cache_release(page);
+ }
+ if (error == -ENOSPC && !once++) {
+ info = SHMEM_I(inode);
+ spin_lock(&info->lock);
+ shmem_recalc_inode(inode);
+ spin_unlock(&info->lock);
goto repeat;
}
- error = -ENOSPC;
- goto out;
+ if (error == -EEXIST)
+ goto repeat;
+ return error;
}
static int shmem_fault(struct vm_area_struct *vma, struct vm_fault *vmf)
@@ -1467,9 +1022,6 @@
int error;
int ret = VM_FAULT_LOCKED;
- if (((loff_t)vmf->pgoff << PAGE_CACHE_SHIFT) >= i_size_read(inode))
- return VM_FAULT_SIGBUS;
-
error = shmem_getpage(inode, vmf->pgoff, &vmf->page, SGP_CACHE, &ret);
if (error)
return ((error == -ENOMEM) ? VM_FAULT_OOM : VM_FAULT_SIGBUS);
@@ -1482,20 +1034,20 @@
}
#ifdef CONFIG_NUMA
-static int shmem_set_policy(struct vm_area_struct *vma, struct mempolicy *new)
+static int shmem_set_policy(struct vm_area_struct *vma, struct mempolicy *mpol)
{
- struct inode *i = vma->vm_file->f_path.dentry->d_inode;
- return mpol_set_shared_policy(&SHMEM_I(i)->policy, vma, new);
+ struct inode *inode = vma->vm_file->f_path.dentry->d_inode;
+ return mpol_set_shared_policy(&SHMEM_I(inode)->policy, vma, mpol);
}
static struct mempolicy *shmem_get_policy(struct vm_area_struct *vma,
unsigned long addr)
{
- struct inode *i = vma->vm_file->f_path.dentry->d_inode;
- unsigned long idx;
+ struct inode *inode = vma->vm_file->f_path.dentry->d_inode;
+ pgoff_t index;
- idx = ((addr - vma->vm_start) >> PAGE_SHIFT) + vma->vm_pgoff;
- return mpol_shared_policy_lookup(&SHMEM_I(i)->policy, idx);
+ index = ((addr - vma->vm_start) >> PAGE_SHIFT) + vma->vm_pgoff;
+ return mpol_shared_policy_lookup(&SHMEM_I(inode)->policy, index);
}
#endif
@@ -1593,7 +1145,7 @@
#ifdef CONFIG_TMPFS
static const struct inode_operations shmem_symlink_inode_operations;
-static const struct inode_operations shmem_symlink_inline_operations;
+static const struct inode_operations shmem_short_symlink_operations;
static int
shmem_write_begin(struct file *file, struct address_space *mapping,
@@ -1626,7 +1178,8 @@
{
struct inode *inode = filp->f_path.dentry->d_inode;
struct address_space *mapping = inode->i_mapping;
- unsigned long index, offset;
+ pgoff_t index;
+ unsigned long offset;
enum sgp_type sgp = SGP_READ;
/*
@@ -1642,7 +1195,8 @@
for (;;) {
struct page *page = NULL;
- unsigned long end_index, nr, ret;
+ pgoff_t end_index;
+ unsigned long nr, ret;
loff_t i_size = i_size_read(inode);
end_index = i_size >> PAGE_CACHE_SHIFT;
@@ -1880,8 +1434,9 @@
buf->f_namelen = NAME_MAX;
if (sbinfo->max_blocks) {
buf->f_blocks = sbinfo->max_blocks;
- buf->f_bavail = buf->f_bfree =
- sbinfo->max_blocks - percpu_counter_sum(&sbinfo->used_blocks);
+ buf->f_bavail =
+ buf->f_bfree = sbinfo->max_blocks -
+ percpu_counter_sum(&sbinfo->used_blocks);
}
if (sbinfo->max_inodes) {
buf->f_files = sbinfo->max_inodes;
@@ -2055,10 +1610,13 @@
info = SHMEM_I(inode);
inode->i_size = len-1;
- if (len <= SHMEM_SYMLINK_INLINE_LEN) {
- /* do it inline */
- memcpy(info->inline_symlink, symname, len);
- inode->i_op = &shmem_symlink_inline_operations;
+ if (len <= SHORT_SYMLINK_LEN) {
+ info->symlink = kmemdup(symname, len, GFP_KERNEL);
+ if (!info->symlink) {
+ iput(inode);
+ return -ENOMEM;
+ }
+ inode->i_op = &shmem_short_symlink_operations;
} else {
error = shmem_getpage(inode, 0, &page, SGP_WRITE, NULL);
if (error) {
@@ -2081,17 +1639,17 @@
return 0;
}
-static void *shmem_follow_link_inline(struct dentry *dentry, struct nameidata *nd)
+static void *shmem_follow_short_symlink(struct dentry *dentry, struct nameidata *nd)
{
- nd_set_link(nd, SHMEM_I(dentry->d_inode)->inline_symlink);
+ nd_set_link(nd, SHMEM_I(dentry->d_inode)->symlink);
return NULL;
}
static void *shmem_follow_link(struct dentry *dentry, struct nameidata *nd)
{
struct page *page = NULL;
- int res = shmem_getpage(dentry->d_inode, 0, &page, SGP_READ, NULL);
- nd_set_link(nd, res ? ERR_PTR(res) : kmap(page));
+ int error = shmem_getpage(dentry->d_inode, 0, &page, SGP_READ, NULL);
+ nd_set_link(nd, error ? ERR_PTR(error) : kmap(page));
if (page)
unlock_page(page);
return page;
@@ -2202,7 +1760,6 @@
return err;
}
-
static const struct xattr_handler *shmem_xattr_handlers[] = {
#ifdef CONFIG_TMPFS_POSIX_ACL
&generic_acl_access_handler,
@@ -2332,9 +1889,9 @@
}
#endif /* CONFIG_TMPFS_XATTR */
-static const struct inode_operations shmem_symlink_inline_operations = {
+static const struct inode_operations shmem_short_symlink_operations = {
.readlink = generic_readlink,
- .follow_link = shmem_follow_link_inline,
+ .follow_link = shmem_follow_short_symlink,
#ifdef CONFIG_TMPFS_XATTR
.setxattr = shmem_setxattr,
.getxattr = shmem_getxattr,
@@ -2534,8 +2091,7 @@
if (config.max_inodes < inodes)
goto out;
/*
- * Those tests also disallow limited->unlimited while any are in
- * use, so i_blocks will always be zero when max_blocks is zero;
+ * Those tests disallow limited->unlimited while any are in use;
* but we must separately disallow unlimited->limited, because
* in that case we have no record of how much is already in use.
*/
@@ -2627,7 +2183,7 @@
goto failed;
sbinfo->free_inodes = sbinfo->max_inodes;
- sb->s_maxbytes = SHMEM_MAX_BYTES;
+ sb->s_maxbytes = MAX_LFS_FILESIZE;
sb->s_blocksize = PAGE_CACHE_SIZE;
sb->s_blocksize_bits = PAGE_CACHE_SHIFT;
sb->s_magic = TMPFS_MAGIC;
@@ -2662,14 +2218,14 @@
static struct inode *shmem_alloc_inode(struct super_block *sb)
{
- struct shmem_inode_info *p;
- p = (struct shmem_inode_info *)kmem_cache_alloc(shmem_inode_cachep, GFP_KERNEL);
- if (!p)
+ struct shmem_inode_info *info;
+ info = kmem_cache_alloc(shmem_inode_cachep, GFP_KERNEL);
+ if (!info)
return NULL;
- return &p->vfs_inode;
+ return &info->vfs_inode;
}
-static void shmem_i_callback(struct rcu_head *head)
+static void shmem_destroy_callback(struct rcu_head *head)
{
struct inode *inode = container_of(head, struct inode, i_rcu);
INIT_LIST_HEAD(&inode->i_dentry);
@@ -2678,29 +2234,26 @@
static void shmem_destroy_inode(struct inode *inode)
{
- if ((inode->i_mode & S_IFMT) == S_IFREG) {
- /* only struct inode is valid if it's an inline symlink */
+ if ((inode->i_mode & S_IFMT) == S_IFREG)
mpol_free_shared_policy(&SHMEM_I(inode)->policy);
- }
- call_rcu(&inode->i_rcu, shmem_i_callback);
+ call_rcu(&inode->i_rcu, shmem_destroy_callback);
}
-static void init_once(void *foo)
+static void shmem_init_inode(void *foo)
{
- struct shmem_inode_info *p = (struct shmem_inode_info *) foo;
-
- inode_init_once(&p->vfs_inode);
+ struct shmem_inode_info *info = foo;
+ inode_init_once(&info->vfs_inode);
}
-static int init_inodecache(void)
+static int shmem_init_inodecache(void)
{
shmem_inode_cachep = kmem_cache_create("shmem_inode_cache",
sizeof(struct shmem_inode_info),
- 0, SLAB_PANIC, init_once);
+ 0, SLAB_PANIC, shmem_init_inode);
return 0;
}
-static void destroy_inodecache(void)
+static void shmem_destroy_inodecache(void)
{
kmem_cache_destroy(shmem_inode_cachep);
}
@@ -2797,21 +2350,20 @@
#endif
};
-
static struct dentry *shmem_mount(struct file_system_type *fs_type,
int flags, const char *dev_name, void *data)
{
return mount_nodev(fs_type, flags, data, shmem_fill_super);
}
-static struct file_system_type tmpfs_fs_type = {
+static struct file_system_type shmem_fs_type = {
.owner = THIS_MODULE,
.name = "tmpfs",
.mount = shmem_mount,
.kill_sb = kill_litter_super,
};
-int __init init_tmpfs(void)
+int __init shmem_init(void)
{
int error;
@@ -2819,18 +2371,18 @@
if (error)
goto out4;
- error = init_inodecache();
+ error = shmem_init_inodecache();
if (error)
goto out3;
- error = register_filesystem(&tmpfs_fs_type);
+ error = register_filesystem(&shmem_fs_type);
if (error) {
printk(KERN_ERR "Could not register tmpfs\n");
goto out2;
}
- shm_mnt = vfs_kern_mount(&tmpfs_fs_type, MS_NOUSER,
- tmpfs_fs_type.name, NULL);
+ shm_mnt = vfs_kern_mount(&shmem_fs_type, MS_NOUSER,
+ shmem_fs_type.name, NULL);
if (IS_ERR(shm_mnt)) {
error = PTR_ERR(shm_mnt);
printk(KERN_ERR "Could not kern_mount tmpfs\n");
@@ -2839,9 +2391,9 @@
return 0;
out1:
- unregister_filesystem(&tmpfs_fs_type);
+ unregister_filesystem(&shmem_fs_type);
out2:
- destroy_inodecache();
+ shmem_destroy_inodecache();
out3:
bdi_destroy(&shmem_backing_dev_info);
out4:
@@ -2849,45 +2401,6 @@
return error;
}
-#ifdef CONFIG_CGROUP_MEM_RES_CTLR
-/**
- * mem_cgroup_get_shmem_target - find a page or entry assigned to the shmem file
- * @inode: the inode to be searched
- * @pgoff: the offset to be searched
- * @pagep: the pointer for the found page to be stored
- * @ent: the pointer for the found swap entry to be stored
- *
- * If a page is found, refcount of it is incremented. Callers should handle
- * these refcount.
- */
-void mem_cgroup_get_shmem_target(struct inode *inode, pgoff_t pgoff,
- struct page **pagep, swp_entry_t *ent)
-{
- swp_entry_t entry = { .val = 0 }, *ptr;
- struct page *page = NULL;
- struct shmem_inode_info *info = SHMEM_I(inode);
-
- if ((pgoff << PAGE_CACHE_SHIFT) >= i_size_read(inode))
- goto out;
-
- spin_lock(&info->lock);
- ptr = shmem_swp_entry(info, pgoff, NULL);
-#ifdef CONFIG_SWAP
- if (ptr && ptr->val) {
- entry.val = ptr->val;
- page = find_get_page(&swapper_space, entry.val);
- } else
-#endif
- page = find_get_page(inode->i_mapping, pgoff);
- if (ptr)
- shmem_swp_unmap(ptr);
- spin_unlock(&info->lock);
-out:
- *pagep = page;
- *ent = entry;
-}
-#endif
-
#else /* !CONFIG_SHMEM */
/*
@@ -2901,23 +2414,23 @@
#include <linux/ramfs.h>
-static struct file_system_type tmpfs_fs_type = {
+static struct file_system_type shmem_fs_type = {
.name = "tmpfs",
.mount = ramfs_mount,
.kill_sb = kill_litter_super,
};
-int __init init_tmpfs(void)
+int __init shmem_init(void)
{
- BUG_ON(register_filesystem(&tmpfs_fs_type) != 0);
+ BUG_ON(register_filesystem(&shmem_fs_type) != 0);
- shm_mnt = kern_mount(&tmpfs_fs_type);
+ shm_mnt = kern_mount(&shmem_fs_type);
BUG_ON(IS_ERR(shm_mnt));
return 0;
}
-int shmem_unuse(swp_entry_t entry, struct page *page)
+int shmem_unuse(swp_entry_t swap, struct page *page)
{
return 0;
}
@@ -2927,43 +2440,17 @@
return 0;
}
-void shmem_truncate_range(struct inode *inode, loff_t start, loff_t end)
+void shmem_truncate_range(struct inode *inode, loff_t lstart, loff_t lend)
{
- truncate_inode_pages_range(inode->i_mapping, start, end);
+ truncate_inode_pages_range(inode->i_mapping, lstart, lend);
}
EXPORT_SYMBOL_GPL(shmem_truncate_range);
-#ifdef CONFIG_CGROUP_MEM_RES_CTLR
-/**
- * mem_cgroup_get_shmem_target - find a page or entry assigned to the shmem file
- * @inode: the inode to be searched
- * @pgoff: the offset to be searched
- * @pagep: the pointer for the found page to be stored
- * @ent: the pointer for the found swap entry to be stored
- *
- * If a page is found, refcount of it is incremented. Callers should handle
- * these refcount.
- */
-void mem_cgroup_get_shmem_target(struct inode *inode, pgoff_t pgoff,
- struct page **pagep, swp_entry_t *ent)
-{
- struct page *page = NULL;
-
- if ((pgoff << PAGE_CACHE_SHIFT) >= i_size_read(inode))
- goto out;
- page = find_get_page(inode->i_mapping, pgoff);
-out:
- *pagep = page;
- *ent = (swp_entry_t){ .val = 0 };
-}
-#endif
-
#define shmem_vm_ops generic_file_vm_ops
#define shmem_file_operations ramfs_file_operations
#define shmem_get_inode(sb, dir, mode, dev, flags) ramfs_get_inode(sb, dir, mode, dev)
#define shmem_acct_size(flags, size) 0
#define shmem_unacct_size(flags, size) do {} while (0)
-#define SHMEM_MAX_BYTES MAX_LFS_FILESIZE
#endif /* CONFIG_SHMEM */
@@ -2987,7 +2474,7 @@
if (IS_ERR(shm_mnt))
return (void *)shm_mnt;
- if (size < 0 || size > SHMEM_MAX_BYTES)
+ if (size < 0 || size > MAX_LFS_FILESIZE)
return ERR_PTR(-EINVAL);
if (shmem_acct_size(flags, size))
diff --git a/mm/slab.c b/mm/slab.c
index 9594740..6d90a09 100644
--- a/mm/slab.c
+++ b/mm/slab.c
@@ -622,6 +622,51 @@
static struct lock_class_key on_slab_l3_key;
static struct lock_class_key on_slab_alc_key;
+static struct lock_class_key debugobj_l3_key;
+static struct lock_class_key debugobj_alc_key;
+
+static void slab_set_lock_classes(struct kmem_cache *cachep,
+ struct lock_class_key *l3_key, struct lock_class_key *alc_key,
+ int q)
+{
+ struct array_cache **alc;
+ struct kmem_list3 *l3;
+ int r;
+
+ l3 = cachep->nodelists[q];
+ if (!l3)
+ return;
+
+ lockdep_set_class(&l3->list_lock, l3_key);
+ alc = l3->alien;
+ /*
+ * FIXME: This check for BAD_ALIEN_MAGIC
+ * should go away when common slab code is taught to
+ * work even without alien caches.
+ * Currently, non NUMA code returns BAD_ALIEN_MAGIC
+ * for alloc_alien_cache,
+ */
+ if (!alc || (unsigned long)alc == BAD_ALIEN_MAGIC)
+ return;
+ for_each_node(r) {
+ if (alc[r])
+ lockdep_set_class(&alc[r]->lock, alc_key);
+ }
+}
+
+static void slab_set_debugobj_lock_classes_node(struct kmem_cache *cachep, int node)
+{
+ slab_set_lock_classes(cachep, &debugobj_l3_key, &debugobj_alc_key, node);
+}
+
+static void slab_set_debugobj_lock_classes(struct kmem_cache *cachep)
+{
+ int node;
+
+ for_each_online_node(node)
+ slab_set_debugobj_lock_classes_node(cachep, node);
+}
+
static void init_node_lock_keys(int q)
{
struct cache_sizes *s = malloc_sizes;
@@ -630,29 +675,14 @@
return;
for (s = malloc_sizes; s->cs_size != ULONG_MAX; s++) {
- struct array_cache **alc;
struct kmem_list3 *l3;
- int r;
l3 = s->cs_cachep->nodelists[q];
if (!l3 || OFF_SLAB(s->cs_cachep))
continue;
- lockdep_set_class(&l3->list_lock, &on_slab_l3_key);
- alc = l3->alien;
- /*
- * FIXME: This check for BAD_ALIEN_MAGIC
- * should go away when common slab code is taught to
- * work even without alien caches.
- * Currently, non NUMA code returns BAD_ALIEN_MAGIC
- * for alloc_alien_cache,
- */
- if (!alc || (unsigned long)alc == BAD_ALIEN_MAGIC)
- continue;
- for_each_node(r) {
- if (alc[r])
- lockdep_set_class(&alc[r]->lock,
- &on_slab_alc_key);
- }
+
+ slab_set_lock_classes(s->cs_cachep, &on_slab_l3_key,
+ &on_slab_alc_key, q);
}
}
@@ -671,6 +701,14 @@
static inline void init_lock_keys(void)
{
}
+
+static void slab_set_debugobj_lock_classes_node(struct kmem_cache *cachep, int node)
+{
+}
+
+static void slab_set_debugobj_lock_classes(struct kmem_cache *cachep)
+{
+}
#endif
/*
@@ -1264,6 +1302,8 @@
spin_unlock_irq(&l3->list_lock);
kfree(shared);
free_alien_cache(alien);
+ if (cachep->flags & SLAB_DEBUG_OBJECTS)
+ slab_set_debugobj_lock_classes_node(cachep, node);
}
init_node_lock_keys(node);
@@ -1626,6 +1666,9 @@
{
struct kmem_cache *cachep;
+ /* Annotate slab for lockdep -- annotate the malloc caches */
+ init_lock_keys();
+
/* 6) resize the head arrays to their final sizes */
mutex_lock(&cache_chain_mutex);
list_for_each_entry(cachep, &cache_chain, next)
@@ -1636,9 +1679,6 @@
/* Done! */
g_cpucache_up = FULL;
- /* Annotate slab for lockdep -- annotate the malloc caches */
- init_lock_keys();
-
/*
* Register a cpu startup notifier callback that initializes
* cpu_cache_get for all new cpus
@@ -2426,6 +2466,16 @@
goto oops;
}
+ if (flags & SLAB_DEBUG_OBJECTS) {
+ /*
+ * Would deadlock through slab_destroy()->call_rcu()->
+ * debug_object_activate()->kmem_cache_alloc().
+ */
+ WARN_ON_ONCE(flags & SLAB_DESTROY_BY_RCU);
+
+ slab_set_debugobj_lock_classes(cachep);
+ }
+
/* cache setup completed, link it into the list */
list_add(&cachep->next, &cache_chain);
oops:
diff --git a/mm/slub.c b/mm/slub.c
index eb5a8f9..9f662d7 100644
--- a/mm/slub.c
+++ b/mm/slub.c
@@ -701,7 +701,7 @@
return check_bytes8(start, value, bytes);
value64 = value | value << 8 | value << 16 | value << 24;
- value64 = value64 | value64 << 32;
+ value64 = (value64 & 0xffffffff) | value64 << 32;
prefix = 8 - ((unsigned long)start) % 8;
if (prefix) {
@@ -1854,7 +1854,7 @@
new.frozen = 0;
- if (!new.inuse && n->nr_partial < s->min_partial)
+ if (!new.inuse && n->nr_partial > s->min_partial)
m = M_FREE;
else if (new.freelist) {
m = M_PARTIAL;
@@ -2387,11 +2387,13 @@
slab_empty:
if (prior) {
/*
- * Slab still on the partial list.
+ * Slab on the partial list.
*/
remove_partial(n, page);
stat(s, FREE_REMOVE_PARTIAL);
- }
+ } else
+ /* Slab must be on the full list */
+ remove_full(s, page);
spin_unlock_irqrestore(&n->list_lock, flags);
stat(s, FREE_SLAB);
diff --git a/mm/swapfile.c b/mm/swapfile.c
index 1b8c339..17bc224 100644
--- a/mm/swapfile.c
+++ b/mm/swapfile.c
@@ -1924,20 +1924,24 @@
/*
* Find out how many pages are allowed for a single swap
- * device. There are two limiting factors: 1) the number of
- * bits for the swap offset in the swp_entry_t type and
- * 2) the number of bits in the a swap pte as defined by
- * the different architectures. In order to find the
- * largest possible bit mask a swap entry with swap type 0
+ * device. There are three limiting factors: 1) the number
+ * of bits for the swap offset in the swp_entry_t type, and
+ * 2) the number of bits in the swap pte as defined by the
+ * the different architectures, and 3) the number of free bits
+ * in an exceptional radix_tree entry. In order to find the
+ * largest possible bit mask, a swap entry with swap type 0
* and swap offset ~0UL is created, encoded to a swap pte,
- * decoded to a swp_entry_t again and finally the swap
+ * decoded to a swp_entry_t again, and finally the swap
* offset is extracted. This will mask all the bits from
* the initial ~0UL mask that can't be encoded in either
* the swp_entry_t or the architecture definition of a
- * swap pte.
+ * swap pte. Then the same is done for a radix_tree entry.
*/
maxpages = swp_offset(pte_to_swp_entry(
- swp_entry_to_pte(swp_entry(0, ~0UL)))) + 1;
+ swp_entry_to_pte(swp_entry(0, ~0UL))));
+ maxpages = swp_offset(radix_to_swp_entry(
+ swp_to_radix_entry(swp_entry(0, maxpages)))) + 1;
+
if (maxpages > swap_header->info.last_page) {
maxpages = swap_header->info.last_page + 1;
/* p->max is an unsigned int: don't overflow it */
diff --git a/mm/truncate.c b/mm/truncate.c
index 232eb27..b40ac6d 100644
--- a/mm/truncate.c
+++ b/mm/truncate.c
@@ -336,6 +336,14 @@
unsigned long count = 0;
int i;
+ /*
+ * Note: this function may get called on a shmem/tmpfs mapping:
+ * pagevec_lookup() might then return 0 prematurely (because it
+ * got a gangful of swap entries); but it's hardly worth worrying
+ * about - it can rarely have anything to free from such a mapping
+ * (most pages are dirty), and already skips over any difficulties.
+ */
+
pagevec_init(&pvec, 0);
while (index <= end && pagevec_lookup(&pvec, mapping, index,
min(end - index, (pgoff_t)PAGEVEC_SIZE - 1) + 1)) {
diff --git a/mm/vmalloc.c b/mm/vmalloc.c
index 464621d..7ef0903 100644
--- a/mm/vmalloc.c
+++ b/mm/vmalloc.c
@@ -725,9 +725,10 @@
#define VMAP_BBMAP_BITS_MIN (VMAP_MAX_ALLOC*2)
#define VMAP_MIN(x, y) ((x) < (y) ? (x) : (y)) /* can't use min() */
#define VMAP_MAX(x, y) ((x) > (y) ? (x) : (y)) /* can't use max() */
-#define VMAP_BBMAP_BITS VMAP_MIN(VMAP_BBMAP_BITS_MAX, \
- VMAP_MAX(VMAP_BBMAP_BITS_MIN, \
- VMALLOC_PAGES / NR_CPUS / 16))
+#define VMAP_BBMAP_BITS \
+ VMAP_MIN(VMAP_BBMAP_BITS_MAX, \
+ VMAP_MAX(VMAP_BBMAP_BITS_MIN, \
+ VMALLOC_PAGES / roundup_pow_of_two(NR_CPUS) / 16))
#define VMAP_BLOCK_SIZE (VMAP_BBMAP_BITS * PAGE_SIZE)
diff --git a/net/atm/br2684.c b/net/atm/br2684.c
index 2252c20..52cfd0c 100644
--- a/net/atm/br2684.c
+++ b/net/atm/br2684.c
@@ -242,8 +242,6 @@
if (brdev->payload == p_bridged) {
skb_push(skb, 2);
memset(skb->data, 0, 2);
- } else { /* p_routed */
- skb_pull(skb, ETH_HLEN);
}
}
skb_debug(skb);
diff --git a/net/bridge/br_if.c b/net/bridge/br_if.c
index 3176e2e..2cdf007 100644
--- a/net/bridge/br_if.c
+++ b/net/bridge/br_if.c
@@ -417,6 +417,7 @@
int br_del_if(struct net_bridge *br, struct net_device *dev)
{
struct net_bridge_port *p;
+ bool changed_addr;
p = br_port_get_rtnl(dev);
if (!p || p->br != br)
@@ -425,9 +426,12 @@
del_nbp(p);
spin_lock_bh(&br->lock);
- br_stp_recalculate_bridge_id(br);
+ changed_addr = br_stp_recalculate_bridge_id(br);
spin_unlock_bh(&br->lock);
+ if (changed_addr)
+ call_netdevice_notifiers(NETDEV_CHANGEADDR, br->dev);
+
netdev_update_features(br->dev);
return 0;
diff --git a/net/bridge/br_notify.c b/net/bridge/br_notify.c
index 6545ee9..a76b621 100644
--- a/net/bridge/br_notify.c
+++ b/net/bridge/br_notify.c
@@ -34,6 +34,7 @@
struct net_device *dev = ptr;
struct net_bridge_port *p;
struct net_bridge *br;
+ bool changed_addr;
int err;
/* register of bridge completed, add sysfs entries */
@@ -57,8 +58,12 @@
case NETDEV_CHANGEADDR:
spin_lock_bh(&br->lock);
br_fdb_changeaddr(p, dev->dev_addr);
- br_stp_recalculate_bridge_id(br);
+ changed_addr = br_stp_recalculate_bridge_id(br);
spin_unlock_bh(&br->lock);
+
+ if (changed_addr)
+ call_netdevice_notifiers(NETDEV_CHANGEADDR, br->dev);
+
break;
case NETDEV_CHANGE:
diff --git a/net/bridge/netfilter/ebtables.c b/net/bridge/netfilter/ebtables.c
index 2b5ca1a..5864cc4 100644
--- a/net/bridge/netfilter/ebtables.c
+++ b/net/bridge/netfilter/ebtables.c
@@ -1198,7 +1198,8 @@
if (table->check && table->check(newinfo, table->valid_hooks)) {
BUGPRINT("The table doesn't like its own initial data, lol\n");
- return ERR_PTR(-EINVAL);
+ ret = -EINVAL;
+ goto free_chainstack;
}
table->private = newinfo;
diff --git a/net/core/Makefile b/net/core/Makefile
index 8a04dd2..0d357b1 100644
--- a/net/core/Makefile
+++ b/net/core/Makefile
@@ -3,7 +3,7 @@
#
obj-y := sock.o request_sock.o skbuff.o iovec.o datagram.o stream.o scm.o \
- gen_stats.o gen_estimator.o net_namespace.o
+ gen_stats.o gen_estimator.o net_namespace.o secure_seq.o
obj-$(CONFIG_SYSCTL) += sysctl_net_core.o
diff --git a/net/core/scm.c b/net/core/scm.c
index 4c1ef02..811b53f 100644
--- a/net/core/scm.c
+++ b/net/core/scm.c
@@ -192,7 +192,7 @@
goto error;
cred->uid = cred->euid = p->creds.uid;
- cred->gid = cred->egid = p->creds.uid;
+ cred->gid = cred->egid = p->creds.gid;
put_cred(p->cred);
p->cred = cred;
}
diff --git a/net/core/secure_seq.c b/net/core/secure_seq.c
new file mode 100644
index 0000000..45329d7
--- /dev/null
+++ b/net/core/secure_seq.c
@@ -0,0 +1,184 @@
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/cryptohash.h>
+#include <linux/module.h>
+#include <linux/cache.h>
+#include <linux/random.h>
+#include <linux/hrtimer.h>
+#include <linux/ktime.h>
+#include <linux/string.h>
+
+#include <net/secure_seq.h>
+
+static u32 net_secret[MD5_MESSAGE_BYTES / 4] ____cacheline_aligned;
+
+static int __init net_secret_init(void)
+{
+ get_random_bytes(net_secret, sizeof(net_secret));
+ return 0;
+}
+late_initcall(net_secret_init);
+
+static u32 seq_scale(u32 seq)
+{
+ /*
+ * As close as possible to RFC 793, which
+ * suggests using a 250 kHz clock.
+ * Further reading shows this assumes 2 Mb/s networks.
+ * For 10 Mb/s Ethernet, a 1 MHz clock is appropriate.
+ * For 10 Gb/s Ethernet, a 1 GHz clock should be ok, but
+ * we also need to limit the resolution so that the u32 seq
+ * overlaps less than one time per MSL (2 minutes).
+ * Choosing a clock of 64 ns period is OK. (period of 274 s)
+ */
+ return seq + (ktime_to_ns(ktime_get_real()) >> 6);
+}
+
+#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
+__u32 secure_tcpv6_sequence_number(__be32 *saddr, __be32 *daddr,
+ __be16 sport, __be16 dport)
+{
+ u32 secret[MD5_MESSAGE_BYTES / 4];
+ u32 hash[MD5_DIGEST_WORDS];
+ u32 i;
+
+ memcpy(hash, saddr, 16);
+ for (i = 0; i < 4; i++)
+ secret[i] = net_secret[i] + daddr[i];
+ secret[4] = net_secret[4] +
+ (((__force u16)sport << 16) + (__force u16)dport);
+ for (i = 5; i < MD5_MESSAGE_BYTES / 4; i++)
+ secret[i] = net_secret[i];
+
+ md5_transform(hash, secret);
+
+ return seq_scale(hash[0]);
+}
+EXPORT_SYMBOL(secure_tcpv6_sequence_number);
+
+u32 secure_ipv6_port_ephemeral(const __be32 *saddr, const __be32 *daddr,
+ __be16 dport)
+{
+ u32 secret[MD5_MESSAGE_BYTES / 4];
+ u32 hash[MD5_DIGEST_WORDS];
+ u32 i;
+
+ memcpy(hash, saddr, 16);
+ for (i = 0; i < 4; i++)
+ secret[i] = net_secret[i] + (__force u32) daddr[i];
+ secret[4] = net_secret[4] + (__force u32)dport;
+ for (i = 5; i < MD5_MESSAGE_BYTES / 4; i++)
+ secret[i] = net_secret[i];
+
+ md5_transform(hash, secret);
+
+ return hash[0];
+}
+#endif
+
+#ifdef CONFIG_INET
+__u32 secure_ip_id(__be32 daddr)
+{
+ u32 hash[MD5_DIGEST_WORDS];
+
+ hash[0] = (__force __u32) daddr;
+ hash[1] = net_secret[13];
+ hash[2] = net_secret[14];
+ hash[3] = net_secret[15];
+
+ md5_transform(hash, net_secret);
+
+ return hash[0];
+}
+
+__u32 secure_ipv6_id(const __be32 daddr[4])
+{
+ __u32 hash[4];
+
+ memcpy(hash, daddr, 16);
+ md5_transform(hash, net_secret);
+
+ return hash[0];
+}
+
+__u32 secure_tcp_sequence_number(__be32 saddr, __be32 daddr,
+ __be16 sport, __be16 dport)
+{
+ u32 hash[MD5_DIGEST_WORDS];
+
+ hash[0] = (__force u32)saddr;
+ hash[1] = (__force u32)daddr;
+ hash[2] = ((__force u16)sport << 16) + (__force u16)dport;
+ hash[3] = net_secret[15];
+
+ md5_transform(hash, net_secret);
+
+ return seq_scale(hash[0]);
+}
+
+u32 secure_ipv4_port_ephemeral(__be32 saddr, __be32 daddr, __be16 dport)
+{
+ u32 hash[MD5_DIGEST_WORDS];
+
+ hash[0] = (__force u32)saddr;
+ hash[1] = (__force u32)daddr;
+ hash[2] = (__force u32)dport ^ net_secret[14];
+ hash[3] = net_secret[15];
+
+ md5_transform(hash, net_secret);
+
+ return hash[0];
+}
+EXPORT_SYMBOL_GPL(secure_ipv4_port_ephemeral);
+#endif
+
+#if defined(CONFIG_IP_DCCP) || defined(CONFIG_IP_DCCP_MODULE)
+u64 secure_dccp_sequence_number(__be32 saddr, __be32 daddr,
+ __be16 sport, __be16 dport)
+{
+ u32 hash[MD5_DIGEST_WORDS];
+ u64 seq;
+
+ hash[0] = (__force u32)saddr;
+ hash[1] = (__force u32)daddr;
+ hash[2] = ((__force u16)sport << 16) + (__force u16)dport;
+ hash[3] = net_secret[15];
+
+ md5_transform(hash, net_secret);
+
+ seq = hash[0] | (((u64)hash[1]) << 32);
+ seq += ktime_to_ns(ktime_get_real());
+ seq &= (1ull << 48) - 1;
+
+ return seq;
+}
+EXPORT_SYMBOL(secure_dccp_sequence_number);
+
+#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
+u64 secure_dccpv6_sequence_number(__be32 *saddr, __be32 *daddr,
+ __be16 sport, __be16 dport)
+{
+ u32 secret[MD5_MESSAGE_BYTES / 4];
+ u32 hash[MD5_DIGEST_WORDS];
+ u64 seq;
+ u32 i;
+
+ memcpy(hash, saddr, 16);
+ for (i = 0; i < 4; i++)
+ secret[i] = net_secret[i] + daddr[i];
+ secret[4] = net_secret[4] +
+ (((__force u16)sport << 16) + (__force u16)dport);
+ for (i = 5; i < MD5_MESSAGE_BYTES / 4; i++)
+ secret[i] = net_secret[i];
+
+ md5_transform(hash, secret);
+
+ seq = hash[0] | (((u64)hash[1]) << 32);
+ seq += ktime_to_ns(ktime_get_real());
+ seq &= (1ull << 48) - 1;
+
+ return seq;
+}
+EXPORT_SYMBOL(secure_dccpv6_sequence_number);
+#endif
+#endif
diff --git a/net/core/skbuff.c b/net/core/skbuff.c
index 2beda82..27002df 100644
--- a/net/core/skbuff.c
+++ b/net/core/skbuff.c
@@ -1369,8 +1369,21 @@
}
EXPORT_SYMBOL(__pskb_pull_tail);
-/* Copy some data bits from skb to kernel buffer. */
-
+/**
+ * skb_copy_bits - copy bits from skb to kernel buffer
+ * @skb: source skb
+ * @offset: offset in source
+ * @to: destination buffer
+ * @len: number of bytes to copy
+ *
+ * Copy the specified number of bytes from the source skb to the
+ * destination buffer.
+ *
+ * CAUTION ! :
+ * If its prototype is ever changed,
+ * check arch/{*}/net/{*}.S files,
+ * since it is called from BPF assembly code.
+ */
int skb_copy_bits(const struct sk_buff *skb, int offset, void *to, int len)
{
int start = skb_headlen(skb);
diff --git a/net/dccp/ipv4.c b/net/dccp/ipv4.c
index 8c36adf..332639b 100644
--- a/net/dccp/ipv4.c
+++ b/net/dccp/ipv4.c
@@ -26,6 +26,7 @@
#include <net/timewait_sock.h>
#include <net/tcp_states.h>
#include <net/xfrm.h>
+#include <net/secure_seq.h>
#include "ackvec.h"
#include "ccid.h"
diff --git a/net/dccp/ipv6.c b/net/dccp/ipv6.c
index 8dc4348..b74f761 100644
--- a/net/dccp/ipv6.c
+++ b/net/dccp/ipv6.c
@@ -29,6 +29,7 @@
#include <net/transp_v6.h>
#include <net/ip6_checksum.h>
#include <net/xfrm.h>
+#include <net/secure_seq.h>
#include "dccp.h"
#include "ipv6.h"
@@ -69,13 +70,7 @@
dh->dccph_checksum = dccp_v6_csum_finish(skb, &np->saddr, &np->daddr);
}
-static inline __u32 secure_dccpv6_sequence_number(__be32 *saddr, __be32 *daddr,
- __be16 sport, __be16 dport )
-{
- return secure_tcpv6_sequence_number(saddr, daddr, sport, dport);
-}
-
-static inline __u32 dccp_v6_init_sequence(struct sk_buff *skb)
+static inline __u64 dccp_v6_init_sequence(struct sk_buff *skb)
{
return secure_dccpv6_sequence_number(ipv6_hdr(skb)->daddr.s6_addr32,
ipv6_hdr(skb)->saddr.s6_addr32,
diff --git a/net/ipv4/igmp.c b/net/ipv4/igmp.c
index f1d27f6..283c0a2 100644
--- a/net/ipv4/igmp.c
+++ b/net/ipv4/igmp.c
@@ -1718,7 +1718,7 @@
pmc->sfcount[sfmode]--;
for (j=0; j<i; j++)
- (void) ip_mc_del1_src(pmc, sfmode, &psfsrc[i]);
+ (void) ip_mc_del1_src(pmc, sfmode, &psfsrc[j]);
} else if (isexclude != (pmc->sfcount[MCAST_EXCLUDE] != 0)) {
#ifdef CONFIG_IP_MULTICAST
struct ip_sf_list *psf;
diff --git a/net/ipv4/inet_hashtables.c b/net/ipv4/inet_hashtables.c
index 3c0369a..984ec65 100644
--- a/net/ipv4/inet_hashtables.c
+++ b/net/ipv4/inet_hashtables.c
@@ -21,6 +21,7 @@
#include <net/inet_connection_sock.h>
#include <net/inet_hashtables.h>
+#include <net/secure_seq.h>
#include <net/ip.h>
/*
diff --git a/net/ipv4/inetpeer.c b/net/ipv4/inetpeer.c
index e382138..86f13c67 100644
--- a/net/ipv4/inetpeer.c
+++ b/net/ipv4/inetpeer.c
@@ -19,6 +19,7 @@
#include <linux/net.h>
#include <net/ip.h>
#include <net/inetpeer.h>
+#include <net/secure_seq.h>
/*
* Theory of operations.
diff --git a/net/ipv4/ip_output.c b/net/ipv4/ip_output.c
index ccaaa85..8c65633 100644
--- a/net/ipv4/ip_output.c
+++ b/net/ipv4/ip_output.c
@@ -122,6 +122,7 @@
newskb->pkt_type = PACKET_LOOPBACK;
newskb->ip_summed = CHECKSUM_UNNECESSARY;
WARN_ON(!skb_dst(newskb));
+ skb_dst_force(newskb);
netif_rx_ni(newskb);
return 0;
}
@@ -204,9 +205,15 @@
skb = skb2;
}
+ rcu_read_lock();
neigh = dst_get_neighbour(dst);
- if (neigh)
- return neigh_output(neigh, skb);
+ if (neigh) {
+ int res = neigh_output(neigh, skb);
+
+ rcu_read_unlock();
+ return res;
+ }
+ rcu_read_unlock();
if (net_ratelimit())
printk(KERN_DEBUG "ip_finish_output2: No header cache and no neighbour!\n");
diff --git a/net/ipv4/ip_sockglue.c b/net/ipv4/ip_sockglue.c
index ab0c9ef..8905e92 100644
--- a/net/ipv4/ip_sockglue.c
+++ b/net/ipv4/ip_sockglue.c
@@ -1067,7 +1067,7 @@
*/
static int do_ip_getsockopt(struct sock *sk, int level, int optname,
- char __user *optval, int __user *optlen)
+ char __user *optval, int __user *optlen, unsigned flags)
{
struct inet_sock *inet = inet_sk(sk);
int val;
@@ -1240,7 +1240,7 @@
msg.msg_control = optval;
msg.msg_controllen = len;
- msg.msg_flags = 0;
+ msg.msg_flags = flags;
if (inet->cmsg_flags & IP_CMSG_PKTINFO) {
struct in_pktinfo info;
@@ -1294,7 +1294,7 @@
{
int err;
- err = do_ip_getsockopt(sk, level, optname, optval, optlen);
+ err = do_ip_getsockopt(sk, level, optname, optval, optlen, 0);
#ifdef CONFIG_NETFILTER
/* we need to exclude all possible ENOPROTOOPTs except default case */
if (err == -ENOPROTOOPT && optname != IP_PKTOPTIONS &&
@@ -1327,7 +1327,8 @@
return compat_mc_getsockopt(sk, level, optname, optval, optlen,
ip_getsockopt);
- err = do_ip_getsockopt(sk, level, optname, optval, optlen);
+ err = do_ip_getsockopt(sk, level, optname, optval, optlen,
+ MSG_CMSG_COMPAT);
#ifdef CONFIG_NETFILTER
/* we need to exclude all possible ENOPROTOOPTs except default case */
diff --git a/net/ipv4/netfilter.c b/net/ipv4/netfilter.c
index 2e97e3e..929b27b 100644
--- a/net/ipv4/netfilter.c
+++ b/net/ipv4/netfilter.c
@@ -18,17 +18,15 @@
struct rtable *rt;
struct flowi4 fl4 = {};
__be32 saddr = iph->saddr;
- __u8 flags = 0;
+ __u8 flags = skb->sk ? inet_sk_flowi_flags(skb->sk) : 0;
unsigned int hh_len;
- if (!skb->sk && addr_type != RTN_LOCAL) {
- if (addr_type == RTN_UNSPEC)
- addr_type = inet_addr_type(net, saddr);
- if (addr_type == RTN_LOCAL || addr_type == RTN_UNICAST)
- flags |= FLOWI_FLAG_ANYSRC;
- else
- saddr = 0;
- }
+ if (addr_type == RTN_UNSPEC)
+ addr_type = inet_addr_type(net, saddr);
+ if (addr_type == RTN_LOCAL || addr_type == RTN_UNICAST)
+ flags |= FLOWI_FLAG_ANYSRC;
+ else
+ saddr = 0;
/* some non-standard hacks like ipt_REJECT.c:send_reset() can cause
* packets with foreign saddr to appear on the NF_INET_LOCAL_OUT hook.
@@ -38,7 +36,7 @@
fl4.flowi4_tos = RT_TOS(iph->tos);
fl4.flowi4_oif = skb->sk ? skb->sk->sk_bound_dev_if : 0;
fl4.flowi4_mark = skb->mark;
- fl4.flowi4_flags = skb->sk ? inet_sk_flowi_flags(skb->sk) : flags;
+ fl4.flowi4_flags = flags;
rt = ip_route_output_key(net, &fl4);
if (IS_ERR(rt))
return -1;
diff --git a/net/ipv4/netfilter/nf_nat_proto_common.c b/net/ipv4/netfilter/nf_nat_proto_common.c
index 3e61faf..f52d41e 100644
--- a/net/ipv4/netfilter/nf_nat_proto_common.c
+++ b/net/ipv4/netfilter/nf_nat_proto_common.c
@@ -12,6 +12,7 @@
#include <linux/ip.h>
#include <linux/netfilter.h>
+#include <net/secure_seq.h>
#include <net/netfilter/nf_nat.h>
#include <net/netfilter/nf_nat_core.h>
#include <net/netfilter/nf_nat_rule.h>
diff --git a/net/ipv4/raw.c b/net/ipv4/raw.c
index 1457acb..61714bd 100644
--- a/net/ipv4/raw.c
+++ b/net/ipv4/raw.c
@@ -563,7 +563,8 @@
flowi4_init_output(&fl4, ipc.oif, sk->sk_mark, tos,
RT_SCOPE_UNIVERSE,
inet->hdrincl ? IPPROTO_RAW : sk->sk_protocol,
- FLOWI_FLAG_CAN_SLEEP, daddr, saddr, 0, 0);
+ inet_sk_flowi_flags(sk) | FLOWI_FLAG_CAN_SLEEP,
+ daddr, saddr, 0, 0);
if (!inet->hdrincl) {
err = raw_probe_proto_opt(&fl4, msg);
diff --git a/net/ipv4/route.c b/net/ipv4/route.c
index 1730689..075212e 100644
--- a/net/ipv4/route.c
+++ b/net/ipv4/route.c
@@ -109,6 +109,7 @@
#include <linux/sysctl.h>
#endif
#include <net/atmclip.h>
+#include <net/secure_seq.h>
#define RT_FL_TOS(oldflp4) \
((u32)(oldflp4->flowi4_tos & (IPTOS_RT_MASK | RTO_ONLINK)))
@@ -721,7 +722,7 @@
{
return ((((__force u32)rt1->rt_key_dst ^ (__force u32)rt2->rt_key_dst) |
((__force u32)rt1->rt_key_src ^ (__force u32)rt2->rt_key_src) |
- (rt1->rt_iif ^ rt2->rt_iif)) == 0);
+ (rt1->rt_route_iif ^ rt2->rt_route_iif)) == 0);
}
static inline int compare_keys(struct rtable *rt1, struct rtable *rt2)
@@ -730,8 +731,8 @@
((__force u32)rt1->rt_key_src ^ (__force u32)rt2->rt_key_src) |
(rt1->rt_mark ^ rt2->rt_mark) |
(rt1->rt_key_tos ^ rt2->rt_key_tos) |
- (rt1->rt_oif ^ rt2->rt_oif) |
- (rt1->rt_iif ^ rt2->rt_iif)) == 0;
+ (rt1->rt_route_iif ^ rt2->rt_route_iif) |
+ (rt1->rt_oif ^ rt2->rt_oif)) == 0;
}
static inline int compare_netns(struct rtable *rt1, struct rtable *rt2)
@@ -1628,16 +1629,18 @@
{
struct rtable *rt = (struct rtable *) dst;
__be32 orig_gw = rt->rt_gateway;
- struct neighbour *n;
+ struct neighbour *n, *old_n;
dst_confirm(&rt->dst);
- neigh_release(dst_get_neighbour(&rt->dst));
- dst_set_neighbour(&rt->dst, NULL);
-
rt->rt_gateway = peer->redirect_learned.a4;
- rt_bind_neighbour(rt);
- n = dst_get_neighbour(&rt->dst);
+
+ n = ipv4_neigh_lookup(&rt->dst, &rt->rt_gateway);
+ if (IS_ERR(n))
+ return PTR_ERR(n);
+ old_n = xchg(&rt->dst._neighbour, n);
+ if (old_n)
+ neigh_release(old_n);
if (!n || !(n->nud_state & NUD_VALID)) {
if (n)
neigh_event_send(n, NULL);
@@ -2317,8 +2320,7 @@
rth = rcu_dereference(rth->dst.rt_next)) {
if ((((__force u32)rth->rt_key_dst ^ (__force u32)daddr) |
((__force u32)rth->rt_key_src ^ (__force u32)saddr) |
- (rth->rt_iif ^ iif) |
- rth->rt_oif |
+ (rth->rt_route_iif ^ iif) |
(rth->rt_key_tos ^ tos)) == 0 &&
rth->rt_mark == skb->mark &&
net_eq(dev_net(rth->dst.dev), net) &&
diff --git a/net/ipv4/syncookies.c b/net/ipv4/syncookies.c
index 92bb943..3bc5c8f 100644
--- a/net/ipv4/syncookies.c
+++ b/net/ipv4/syncookies.c
@@ -276,7 +276,7 @@
int mss;
struct rtable *rt;
__u8 rcv_wscale;
- bool ecn_ok;
+ bool ecn_ok = false;
if (!sysctl_tcp_syncookies || !th->ack || th->rst)
goto out;
diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c
index 955b8e6..1c12b8e 100644
--- a/net/ipv4/tcp_ipv4.c
+++ b/net/ipv4/tcp_ipv4.c
@@ -72,6 +72,7 @@
#include <net/timewait_sock.h>
#include <net/xfrm.h>
#include <net/netdma.h>
+#include <net/secure_seq.h>
#include <linux/inet.h>
#include <linux/ipv6.h>
diff --git a/net/ipv6/addrconf.c b/net/ipv6/addrconf.c
index a55500c..f012ebd 100644
--- a/net/ipv6/addrconf.c
+++ b/net/ipv6/addrconf.c
@@ -656,7 +656,7 @@
* layer address of our nexhop router
*/
- if (dst_get_neighbour(&rt->dst) == NULL)
+ if (dst_get_neighbour_raw(&rt->dst) == NULL)
ifa->flags &= ~IFA_F_OPTIMISTIC;
ifa->idev = idev;
diff --git a/net/ipv6/datagram.c b/net/ipv6/datagram.c
index 1656033..9ef1831 100644
--- a/net/ipv6/datagram.c
+++ b/net/ipv6/datagram.c
@@ -33,6 +33,11 @@
#include <linux/errqueue.h>
#include <asm/uaccess.h>
+static inline int ipv6_mapped_addr_any(const struct in6_addr *a)
+{
+ return (ipv6_addr_v4mapped(a) && (a->s6_addr32[3] == 0));
+}
+
int ip6_datagram_connect(struct sock *sk, struct sockaddr *uaddr, int addr_len)
{
struct sockaddr_in6 *usin = (struct sockaddr_in6 *) uaddr;
@@ -102,10 +107,12 @@
ipv6_addr_set_v4mapped(inet->inet_daddr, &np->daddr);
- if (ipv6_addr_any(&np->saddr))
+ if (ipv6_addr_any(&np->saddr) ||
+ ipv6_mapped_addr_any(&np->saddr))
ipv6_addr_set_v4mapped(inet->inet_saddr, &np->saddr);
- if (ipv6_addr_any(&np->rcv_saddr)) {
+ if (ipv6_addr_any(&np->rcv_saddr) ||
+ ipv6_mapped_addr_any(&np->rcv_saddr)) {
ipv6_addr_set_v4mapped(inet->inet_rcv_saddr,
&np->rcv_saddr);
if (sk->sk_prot->rehash)
diff --git a/net/ipv6/inet6_hashtables.c b/net/ipv6/inet6_hashtables.c
index b531972..73f1a00 100644
--- a/net/ipv6/inet6_hashtables.c
+++ b/net/ipv6/inet6_hashtables.c
@@ -20,6 +20,7 @@
#include <net/inet_connection_sock.h>
#include <net/inet_hashtables.h>
#include <net/inet6_hashtables.h>
+#include <net/secure_seq.h>
#include <net/ip.h>
int __inet6_hash(struct sock *sk, struct inet_timewait_sock *tw)
diff --git a/net/ipv6/ip6_fib.c b/net/ipv6/ip6_fib.c
index 54a4678..320d91d 100644
--- a/net/ipv6/ip6_fib.c
+++ b/net/ipv6/ip6_fib.c
@@ -1455,7 +1455,7 @@
RT6_TRACE("aging clone %p\n", rt);
return -1;
} else if ((rt->rt6i_flags & RTF_GATEWAY) &&
- (!(dst_get_neighbour(&rt->dst)->flags & NTF_ROUTER))) {
+ (!(dst_get_neighbour_raw(&rt->dst)->flags & NTF_ROUTER))) {
RT6_TRACE("purging route %p via non-router but gateway\n",
rt);
return -1;
diff --git a/net/ipv6/ip6_output.c b/net/ipv6/ip6_output.c
index 32e5339..4c882cf 100644
--- a/net/ipv6/ip6_output.c
+++ b/net/ipv6/ip6_output.c
@@ -135,10 +135,15 @@
skb->len);
}
+ rcu_read_lock();
neigh = dst_get_neighbour(dst);
- if (neigh)
- return neigh_output(neigh, skb);
+ if (neigh) {
+ int res = neigh_output(neigh, skb);
+ rcu_read_unlock();
+ return res;
+ }
+ rcu_read_unlock();
IP6_INC_STATS_BH(dev_net(dst->dev),
ip6_dst_idev(dst), IPSTATS_MIB_OUTNOROUTES);
kfree_skb(skb);
@@ -975,12 +980,14 @@
* dst entry and replace it instead with the
* dst entry of the nexthop router
*/
+ rcu_read_lock();
n = dst_get_neighbour(*dst);
if (n && !(n->nud_state & NUD_VALID)) {
struct inet6_ifaddr *ifp;
struct flowi6 fl_gw6;
int redirect;
+ rcu_read_unlock();
ifp = ipv6_get_ifaddr(net, &fl6->saddr,
(*dst)->dev, 1);
@@ -1000,6 +1007,8 @@
if ((err = (*dst)->error))
goto out_err_release;
}
+ } else {
+ rcu_read_unlock();
}
#endif
diff --git a/net/ipv6/route.c b/net/ipv6/route.c
index e8987da..9e69eb0 100644
--- a/net/ipv6/route.c
+++ b/net/ipv6/route.c
@@ -364,7 +364,7 @@
#ifdef CONFIG_IPV6_ROUTER_PREF
static void rt6_probe(struct rt6_info *rt)
{
- struct neighbour *neigh = rt ? dst_get_neighbour(&rt->dst) : NULL;
+ struct neighbour *neigh;
/*
* Okay, this does not seem to be appropriate
* for now, however, we need to check if it
@@ -373,8 +373,10 @@
* Router Reachability Probe MUST be rate-limited
* to no more than one per minute.
*/
+ rcu_read_lock();
+ neigh = rt ? dst_get_neighbour(&rt->dst) : NULL;
if (!neigh || (neigh->nud_state & NUD_VALID))
- return;
+ goto out;
read_lock_bh(&neigh->lock);
if (!(neigh->nud_state & NUD_VALID) &&
time_after(jiffies, neigh->updated + rt->rt6i_idev->cnf.rtr_probe_interval)) {
@@ -387,8 +389,11 @@
target = (struct in6_addr *)&neigh->primary_key;
addrconf_addr_solict_mult(target, &mcaddr);
ndisc_send_ns(rt->rt6i_dev, NULL, target, &mcaddr, NULL);
- } else
+ } else {
read_unlock_bh(&neigh->lock);
+ }
+out:
+ rcu_read_unlock();
}
#else
static inline void rt6_probe(struct rt6_info *rt)
@@ -412,8 +417,11 @@
static inline int rt6_check_neigh(struct rt6_info *rt)
{
- struct neighbour *neigh = dst_get_neighbour(&rt->dst);
+ struct neighbour *neigh;
int m;
+
+ rcu_read_lock();
+ neigh = dst_get_neighbour(&rt->dst);
if (rt->rt6i_flags & RTF_NONEXTHOP ||
!(rt->rt6i_flags & RTF_GATEWAY))
m = 1;
@@ -430,6 +438,7 @@
read_unlock_bh(&neigh->lock);
} else
m = 0;
+ rcu_read_unlock();
return m;
}
@@ -769,7 +778,7 @@
rt->rt6i_dst.plen = 128;
rt->rt6i_flags |= RTF_CACHE;
rt->dst.flags |= DST_HOST;
- dst_set_neighbour(&rt->dst, neigh_clone(dst_get_neighbour(&ort->dst)));
+ dst_set_neighbour(&rt->dst, neigh_clone(dst_get_neighbour_raw(&ort->dst)));
}
return rt;
}
@@ -803,7 +812,7 @@
dst_hold(&rt->dst);
read_unlock_bh(&table->tb6_lock);
- if (!dst_get_neighbour(&rt->dst) && !(rt->rt6i_flags & RTF_NONEXTHOP))
+ if (!dst_get_neighbour_raw(&rt->dst) && !(rt->rt6i_flags & RTF_NONEXTHOP))
nrt = rt6_alloc_cow(rt, &fl6->daddr, &fl6->saddr);
else if (!(rt->dst.flags & DST_HOST))
nrt = rt6_alloc_clone(rt, &fl6->daddr);
@@ -1587,7 +1596,7 @@
dst_confirm(&rt->dst);
/* Duplicate redirect: silently ignore. */
- if (neigh == dst_get_neighbour(&rt->dst))
+ if (neigh == dst_get_neighbour_raw(&rt->dst))
goto out;
nrt = ip6_rt_copy(rt, dest);
@@ -1682,7 +1691,7 @@
1. It is connected route. Action: COW
2. It is gatewayed route or NONEXTHOP route. Action: clone it.
*/
- if (!dst_get_neighbour(&rt->dst) && !(rt->rt6i_flags & RTF_NONEXTHOP))
+ if (!dst_get_neighbour_raw(&rt->dst) && !(rt->rt6i_flags & RTF_NONEXTHOP))
nrt = rt6_alloc_cow(rt, daddr, saddr);
else
nrt = rt6_alloc_clone(rt, daddr);
@@ -2326,6 +2335,7 @@
struct nlmsghdr *nlh;
long expires;
u32 table;
+ struct neighbour *n;
if (prefix) { /* user wants prefix routes only */
if (!(rt->rt6i_flags & RTF_PREFIX_RT)) {
@@ -2414,8 +2424,11 @@
if (rtnetlink_put_metrics(skb, dst_metrics_ptr(&rt->dst)) < 0)
goto nla_put_failure;
- if (dst_get_neighbour(&rt->dst))
- NLA_PUT(skb, RTA_GATEWAY, 16, &dst_get_neighbour(&rt->dst)->primary_key);
+ rcu_read_lock();
+ n = dst_get_neighbour(&rt->dst);
+ if (n)
+ NLA_PUT(skb, RTA_GATEWAY, 16, &n->primary_key);
+ rcu_read_unlock();
if (rt->dst.dev)
NLA_PUT_U32(skb, RTA_OIF, rt->rt6i_dev->ifindex);
@@ -2608,12 +2621,14 @@
#else
seq_puts(m, "00000000000000000000000000000000 00 ");
#endif
+ rcu_read_lock();
n = dst_get_neighbour(&rt->dst);
if (n) {
seq_printf(m, "%pi6", n->primary_key);
} else {
seq_puts(m, "00000000000000000000000000000000");
}
+ rcu_read_unlock();
seq_printf(m, " %08x %08x %08x %08x %8s\n",
rt->rt6i_metric, atomic_read(&rt->dst.__refcnt),
rt->dst.__use, rt->rt6i_flags,
diff --git a/net/ipv6/syncookies.c b/net/ipv6/syncookies.c
index 89d5bf8..ac83896 100644
--- a/net/ipv6/syncookies.c
+++ b/net/ipv6/syncookies.c
@@ -165,7 +165,7 @@
int mss;
struct dst_entry *dst;
__u8 rcv_wscale;
- bool ecn_ok;
+ bool ecn_ok = false;
if (!sysctl_tcp_syncookies || !th->ack || th->rst)
goto out;
diff --git a/net/ipv6/tcp_ipv6.c b/net/ipv6/tcp_ipv6.c
index 78aa534..d1fb63f 100644
--- a/net/ipv6/tcp_ipv6.c
+++ b/net/ipv6/tcp_ipv6.c
@@ -61,6 +61,7 @@
#include <net/timewait_sock.h>
#include <net/netdma.h>
#include <net/inet_common.h>
+#include <net/secure_seq.h>
#include <asm/uaccess.h>
diff --git a/net/netfilter/ipvs/ip_vs_ctl.c b/net/netfilter/ipvs/ip_vs_ctl.c
index be43fd8..2b771dc 100644
--- a/net/netfilter/ipvs/ip_vs_ctl.c
+++ b/net/netfilter/ipvs/ip_vs_ctl.c
@@ -3771,6 +3771,7 @@
void ip_vs_control_cleanup(void)
{
EnterFunction(2);
+ unregister_netdevice_notifier(&ip_vs_dst_notifier);
ip_vs_genl_unregister();
nf_unregister_sockopt(&ip_vs_sockopts);
LeaveFunction(2);
diff --git a/net/netfilter/nf_queue.c b/net/netfilter/nf_queue.c
index 5b466cd..84d0fd4 100644
--- a/net/netfilter/nf_queue.c
+++ b/net/netfilter/nf_queue.c
@@ -312,6 +312,7 @@
}
break;
case NF_STOLEN:
+ break;
default:
kfree_skb(skb);
}
diff --git a/net/netlabel/Makefile b/net/netlabel/Makefile
index ea750e9..d2732fc 100644
--- a/net/netlabel/Makefile
+++ b/net/netlabel/Makefile
@@ -1,8 +1,6 @@
#
# Makefile for the NetLabel subsystem.
#
-# Feb 9, 2006, Paul Moore <paul.moore@hp.com>
-#
# base objects
obj-y := netlabel_user.o netlabel_kapi.o
diff --git a/net/netlabel/netlabel_addrlist.c b/net/netlabel/netlabel_addrlist.c
index c051913..96b749d 100644
--- a/net/netlabel/netlabel_addrlist.c
+++ b/net/netlabel/netlabel_addrlist.c
@@ -6,7 +6,7 @@
* system manages static and dynamic label mappings for network protocols such
* as CIPSO and RIPSO.
*
- * Author: Paul Moore <paul.moore@hp.com>
+ * Author: Paul Moore <paul@paul-moore.com>
*
*/
diff --git a/net/netlabel/netlabel_addrlist.h b/net/netlabel/netlabel_addrlist.h
index 2b9644e..fdbc1d2 100644
--- a/net/netlabel/netlabel_addrlist.h
+++ b/net/netlabel/netlabel_addrlist.h
@@ -6,7 +6,7 @@
* system manages static and dynamic label mappings for network protocols such
* as CIPSO and RIPSO.
*
- * Author: Paul Moore <paul.moore@hp.com>
+ * Author: Paul Moore <paul@paul-moore.com>
*
*/
diff --git a/net/netlabel/netlabel_cipso_v4.c b/net/netlabel/netlabel_cipso_v4.c
index dd53a36..6bf8783 100644
--- a/net/netlabel/netlabel_cipso_v4.c
+++ b/net/netlabel/netlabel_cipso_v4.c
@@ -5,7 +5,7 @@
* NetLabel system manages static and dynamic label mappings for network
* protocols such as CIPSO and RIPSO.
*
- * Author: Paul Moore <paul.moore@hp.com>
+ * Author: Paul Moore <paul@paul-moore.com>
*
*/
diff --git a/net/netlabel/netlabel_cipso_v4.h b/net/netlabel/netlabel_cipso_v4.h
index af7f335..d24d774 100644
--- a/net/netlabel/netlabel_cipso_v4.h
+++ b/net/netlabel/netlabel_cipso_v4.h
@@ -5,7 +5,7 @@
* NetLabel system manages static and dynamic label mappings for network
* protocols such as CIPSO and RIPSO.
*
- * Author: Paul Moore <paul.moore@hp.com>
+ * Author: Paul Moore <paul@paul-moore.com>
*
*/
diff --git a/net/netlabel/netlabel_domainhash.c b/net/netlabel/netlabel_domainhash.c
index 2aa975e..7d8083c 100644
--- a/net/netlabel/netlabel_domainhash.c
+++ b/net/netlabel/netlabel_domainhash.c
@@ -6,7 +6,7 @@
* system manages static and dynamic label mappings for network protocols such
* as CIPSO and RIPSO.
*
- * Author: Paul Moore <paul.moore@hp.com>
+ * Author: Paul Moore <paul@paul-moore.com>
*
*/
diff --git a/net/netlabel/netlabel_domainhash.h b/net/netlabel/netlabel_domainhash.h
index 0261dda..bfcc0f7 100644
--- a/net/netlabel/netlabel_domainhash.h
+++ b/net/netlabel/netlabel_domainhash.h
@@ -6,7 +6,7 @@
* system manages static and dynamic label mappings for network protocols such
* as CIPSO and RIPSO.
*
- * Author: Paul Moore <paul.moore@hp.com>
+ * Author: Paul Moore <paul@paul-moore.com>
*
*/
diff --git a/net/netlabel/netlabel_kapi.c b/net/netlabel/netlabel_kapi.c
index b528dd9..9c24de1 100644
--- a/net/netlabel/netlabel_kapi.c
+++ b/net/netlabel/netlabel_kapi.c
@@ -5,7 +5,7 @@
* system manages static and dynamic label mappings for network protocols such
* as CIPSO and RIPSO.
*
- * Author: Paul Moore <paul.moore@hp.com>
+ * Author: Paul Moore <paul@paul-moore.com>
*
*/
@@ -341,11 +341,11 @@
entry = kzalloc(sizeof(*entry), GFP_ATOMIC);
if (entry == NULL)
- return -ENOMEM;
+ goto out_entry;
if (domain != NULL) {
entry->domain = kstrdup(domain, GFP_ATOMIC);
if (entry->domain == NULL)
- goto cfg_cipsov4_map_add_failure;
+ goto out_domain;
}
if (addr == NULL && mask == NULL) {
@@ -354,13 +354,13 @@
} else if (addr != NULL && mask != NULL) {
addrmap = kzalloc(sizeof(*addrmap), GFP_ATOMIC);
if (addrmap == NULL)
- goto cfg_cipsov4_map_add_failure;
+ goto out_addrmap;
INIT_LIST_HEAD(&addrmap->list4);
INIT_LIST_HEAD(&addrmap->list6);
addrinfo = kzalloc(sizeof(*addrinfo), GFP_ATOMIC);
if (addrinfo == NULL)
- goto cfg_cipsov4_map_add_failure;
+ goto out_addrinfo;
addrinfo->type_def.cipsov4 = doi_def;
addrinfo->type = NETLBL_NLTYPE_CIPSOV4;
addrinfo->list.addr = addr->s_addr & mask->s_addr;
@@ -374,7 +374,7 @@
entry->type = NETLBL_NLTYPE_ADDRSELECT;
} else {
ret_val = -EINVAL;
- goto cfg_cipsov4_map_add_failure;
+ goto out_addrmap;
}
ret_val = netlbl_domhsh_add(entry, audit_info);
@@ -384,11 +384,15 @@
return 0;
cfg_cipsov4_map_add_failure:
- cipso_v4_doi_putdef(doi_def);
- kfree(entry->domain);
- kfree(entry);
- kfree(addrmap);
kfree(addrinfo);
+out_addrinfo:
+ kfree(addrmap);
+out_addrmap:
+ kfree(entry->domain);
+out_domain:
+ kfree(entry);
+out_entry:
+ cipso_v4_doi_putdef(doi_def);
return ret_val;
}
diff --git a/net/netlabel/netlabel_mgmt.c b/net/netlabel/netlabel_mgmt.c
index dff8a08..bfa5558 100644
--- a/net/netlabel/netlabel_mgmt.c
+++ b/net/netlabel/netlabel_mgmt.c
@@ -5,7 +5,7 @@
* NetLabel system manages static and dynamic label mappings for network
* protocols such as CIPSO and RIPSO.
*
- * Author: Paul Moore <paul.moore@hp.com>
+ * Author: Paul Moore <paul@paul-moore.com>
*
*/
diff --git a/net/netlabel/netlabel_mgmt.h b/net/netlabel/netlabel_mgmt.h
index 8db37f4..5a9f31c 100644
--- a/net/netlabel/netlabel_mgmt.h
+++ b/net/netlabel/netlabel_mgmt.h
@@ -5,7 +5,7 @@
* NetLabel system manages static and dynamic label mappings for network
* protocols such as CIPSO and RIPSO.
*
- * Author: Paul Moore <paul.moore@hp.com>
+ * Author: Paul Moore <paul@paul-moore.com>
*
*/
diff --git a/net/netlabel/netlabel_unlabeled.c b/net/netlabel/netlabel_unlabeled.c
index f1ecf84..e6e8236 100644
--- a/net/netlabel/netlabel_unlabeled.c
+++ b/net/netlabel/netlabel_unlabeled.c
@@ -5,7 +5,7 @@
* NetLabel system. The NetLabel system manages static and dynamic label
* mappings for network protocols such as CIPSO and RIPSO.
*
- * Author: Paul Moore <paul.moore@hp.com>
+ * Author: Paul Moore <paul@paul-moore.com>
*
*/
diff --git a/net/netlabel/netlabel_unlabeled.h b/net/netlabel/netlabel_unlabeled.h
index 0bc8dc3..700af49 100644
--- a/net/netlabel/netlabel_unlabeled.h
+++ b/net/netlabel/netlabel_unlabeled.h
@@ -5,7 +5,7 @@
* NetLabel system. The NetLabel system manages static and dynamic label
* mappings for network protocols such as CIPSO and RIPSO.
*
- * Author: Paul Moore <paul.moore@hp.com>
+ * Author: Paul Moore <paul@paul-moore.com>
*
*/
diff --git a/net/netlabel/netlabel_user.c b/net/netlabel/netlabel_user.c
index a3fd75a..9fae63f 100644
--- a/net/netlabel/netlabel_user.c
+++ b/net/netlabel/netlabel_user.c
@@ -5,7 +5,7 @@
* NetLabel system manages static and dynamic label mappings for network
* protocols such as CIPSO and RIPSO.
*
- * Author: Paul Moore <paul.moore@hp.com>
+ * Author: Paul Moore <paul@paul-moore.com>
*
*/
diff --git a/net/netlabel/netlabel_user.h b/net/netlabel/netlabel_user.h
index f4fc4c9..8196978 100644
--- a/net/netlabel/netlabel_user.h
+++ b/net/netlabel/netlabel_user.h
@@ -5,7 +5,7 @@
* NetLabel system manages static and dynamic label mappings for network
* protocols such as CIPSO and RIPSO.
*
- * Author: Paul Moore <paul.moore@hp.com>
+ * Author: Paul Moore <paul@paul-moore.com>
*
*/
diff --git a/net/sched/sch_prio.c b/net/sched/sch_prio.c
index 2a318f2..b5d56a2 100644
--- a/net/sched/sch_prio.c
+++ b/net/sched/sch_prio.c
@@ -112,7 +112,7 @@
for (prio = 0; prio < q->bands; prio++) {
struct Qdisc *qdisc = q->queues[prio];
- struct sk_buff *skb = qdisc->dequeue(qdisc);
+ struct sk_buff *skb = qdisc_dequeue_peeked(qdisc);
if (skb) {
qdisc_bstats_update(sch, skb);
sch->q.qlen--;
diff --git a/net/sched/sch_sfq.c b/net/sched/sch_sfq.c
index 4536ee6..4f5510e 100644
--- a/net/sched/sch_sfq.c
+++ b/net/sched/sch_sfq.c
@@ -410,7 +410,12 @@
/* Return Congestion Notification only if we dropped a packet
* from this flow.
*/
- return (qlen != slot->qlen) ? NET_XMIT_CN : NET_XMIT_SUCCESS;
+ if (qlen != slot->qlen)
+ return NET_XMIT_CN;
+
+ /* As we dropped a packet, better let upper stack know this */
+ qdisc_tree_decrease_qlen(sch, 1);
+ return NET_XMIT_SUCCESS;
}
static struct sk_buff *
diff --git a/net/socket.c b/net/socket.c
index b1cbbcd..24a7740 100644
--- a/net/socket.c
+++ b/net/socket.c
@@ -1871,8 +1871,14 @@
#define COMPAT_NAMELEN(msg) COMPAT_MSG(msg, msg_namelen)
#define COMPAT_FLAGS(msg) COMPAT_MSG(msg, msg_flags)
+struct used_address {
+ struct sockaddr_storage name;
+ unsigned int name_len;
+};
+
static int __sys_sendmsg(struct socket *sock, struct msghdr __user *msg,
- struct msghdr *msg_sys, unsigned flags, int nosec)
+ struct msghdr *msg_sys, unsigned flags,
+ struct used_address *used_address)
{
struct compat_msghdr __user *msg_compat =
(struct compat_msghdr __user *)msg;
@@ -1953,8 +1959,28 @@
if (sock->file->f_flags & O_NONBLOCK)
msg_sys->msg_flags |= MSG_DONTWAIT;
- err = (nosec ? sock_sendmsg_nosec : sock_sendmsg)(sock, msg_sys,
- total_len);
+ /*
+ * If this is sendmmsg() and current destination address is same as
+ * previously succeeded address, omit asking LSM's decision.
+ * used_address->name_len is initialized to UINT_MAX so that the first
+ * destination address never matches.
+ */
+ if (used_address && used_address->name_len == msg_sys->msg_namelen &&
+ !memcmp(&used_address->name, msg->msg_name,
+ used_address->name_len)) {
+ err = sock_sendmsg_nosec(sock, msg_sys, total_len);
+ goto out_freectl;
+ }
+ err = sock_sendmsg(sock, msg_sys, total_len);
+ /*
+ * If this is sendmmsg() and sending to current destination address was
+ * successful, remember it.
+ */
+ if (used_address && err >= 0) {
+ used_address->name_len = msg_sys->msg_namelen;
+ memcpy(&used_address->name, msg->msg_name,
+ used_address->name_len);
+ }
out_freectl:
if (ctl_buf != ctl)
@@ -1979,7 +2005,7 @@
if (!sock)
goto out;
- err = __sys_sendmsg(sock, msg, &msg_sys, flags, 0);
+ err = __sys_sendmsg(sock, msg, &msg_sys, flags, NULL);
fput_light(sock->file, fput_needed);
out:
@@ -1998,6 +2024,10 @@
struct mmsghdr __user *entry;
struct compat_mmsghdr __user *compat_entry;
struct msghdr msg_sys;
+ struct used_address used_address;
+
+ if (vlen > UIO_MAXIOV)
+ vlen = UIO_MAXIOV;
datagrams = 0;
@@ -2005,27 +2035,22 @@
if (!sock)
return err;
- err = sock_error(sock->sk);
- if (err)
- goto out_put;
-
+ used_address.name_len = UINT_MAX;
entry = mmsg;
compat_entry = (struct compat_mmsghdr __user *)mmsg;
+ err = 0;
while (datagrams < vlen) {
- /*
- * No need to ask LSM for more than the first datagram.
- */
if (MSG_CMSG_COMPAT & flags) {
err = __sys_sendmsg(sock, (struct msghdr __user *)compat_entry,
- &msg_sys, flags, datagrams);
+ &msg_sys, flags, &used_address);
if (err < 0)
break;
err = __put_user(err, &compat_entry->msg_len);
++compat_entry;
} else {
err = __sys_sendmsg(sock, (struct msghdr __user *)entry,
- &msg_sys, flags, datagrams);
+ &msg_sys, flags, &used_address);
if (err < 0)
break;
err = put_user(err, &entry->msg_len);
@@ -2037,30 +2062,12 @@
++datagrams;
}
-out_put:
fput_light(sock->file, fput_needed);
- if (err == 0)
+ /* We only return an error if no datagrams were able to be sent */
+ if (datagrams != 0)
return datagrams;
- if (datagrams != 0) {
- /*
- * We may send less entries than requested (vlen) if the
- * sock is non blocking...
- */
- if (err != -EAGAIN) {
- /*
- * ... or if sendmsg returns an error after we
- * send some datagrams, where we record the
- * error to return on the next call or if the
- * app asks about it using getsockopt(SO_ERROR).
- */
- sock->sk->sk_err = -err;
- }
-
- return datagrams;
- }
-
return err;
}
diff --git a/net/sunrpc/xprt.c b/net/sunrpc/xprt.c
index 9b6a4d1..f4385e4 100644
--- a/net/sunrpc/xprt.c
+++ b/net/sunrpc/xprt.c
@@ -187,6 +187,7 @@
/**
* xprt_reserve_xprt - serialize write access to transports
* @task: task that is requesting access to the transport
+ * @xprt: pointer to the target transport
*
* This prevents mixing the payload of separate requests, and prevents
* transport connects from colliding with writes. No congestion control
diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c
index 28d2aa1..e83e7fe 100644
--- a/net/wireless/nl80211.c
+++ b/net/wireless/nl80211.c
@@ -3464,7 +3464,7 @@
tmp) {
enum ieee80211_band band = nla_type(attr);
- if (band < 0 || band > IEEE80211_NUM_BANDS) {
+ if (band < 0 || band >= IEEE80211_NUM_BANDS) {
err = -EINVAL;
goto out_free;
}
diff --git a/net/xfrm/xfrm_algo.c b/net/xfrm/xfrm_algo.c
index 58064d9..791ab2e 100644
--- a/net/xfrm/xfrm_algo.c
+++ b/net/xfrm/xfrm_algo.c
@@ -462,8 +462,8 @@
.desc = {
.sadb_alg_id = SADB_X_EALG_AESCTR,
.sadb_alg_ivlen = 8,
- .sadb_alg_minbits = 128,
- .sadb_alg_maxbits = 256
+ .sadb_alg_minbits = 160,
+ .sadb_alg_maxbits = 288
}
},
};
diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c
index a38316b..266a229 100644
--- a/security/selinux/hooks.c
+++ b/security/selinux/hooks.c
@@ -14,7 +14,7 @@
* Copyright (C) 2004-2005 Trusted Computer Solutions, Inc.
* <dgoeddel@trustedcs.com>
* Copyright (C) 2006, 2007, 2009 Hewlett-Packard Development Company, L.P.
- * Paul Moore <paul.moore@hp.com>
+ * Paul Moore <paul@paul-moore.com>
* Copyright (C) 2007 Hitachi Software Engineering Co., Ltd.
* Yuichi Nakamura <ynakam@hitachisoft.jp>
*
diff --git a/security/selinux/include/netif.h b/security/selinux/include/netif.h
index ce23edd..43d5072 100644
--- a/security/selinux/include/netif.h
+++ b/security/selinux/include/netif.h
@@ -8,7 +8,7 @@
*
* Copyright (C) 2003 Red Hat, Inc., James Morris <jmorris@redhat.com>
* Copyright (C) 2007 Hewlett-Packard Development Company, L.P.
- * Paul Moore, <paul.moore@hp.com>
+ * Paul Moore <paul@paul-moore.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,
diff --git a/security/selinux/include/netlabel.h b/security/selinux/include/netlabel.h
index cf2f628..8c59b8f 100644
--- a/security/selinux/include/netlabel.h
+++ b/security/selinux/include/netlabel.h
@@ -1,7 +1,7 @@
/*
* SELinux interface to the NetLabel subsystem
*
- * Author : Paul Moore <paul.moore@hp.com>
+ * Author: Paul Moore <paul@paul-moore.com>
*
*/
diff --git a/security/selinux/include/netnode.h b/security/selinux/include/netnode.h
index 1b94450..df7a5ed 100644
--- a/security/selinux/include/netnode.h
+++ b/security/selinux/include/netnode.h
@@ -6,7 +6,7 @@
* needed to reduce the lookup overhead since most of these queries happen on
* a per-packet basis.
*
- * Author: Paul Moore <paul.moore@hp.com>
+ * Author: Paul Moore <paul@paul-moore.com>
*
*/
diff --git a/security/selinux/include/netport.h b/security/selinux/include/netport.h
index 8991752..4d965b8 100644
--- a/security/selinux/include/netport.h
+++ b/security/selinux/include/netport.h
@@ -5,7 +5,7 @@
* mapping is maintained as part of the normal policy but a fast cache is
* needed to reduce the lookup overhead.
*
- * Author: Paul Moore <paul.moore@hp.com>
+ * Author: Paul Moore <paul@paul-moore.com>
*
*/
diff --git a/security/selinux/netif.c b/security/selinux/netif.c
index 58cc481..326f22c 100644
--- a/security/selinux/netif.c
+++ b/security/selinux/netif.c
@@ -8,7 +8,7 @@
*
* Copyright (C) 2003 Red Hat, Inc., James Morris <jmorris@redhat.com>
* Copyright (C) 2007 Hewlett-Packard Development Company, L.P.
- * Paul Moore <paul.moore@hp.com>
+ * Paul Moore <paul@paul-moore.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,
diff --git a/security/selinux/netlabel.c b/security/selinux/netlabel.c
index c3bf3ed..da4b8b2 100644
--- a/security/selinux/netlabel.c
+++ b/security/selinux/netlabel.c
@@ -4,7 +4,7 @@
* This file provides the necessary glue to tie NetLabel into the SELinux
* subsystem.
*
- * Author: Paul Moore <paul.moore@hp.com>
+ * Author: Paul Moore <paul@paul-moore.com>
*
*/
diff --git a/security/selinux/netnode.c b/security/selinux/netnode.c
index 8b691a8..3bf46ab 100644
--- a/security/selinux/netnode.c
+++ b/security/selinux/netnode.c
@@ -6,7 +6,7 @@
* needed to reduce the lookup overhead since most of these queries happen on
* a per-packet basis.
*
- * Author: Paul Moore <paul.moore@hp.com>
+ * Author: Paul Moore <paul@paul-moore.com>
*
* This code is heavily based on the "netif" concept originally developed by
* James Morris <jmorris@redhat.com>
diff --git a/security/selinux/netport.c b/security/selinux/netport.c
index ae76e29..0b62bd1 100644
--- a/security/selinux/netport.c
+++ b/security/selinux/netport.c
@@ -5,7 +5,7 @@
* mapping is maintained as part of the normal policy but a fast cache is
* needed to reduce the lookup overhead.
*
- * Author: Paul Moore <paul.moore@hp.com>
+ * Author: Paul Moore <paul@paul-moore.com>
*
* This code is heavily based on the "netif" concept originally developed by
* James Morris <jmorris@redhat.com>
diff --git a/security/selinux/selinuxfs.c b/security/selinux/selinuxfs.c
index de7900e..55d92cb 100644
--- a/security/selinux/selinuxfs.c
+++ b/security/selinux/selinuxfs.c
@@ -2,7 +2,7 @@
*
* Added conditional policy language extensions
*
- * Updated: Hewlett-Packard <paul.moore@hp.com>
+ * Updated: Hewlett-Packard <paul@paul-moore.com>
*
* Added support for the policy capability bitmap
*
diff --git a/security/selinux/ss/ebitmap.c b/security/selinux/ss/ebitmap.c
index d42951f..30f119b 100644
--- a/security/selinux/ss/ebitmap.c
+++ b/security/selinux/ss/ebitmap.c
@@ -4,7 +4,7 @@
* Author : Stephen Smalley, <sds@epoch.ncsc.mil>
*/
/*
- * Updated: Hewlett-Packard <paul.moore@hp.com>
+ * Updated: Hewlett-Packard <paul@paul-moore.com>
*
* Added support to import/export the NetLabel category bitmap
*
diff --git a/security/selinux/ss/mls.c b/security/selinux/ss/mls.c
index e961742..fbf9c58 100644
--- a/security/selinux/ss/mls.c
+++ b/security/selinux/ss/mls.c
@@ -11,7 +11,7 @@
* Copyright (C) 2004-2006 Trusted Computer Solutions, Inc.
*/
/*
- * Updated: Hewlett-Packard <paul.moore@hp.com>
+ * Updated: Hewlett-Packard <paul@paul-moore.com>
*
* Added support to import/export the MLS label from NetLabel
*
diff --git a/security/selinux/ss/mls.h b/security/selinux/ss/mls.h
index 037bf9d..e4369e3 100644
--- a/security/selinux/ss/mls.h
+++ b/security/selinux/ss/mls.h
@@ -11,7 +11,7 @@
* Copyright (C) 2004-2006 Trusted Computer Solutions, Inc.
*/
/*
- * Updated: Hewlett-Packard <paul.moore@hp.com>
+ * Updated: Hewlett-Packard <paul@paul-moore.com>
*
* Added support to import/export the MLS label from NetLabel
*
diff --git a/security/selinux/ss/policydb.c b/security/selinux/ss/policydb.c
index d246aca..2381d0d 100644
--- a/security/selinux/ss/policydb.c
+++ b/security/selinux/ss/policydb.c
@@ -13,7 +13,7 @@
*
* Added conditional policy language extensions
*
- * Updated: Hewlett-Packard <paul.moore@hp.com>
+ * Updated: Hewlett-Packard <paul@paul-moore.com>
*
* Added support for the policy capability bitmap
*
diff --git a/security/selinux/ss/services.c b/security/selinux/ss/services.c
index 973e00e..f6917bc 100644
--- a/security/selinux/ss/services.c
+++ b/security/selinux/ss/services.c
@@ -13,7 +13,7 @@
*
* Added conditional policy language extensions
*
- * Updated: Hewlett-Packard <paul.moore@hp.com>
+ * Updated: Hewlett-Packard <paul@paul-moore.com>
*
* Added support for NetLabel
* Added support for the policy capability bitmap
diff --git a/security/smack/smack_lsm.c b/security/smack/smack_lsm.c
index f375eb2..b9c5e14 100644
--- a/security/smack/smack_lsm.c
+++ b/security/smack/smack_lsm.c
@@ -9,7 +9,7 @@
*
* Copyright (C) 2007 Casey Schaufler <casey@schaufler-ca.com>
* Copyright (C) 2009 Hewlett-Packard Development Company, L.P.
- * Paul Moore <paul.moore@hp.com>
+ * Paul Moore <paul@paul-moore.com>
* Copyright (C) 2010 Nokia Corporation
*
* This program is free software; you can redistribute it and/or modify
diff --git a/security/tomoyo/common.c b/security/tomoyo/common.c
index c8439cf2..2e43aec 100644
--- a/security/tomoyo/common.c
+++ b/security/tomoyo/common.c
@@ -710,8 +710,10 @@
head->r.index++)
if (ns->profile_ptr[head->r.index])
break;
- if (head->r.index == TOMOYO_MAX_PROFILES)
+ if (head->r.index == TOMOYO_MAX_PROFILES) {
+ head->r.eof = true;
return;
+ }
head->r.step++;
break;
case 2:
@@ -723,6 +725,7 @@
tomoyo_io_printf(head, "%u-COMMENT=", index);
tomoyo_set_string(head, comment ? comment->name : "");
tomoyo_set_lf(head);
+ tomoyo_print_namespace(head);
tomoyo_io_printf(head, "%u-PREFERENCE={ ", index);
for (i = 0; i < TOMOYO_MAX_PREF; i++)
tomoyo_io_printf(head, "%s=%u ",
diff --git a/sound/core/pcm_compat.c b/sound/core/pcm_compat.c
index 5fb2e28..91cdf94 100644
--- a/sound/core/pcm_compat.c
+++ b/sound/core/pcm_compat.c
@@ -342,7 +342,7 @@
kfree(bufs);
return -EFAULT;
}
- bufs[ch] = compat_ptr(ptr);
+ bufs[i] = compat_ptr(ptr);
bufptr++;
}
if (dir == SNDRV_PCM_STREAM_PLAYBACK)
diff --git a/sound/core/rtctimer.c b/sound/core/rtctimer.c
index 0851cd1..e85e72b 100644
--- a/sound/core/rtctimer.c
+++ b/sound/core/rtctimer.c
@@ -22,7 +22,7 @@
#include <linux/init.h>
#include <linux/interrupt.h>
-#include <linux/moduleparam.h>
+#include <linux/module.h>
#include <linux/log2.h>
#include <sound/core.h>
#include <sound/timer.h>
diff --git a/sound/core/timer.c b/sound/core/timer.c
index 7c1cbf0..67ebf1c 100644
--- a/sound/core/timer.c
+++ b/sound/core/timer.c
@@ -328,6 +328,8 @@
mutex_unlock(®ister_mutex);
} else {
timer = timeri->timer;
+ if (snd_BUG_ON(!timer))
+ goto out;
/* wait, until the active callback is finished */
spin_lock_irq(&timer->lock);
while (timeri->flags & SNDRV_TIMER_IFLG_CALLBACK) {
@@ -353,6 +355,7 @@
}
mutex_unlock(®ister_mutex);
}
+ out:
if (timeri->private_free)
timeri->private_free(timeri);
kfree(timeri->owner);
@@ -531,6 +534,8 @@
if (err < 0)
return err;
timer = timeri->timer;
+ if (!timer)
+ return -EINVAL;
spin_lock_irqsave(&timer->lock, flags);
timeri->cticks = timeri->ticks;
timeri->pticks = 0;
diff --git a/sound/oss/pas2_pcm.c b/sound/oss/pas2_pcm.c
index 8f7d175..6f13ab4 100644
--- a/sound/oss/pas2_pcm.c
+++ b/sound/oss/pas2_pcm.c
@@ -63,13 +63,13 @@
if (pcm_channels & 2)
{
- foo = ((CLOCK_TICK_RATE / 2) + (arg / 2)) / arg;
- arg = ((CLOCK_TICK_RATE / 2) + (foo / 2)) / foo;
+ foo = ((PIT_TICK_RATE / 2) + (arg / 2)) / arg;
+ arg = ((PIT_TICK_RATE / 2) + (foo / 2)) / foo;
}
else
{
- foo = (CLOCK_TICK_RATE + (arg / 2)) / arg;
- arg = (CLOCK_TICK_RATE + (foo / 2)) / foo;
+ foo = (PIT_TICK_RATE + (arg / 2)) / arg;
+ arg = (PIT_TICK_RATE + (foo / 2)) / foo;
}
pcm_speed = arg;
diff --git a/sound/oss/pss.c b/sound/oss/pss.c
index 9b800ce..2fc0624 100644
--- a/sound/oss/pss.c
+++ b/sound/oss/pss.c
@@ -673,7 +673,8 @@
if (pss_cdrom_port == -1) { /* If cdrom port enablation wasn't requested */
printk(KERN_INFO "PSS: CDROM port not enabled.\n");
- } else if (check_region(pss_cdrom_port, 2)) {
+ } else if (!request_region(pss_cdrom_port, 2, "PSS CDROM")) {
+ pss_cdrom_port = -1;
printk(KERN_ERR "PSS: CDROM I/O port conflict.\n");
} else {
set_io_base(devc, CONF_CDROM, pss_cdrom_port);
@@ -1232,7 +1233,8 @@
if(pssmpu)
unload_pss_mpu(&cfg_mpu);
unload_pss(&cfg);
- }
+ } else if (pss_cdrom_port != -1)
+ release_region(pss_cdrom_port, 2);
if(!pss_keep_settings) /* Keep hardware settings if asked */
{
diff --git a/sound/pci/Kconfig b/sound/pci/Kconfig
index 50abf5b..8816804 100644
--- a/sound/pci/Kconfig
+++ b/sound/pci/Kconfig
@@ -1,5 +1,10 @@
# ALSA PCI drivers
+config SND_TEA575X
+ tristate
+ depends on SND_FM801_TEA575X_BOOL || SND_ES1968_RADIO || RADIO_SF16FMR2
+ default SND_FM801 || SND_ES1968 || RADIO_SF16FMR2
+
menuconfig SND_PCI
bool "PCI sound devices"
depends on PCI
@@ -563,11 +568,6 @@
FM801 chip with a TEA5757 tuner (MediaForte SF256-PCS, SF256-PCP and
SF64-PCR) into the snd-fm801 driver.
-config SND_TEA575X
- tristate
- depends on SND_FM801_TEA575X_BOOL || SND_ES1968_RADIO || RADIO_SF16FMR2
- default SND_FM801 || SND_ES1968 || RADIO_SF16FMR2
-
source "sound/pci/hda/Kconfig"
config SND_HDSP
diff --git a/sound/pci/asihpi/hpicmn.c b/sound/pci/asihpi/hpicmn.c
index 65b7ca1..bd47521 100644
--- a/sound/pci/asihpi/hpicmn.c
+++ b/sound/pci/asihpi/hpicmn.c
@@ -631,13 +631,12 @@
if (!p_cache)
return NULL;
- p_cache->p_info =
- kmalloc(sizeof(*p_cache->p_info) * control_count, GFP_KERNEL);
+ p_cache->p_info = kzalloc(sizeof(*p_cache->p_info) * control_count,
+ GFP_KERNEL);
if (!p_cache->p_info) {
kfree(p_cache);
return NULL;
}
- memset(p_cache->p_info, 0, sizeof(*p_cache->p_info) * control_count);
p_cache->cache_size_in_bytes = size_in_bytes;
p_cache->control_count = control_count;
p_cache->p_cache = p_dsp_control_buffer;
diff --git a/sound/pci/asihpi/hpidspcd.c b/sound/pci/asihpi/hpidspcd.c
index 3a7afa3..71d32c8 100644
--- a/sound/pci/asihpi/hpidspcd.c
+++ b/sound/pci/asihpi/hpidspcd.c
@@ -43,6 +43,7 @@
struct pci_dev *dev = os_data;
struct code_header header;
char fw_name[20];
+ short err_ret = HPI_ERROR_DSP_FILE_NOT_FOUND;
int err;
sprintf(fw_name, "asihpi/dsp%04x.bin", adapter);
@@ -85,8 +86,10 @@
HPI_DEBUG_LOG(DEBUG, "dsp code %s opened\n", fw_name);
dsp_code->pvt = kmalloc(sizeof(*dsp_code->pvt), GFP_KERNEL);
- if (!dsp_code->pvt)
- return HPI_ERROR_MEMORY_ALLOC;
+ if (!dsp_code->pvt) {
+ err_ret = HPI_ERROR_MEMORY_ALLOC;
+ goto error2;
+ }
dsp_code->pvt->dev = dev;
dsp_code->pvt->firmware = firmware;
@@ -99,7 +102,7 @@
release_firmware(firmware);
error1:
dsp_code->block_length = 0;
- return HPI_ERROR_DSP_FILE_NOT_FOUND;
+ return err_ret;
}
/*-------------------------------------------------------------------*/
diff --git a/sound/pci/asihpi/hpioctl.c b/sound/pci/asihpi/hpioctl.c
index 9683f84..a32502e 100644
--- a/sound/pci/asihpi/hpioctl.c
+++ b/sound/pci/asihpi/hpioctl.c
@@ -177,16 +177,21 @@
} else {
u16 __user *ptr = NULL;
u32 size = 0;
-
+ u32 adapter_present;
/* -1=no data 0=read from user mem, 1=write to user mem */
int wrflag = -1;
- u32 adapter = hm->h.adapter_index;
- struct hpi_adapter *pa = &adapters[adapter];
+ struct hpi_adapter *pa;
- if ((adapter >= HPI_MAX_ADAPTERS) || (!pa->type)) {
- hpi_init_response(&hr->r0, HPI_OBJ_ADAPTER,
- HPI_ADAPTER_OPEN,
- HPI_ERROR_BAD_ADAPTER_NUMBER);
+ if (hm->h.adapter_index < HPI_MAX_ADAPTERS) {
+ pa = &adapters[hm->h.adapter_index];
+ adapter_present = pa->type;
+ } else {
+ adapter_present = 0;
+ }
+
+ if (!adapter_present) {
+ hpi_init_response(&hr->r0, hm->h.object,
+ hm->h.function, HPI_ERROR_BAD_ADAPTER_NUMBER);
uncopied_bytes =
copy_to_user(puhr, hr, sizeof(hr->h));
diff --git a/sound/pci/azt3328.c b/sound/pci/azt3328.c
index e4d76a2..579fc0d 100644
--- a/sound/pci/azt3328.c
+++ b/sound/pci/azt3328.c
@@ -2625,16 +2625,19 @@
int err;
snd_azf3328_dbgcallenter();
- if (dev >= SNDRV_CARDS)
- return -ENODEV;
+ if (dev >= SNDRV_CARDS) {
+ err = -ENODEV;
+ goto out;
+ }
if (!enable[dev]) {
dev++;
- return -ENOENT;
+ err = -ENOENT;
+ goto out;
}
err = snd_card_create(index[dev], id[dev], THIS_MODULE, 0, &card);
if (err < 0)
- return err;
+ goto out;
strcpy(card->driver, "AZF3328");
strcpy(card->shortname, "Aztech AZF3328 (PCI168)");
diff --git a/sound/pci/hda/alc269_quirks.c b/sound/pci/hda/alc269_quirks.c
index 14fdcf2..5ac0e21 100644
--- a/sound/pci/hda/alc269_quirks.c
+++ b/sound/pci/hda/alc269_quirks.c
@@ -531,17 +531,10 @@
SND_PCI_QUIRK(0x1043, 0x1653, "ASUS U50", ALC269_AMIC),
SND_PCI_QUIRK(0x1043, 0x1693, "ASUS F50N", ALC269_AMIC),
SND_PCI_QUIRK(0x1043, 0x16a3, "ASUS F5Q", ALC269_AMIC),
- SND_PCI_QUIRK(0x1043, 0x16e3, "ASUS UX50", ALC269_DMIC),
SND_PCI_QUIRK(0x1043, 0x1723, "ASUS P80", ALC269_AMIC),
SND_PCI_QUIRK(0x1043, 0x1743, "ASUS U80", ALC269_AMIC),
SND_PCI_QUIRK(0x1043, 0x1773, "ASUS U20A", ALC269_AMIC),
SND_PCI_QUIRK(0x1043, 0x1883, "ASUS F81Se", ALC269_AMIC),
- SND_PCI_QUIRK(0x1043, 0x831a, "ASUS Eeepc P901",
- ALC269_DMIC),
- SND_PCI_QUIRK(0x1043, 0x834a, "ASUS Eeepc S101",
- ALC269_DMIC),
- SND_PCI_QUIRK(0x1043, 0x8398, "ASUS P1005HA", ALC269_DMIC),
- SND_PCI_QUIRK(0x1043, 0x83ce, "ASUS P1005HA", ALC269_DMIC),
SND_PCI_QUIRK(0x104d, 0x9071, "Sony VAIO", ALC269_AUTO),
SND_PCI_QUIRK(0x10cf, 0x1475, "Lifebook ICH9M-based", ALC269_LIFEBOOK),
SND_PCI_QUIRK(0x152d, 0x1778, "Quanta ON1", ALC269_DMIC),
diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c
index e125c60..9a1aa09 100644
--- a/sound/pci/hda/patch_realtek.c
+++ b/sound/pci/hda/patch_realtek.c
@@ -4484,6 +4484,22 @@
spec->stream_analog_capture = &alc269_44k_pcm_analog_capture;
}
+static void alc269_fixup_stereo_dmic(struct hda_codec *codec,
+ const struct alc_fixup *fix, int action)
+{
+ int coef;
+
+ if (action != ALC_FIXUP_ACT_INIT)
+ return;
+ /* The digital-mic unit sends PDM (differential signal) instead of
+ * the standard PCM, thus you can't record a valid mono stream as is.
+ * Below is a workaround specific to ALC269 to control the dmic
+ * signal source as mono.
+ */
+ coef = alc_read_coef_idx(codec, 0x07);
+ alc_write_coef_idx(codec, 0x07, coef | 0x80);
+}
+
enum {
ALC269_FIXUP_SONY_VAIO,
ALC275_FIXUP_SONY_VAIO_GPIO2,
@@ -4494,6 +4510,7 @@
ALC275_FIXUP_SONY_HWEQ,
ALC271_FIXUP_DMIC,
ALC269_FIXUP_PCM_44K,
+ ALC269_FIXUP_STEREO_DMIC,
};
static const struct alc_fixup alc269_fixups[] = {
@@ -4556,10 +4573,19 @@
.type = ALC_FIXUP_FUNC,
.v.func = alc269_fixup_pcm_44k,
},
+ [ALC269_FIXUP_STEREO_DMIC] = {
+ .type = ALC_FIXUP_FUNC,
+ .v.func = alc269_fixup_stereo_dmic,
+ },
};
static const struct snd_pci_quirk alc269_fixup_tbl[] = {
SND_PCI_QUIRK(0x1043, 0x1a13, "Asus G73Jw", ALC269_FIXUP_ASUS_G73JW),
+ SND_PCI_QUIRK(0x1043, 0x16e3, "ASUS UX50", ALC269_FIXUP_STEREO_DMIC),
+ SND_PCI_QUIRK(0x1043, 0x831a, "ASUS P901", ALC269_FIXUP_STEREO_DMIC),
+ SND_PCI_QUIRK(0x1043, 0x834a, "ASUS S101", ALC269_FIXUP_STEREO_DMIC),
+ SND_PCI_QUIRK(0x1043, 0x8398, "ASUS P1005", ALC269_FIXUP_STEREO_DMIC),
+ SND_PCI_QUIRK(0x1043, 0x83ce, "ASUS P1005", ALC269_FIXUP_STEREO_DMIC),
SND_PCI_QUIRK(0x104d, 0x9073, "Sony VAIO", ALC275_FIXUP_SONY_VAIO_GPIO2),
SND_PCI_QUIRK(0x104d, 0x907b, "Sony VAIO", ALC275_FIXUP_SONY_HWEQ),
SND_PCI_QUIRK(0x104d, 0x9084, "Sony VAIO", ALC275_FIXUP_SONY_HWEQ),
diff --git a/sound/pci/hda/patch_sigmatel.c b/sound/pci/hda/patch_sigmatel.c
index aa376b5..5145b66 100644
--- a/sound/pci/hda/patch_sigmatel.c
+++ b/sound/pci/hda/patch_sigmatel.c
@@ -673,6 +673,7 @@
return 0;
}
+#ifdef CONFIG_SND_HDA_POWER_SAVE
static int stac_vrefout_set(struct hda_codec *codec,
hda_nid_t nid, unsigned int new_vref)
{
@@ -696,6 +697,7 @@
return 1;
}
+#endif
static unsigned int stac92xx_vref_set(struct hda_codec *codec,
hda_nid_t nid, unsigned int new_vref)
diff --git a/sound/pci/hda/patch_via.c b/sound/pci/hda/patch_via.c
index 84d8798..4ebfbd8 100644
--- a/sound/pci/hda/patch_via.c
+++ b/sound/pci/hda/patch_via.c
@@ -2084,7 +2084,7 @@
struct via_spec *spec = codec->spec;
struct nid_path *path;
bool check_dac;
- hda_nid_t pin, dac;
+ hda_nid_t pin, dac = 0;
int err;
pin = spec->autocfg.speaker_pins[0];
diff --git a/sound/pci/rme9652/hdspm.c b/sound/pci/rme9652/hdspm.c
index af130ee..493e394 100644
--- a/sound/pci/rme9652/hdspm.c
+++ b/sound/pci/rme9652/hdspm.c
@@ -521,6 +521,7 @@
#define HDSPM_DMA_AREA_KILOBYTES (HDSPM_DMA_AREA_BYTES/1024)
/* revisions >= 230 indicate AES32 card */
+#define HDSPM_MADI_ANCIENT_REV 204
#define HDSPM_MADI_OLD_REV 207
#define HDSPM_MADI_REV 210
#define HDSPM_RAYDAT_REV 211
@@ -1217,6 +1218,22 @@
rate = 0;
break;
}
+
+ /* QS and DS rates normally can not be detected
+ * automatically by the card. Only exception is MADI
+ * in 96k frame mode.
+ *
+ * So if we read SS values (32 .. 48k), check for
+ * user-provided DS/QS bits in the control register
+ * and multiply the base frequency accordingly.
+ */
+ if (rate <= 48000) {
+ if (hdspm->control_register & HDSPM_QuadSpeed)
+ rate *= 4;
+ else if (hdspm->control_register &
+ HDSPM_DoubleSpeed)
+ rate *= 2;
+ }
}
break;
}
@@ -1322,6 +1339,10 @@
break;
case MADIface:
freq_const = 131072000000000ULL;
+ break;
+ default:
+ snd_BUG();
+ return 0;
}
return div_u64(freq_const, period);
@@ -1339,16 +1360,19 @@
switch (hdspm->io_type) {
case MADIface:
- n = 131072000000000ULL; /* 125 MHz */
- break;
+ n = 131072000000000ULL; /* 125 MHz */
+ break;
case MADI:
case AES32:
- n = 110069313433624ULL; /* 105 MHz */
- break;
+ n = 110069313433624ULL; /* 105 MHz */
+ break;
case RayDAT:
case AIO:
- n = 104857600000000ULL; /* 100 MHz */
- break;
+ n = 104857600000000ULL; /* 100 MHz */
+ break;
+ default:
+ snd_BUG();
+ return;
}
n = div_u64(n, rate);
@@ -3415,6 +3439,91 @@
return change;
}
+#define HDSPM_MADI_SPEEDMODE(xname, xindex) \
+{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
+ .name = xname, \
+ .index = xindex, \
+ .info = snd_hdspm_info_madi_speedmode, \
+ .get = snd_hdspm_get_madi_speedmode, \
+ .put = snd_hdspm_put_madi_speedmode \
+}
+
+static int hdspm_madi_speedmode(struct hdspm *hdspm)
+{
+ if (hdspm->control_register & HDSPM_QuadSpeed)
+ return 2;
+ if (hdspm->control_register & HDSPM_DoubleSpeed)
+ return 1;
+ return 0;
+}
+
+static int hdspm_set_madi_speedmode(struct hdspm *hdspm, int mode)
+{
+ hdspm->control_register &= ~(HDSPM_DoubleSpeed | HDSPM_QuadSpeed);
+ switch (mode) {
+ case 0:
+ break;
+ case 1:
+ hdspm->control_register |= HDSPM_DoubleSpeed;
+ break;
+ case 2:
+ hdspm->control_register |= HDSPM_QuadSpeed;
+ break;
+ }
+ hdspm_write(hdspm, HDSPM_controlRegister, hdspm->control_register);
+
+ return 0;
+}
+
+static int snd_hdspm_info_madi_speedmode(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ static char *texts[] = { "Single", "Double", "Quad" };
+
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+ uinfo->count = 1;
+ uinfo->value.enumerated.items = 3;
+
+ if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items)
+ uinfo->value.enumerated.item =
+ uinfo->value.enumerated.items - 1;
+ strcpy(uinfo->value.enumerated.name,
+ texts[uinfo->value.enumerated.item]);
+
+ return 0;
+}
+
+static int snd_hdspm_get_madi_speedmode(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct hdspm *hdspm = snd_kcontrol_chip(kcontrol);
+
+ spin_lock_irq(&hdspm->lock);
+ ucontrol->value.enumerated.item[0] = hdspm_madi_speedmode(hdspm);
+ spin_unlock_irq(&hdspm->lock);
+ return 0;
+}
+
+static int snd_hdspm_put_madi_speedmode(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct hdspm *hdspm = snd_kcontrol_chip(kcontrol);
+ int change;
+ int val;
+
+ if (!snd_hdspm_use_is_exclusive(hdspm))
+ return -EBUSY;
+ val = ucontrol->value.integer.value[0];
+ if (val < 0)
+ val = 0;
+ if (val > 2)
+ val = 2;
+ spin_lock_irq(&hdspm->lock);
+ change = val != hdspm_madi_speedmode(hdspm);
+ hdspm_set_madi_speedmode(hdspm, val);
+ spin_unlock_irq(&hdspm->lock);
+ return change;
+}
#define HDSPM_MIXER(xname, xindex) \
{ .iface = SNDRV_CTL_ELEM_IFACE_HWDEP, \
@@ -4289,7 +4398,8 @@
HDSPM_TX_64("TX 64 channels mode", 0),
HDSPM_C_TMS("Clear Track Marker", 0),
HDSPM_SAFE_MODE("Safe Mode", 0),
- HDSPM_INPUT_SELECT("Input Select", 0)
+ HDSPM_INPUT_SELECT("Input Select", 0),
+ HDSPM_MADI_SPEEDMODE("MADI Speed Mode", 0)
};
@@ -4302,7 +4412,8 @@
HDSPM_SYNC_CHECK("MADI SyncCheck", 0),
HDSPM_TX_64("TX 64 channels mode", 0),
HDSPM_C_TMS("Clear Track Marker", 0),
- HDSPM_SAFE_MODE("Safe Mode", 0)
+ HDSPM_SAFE_MODE("Safe Mode", 0),
+ HDSPM_MADI_SPEEDMODE("MADI Speed Mode", 0)
};
static struct snd_kcontrol_new snd_hdspm_controls_aio[] = {
@@ -6381,6 +6492,7 @@
switch (hdspm->firmware_rev) {
case HDSPM_MADI_REV:
case HDSPM_MADI_OLD_REV:
+ case HDSPM_MADI_ANCIENT_REV:
hdspm->io_type = MADI;
hdspm->card_name = "RME MADI";
hdspm->midiPorts = 3;
diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig
index 379b2e3..665d924 100644
--- a/sound/soc/codecs/Kconfig
+++ b/sound/soc/codecs/Kconfig
@@ -78,7 +78,6 @@
select SND_SOC_WM8900 if I2C
select SND_SOC_WM8903 if I2C
select SND_SOC_WM8904 if I2C
- select SND_SOC_WM8915 if I2C
select SND_SOC_WM8940 if I2C
select SND_SOC_WM8955 if I2C
select SND_SOC_WM8960 if I2C
@@ -95,6 +94,7 @@
select SND_SOC_WM8993 if I2C
select SND_SOC_WM8994 if MFD_WM8994
select SND_SOC_WM8995 if SND_SOC_I2C_AND_SPI
+ select SND_SOC_WM8996 if I2C
select SND_SOC_WM9081 if I2C
select SND_SOC_WM9090 if I2C
select SND_SOC_WM9705 if SND_SOC_AC97_BUS
@@ -329,9 +329,6 @@
config SND_SOC_WM8904
tristate
-config SND_SOC_WM8915
- tristate
-
config SND_SOC_WM8940
tristate
@@ -380,6 +377,9 @@
config SND_SOC_WM8995
tristate
+config SND_SOC_WM8996
+ tristate
+
config SND_SOC_WM9081
tristate
diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile
index da9990f..5119a7e 100644
--- a/sound/soc/codecs/Makefile
+++ b/sound/soc/codecs/Makefile
@@ -63,7 +63,7 @@
snd-soc-wm8900-objs := wm8900.o
snd-soc-wm8903-objs := wm8903.o
snd-soc-wm8904-objs := wm8904.o
-snd-soc-wm8915-objs := wm8915.o
+snd-soc-wm8996-objs := wm8996.o
snd-soc-wm8940-objs := wm8940.o
snd-soc-wm8955-objs := wm8955.o
snd-soc-wm8960-objs := wm8960.o
@@ -160,7 +160,7 @@
obj-$(CONFIG_SND_SOC_WM8900) += snd-soc-wm8900.o
obj-$(CONFIG_SND_SOC_WM8903) += snd-soc-wm8903.o
obj-$(CONFIG_SND_SOC_WM8904) += snd-soc-wm8904.o
-obj-$(CONFIG_SND_SOC_WM8915) += snd-soc-wm8915.o
+obj-$(CONFIG_SND_SOC_WM8996) += snd-soc-wm8996.o
obj-$(CONFIG_SND_SOC_WM8940) += snd-soc-wm8940.o
obj-$(CONFIG_SND_SOC_WM8955) += snd-soc-wm8955.o
obj-$(CONFIG_SND_SOC_WM8960) += snd-soc-wm8960.o
diff --git a/sound/soc/codecs/sgtl5000.c b/sound/soc/codecs/sgtl5000.c
index 76258f2..7e4066e 100644
--- a/sound/soc/codecs/sgtl5000.c
+++ b/sound/soc/codecs/sgtl5000.c
@@ -33,73 +33,31 @@
#define SGTL5000_DAP_REG_OFFSET 0x0100
#define SGTL5000_MAX_REG_OFFSET 0x013A
-/* default value of sgtl5000 registers except DAP */
-static const u16 sgtl5000_regs[SGTL5000_MAX_REG_OFFSET >> 1] = {
- 0xa011, /* 0x0000, CHIP_ID. 11 stand for revison 17 */
- 0x0000, /* 0x0002, CHIP_DIG_POWER. */
- 0x0008, /* 0x0004, CHIP_CKL_CTRL */
- 0x0010, /* 0x0006, CHIP_I2S_CTRL */
- 0x0000, /* 0x0008, reserved */
- 0x0008, /* 0x000A, CHIP_SSS_CTRL */
- 0x0000, /* 0x000C, reserved */
- 0x020c, /* 0x000E, CHIP_ADCDAC_CTRL */
- 0x3c3c, /* 0x0010, CHIP_DAC_VOL */
- 0x0000, /* 0x0012, reserved */
- 0x015f, /* 0x0014, CHIP_PAD_STRENGTH */
- 0x0000, /* 0x0016, reserved */
- 0x0000, /* 0x0018, reserved */
- 0x0000, /* 0x001A, reserved */
- 0x0000, /* 0x001E, reserved */
- 0x0000, /* 0x0020, CHIP_ANA_ADC_CTRL */
- 0x1818, /* 0x0022, CHIP_ANA_HP_CTRL */
- 0x0111, /* 0x0024, CHIP_ANN_CTRL */
- 0x0000, /* 0x0026, CHIP_LINREG_CTRL */
- 0x0000, /* 0x0028, CHIP_REF_CTRL */
- 0x0000, /* 0x002A, CHIP_MIC_CTRL */
- 0x0000, /* 0x002C, CHIP_LINE_OUT_CTRL */
- 0x0404, /* 0x002E, CHIP_LINE_OUT_VOL */
- 0x7060, /* 0x0030, CHIP_ANA_POWER */
- 0x5000, /* 0x0032, CHIP_PLL_CTRL */
- 0x0000, /* 0x0034, CHIP_CLK_TOP_CTRL */
- 0x0000, /* 0x0036, CHIP_ANA_STATUS */
- 0x0000, /* 0x0038, reserved */
- 0x0000, /* 0x003A, CHIP_ANA_TEST2 */
- 0x0000, /* 0x003C, CHIP_SHORT_CTRL */
- 0x0000, /* reserved */
-};
-
-/* default value of dap registers */
-static const u16 sgtl5000_dap_regs[] = {
- 0x0000, /* 0x0100, DAP_CONTROL */
- 0x0000, /* 0x0102, DAP_PEQ */
- 0x0040, /* 0x0104, DAP_BASS_ENHANCE */
- 0x051f, /* 0x0106, DAP_BASS_ENHANCE_CTRL */
- 0x0000, /* 0x0108, DAP_AUDIO_EQ */
- 0x0040, /* 0x010A, DAP_SGTL_SURROUND */
- 0x0000, /* 0x010C, DAP_FILTER_COEF_ACCESS */
- 0x0000, /* 0x010E, DAP_COEF_WR_B0_MSB */
- 0x0000, /* 0x0110, DAP_COEF_WR_B0_LSB */
- 0x0000, /* 0x0112, reserved */
- 0x0000, /* 0x0114, reserved */
- 0x002f, /* 0x0116, DAP_AUDIO_EQ_BASS_BAND0 */
- 0x002f, /* 0x0118, DAP_AUDIO_EQ_BAND0 */
- 0x002f, /* 0x011A, DAP_AUDIO_EQ_BAND2 */
- 0x002f, /* 0x011C, DAP_AUDIO_EQ_BAND3 */
- 0x002f, /* 0x011E, DAP_AUDIO_EQ_TREBLE_BAND4 */
- 0x8000, /* 0x0120, DAP_MAIN_CHAN */
- 0x0000, /* 0x0122, DAP_MIX_CHAN */
- 0x0510, /* 0x0124, DAP_AVC_CTRL */
- 0x1473, /* 0x0126, DAP_AVC_THRESHOLD */
- 0x0028, /* 0x0128, DAP_AVC_ATTACK */
- 0x0050, /* 0x012A, DAP_AVC_DECAY */
- 0x0000, /* 0x012C, DAP_COEF_WR_B1_MSB */
- 0x0000, /* 0x012E, DAP_COEF_WR_B1_LSB */
- 0x0000, /* 0x0130, DAP_COEF_WR_B2_MSB */
- 0x0000, /* 0x0132, DAP_COEF_WR_B2_LSB */
- 0x0000, /* 0x0134, DAP_COEF_WR_A1_MSB */
- 0x0000, /* 0x0136, DAP_COEF_WR_A1_LSB */
- 0x0000, /* 0x0138, DAP_COEF_WR_A2_MSB */
- 0x0000, /* 0x013A, DAP_COEF_WR_A2_LSB */
+/* default value of sgtl5000 registers */
+static const u16 sgtl5000_regs[SGTL5000_MAX_REG_OFFSET] = {
+ [SGTL5000_CHIP_CLK_CTRL] = 0x0008,
+ [SGTL5000_CHIP_I2S_CTRL] = 0x0010,
+ [SGTL5000_CHIP_SSS_CTRL] = 0x0008,
+ [SGTL5000_CHIP_DAC_VOL] = 0x3c3c,
+ [SGTL5000_CHIP_PAD_STRENGTH] = 0x015f,
+ [SGTL5000_CHIP_ANA_HP_CTRL] = 0x1818,
+ [SGTL5000_CHIP_ANA_CTRL] = 0x0111,
+ [SGTL5000_CHIP_LINE_OUT_VOL] = 0x0404,
+ [SGTL5000_CHIP_ANA_POWER] = 0x7060,
+ [SGTL5000_CHIP_PLL_CTRL] = 0x5000,
+ [SGTL5000_DAP_BASS_ENHANCE] = 0x0040,
+ [SGTL5000_DAP_BASS_ENHANCE_CTRL] = 0x051f,
+ [SGTL5000_DAP_SURROUND] = 0x0040,
+ [SGTL5000_DAP_EQ_BASS_BAND0] = 0x002f,
+ [SGTL5000_DAP_EQ_BASS_BAND1] = 0x002f,
+ [SGTL5000_DAP_EQ_BASS_BAND2] = 0x002f,
+ [SGTL5000_DAP_EQ_BASS_BAND3] = 0x002f,
+ [SGTL5000_DAP_EQ_BASS_BAND4] = 0x002f,
+ [SGTL5000_DAP_MAIN_CHAN] = 0x8000,
+ [SGTL5000_DAP_AVC_CTRL] = 0x0510,
+ [SGTL5000_DAP_AVC_THRESHOLD] = 0x1473,
+ [SGTL5000_DAP_AVC_ATTACK] = 0x0028,
+ [SGTL5000_DAP_AVC_DECAY] = 0x0050,
};
/* regulator supplies for sgtl5000, VDDD is an optional external supply */
@@ -1023,12 +981,10 @@
static int sgtl5000_restore_regs(struct snd_soc_codec *codec)
{
u16 *cache = codec->reg_cache;
- int i;
- int regular_regs = SGTL5000_CHIP_SHORT_CTRL >> 1;
+ u16 reg;
/* restore regular registers */
- for (i = 0; i < regular_regs; i++) {
- int reg = i << 1;
+ for (reg = 0; reg <= SGTL5000_CHIP_SHORT_CTRL; reg += 2) {
/* this regs depends on the others */
if (reg == SGTL5000_CHIP_ANA_POWER ||
@@ -1038,35 +994,31 @@
reg == SGTL5000_CHIP_CLK_CTRL)
continue;
- snd_soc_write(codec, reg, cache[i]);
+ snd_soc_write(codec, reg, cache[reg]);
}
/* restore dap registers */
- for (i = SGTL5000_DAP_REG_OFFSET >> 1;
- i < SGTL5000_MAX_REG_OFFSET >> 1; i++) {
- int reg = i << 1;
-
- snd_soc_write(codec, reg, cache[i]);
- }
+ for (reg = SGTL5000_DAP_REG_OFFSET; reg < SGTL5000_MAX_REG_OFFSET; reg += 2)
+ snd_soc_write(codec, reg, cache[reg]);
/*
* restore power and other regs according
* to set_power() and set_clock()
*/
snd_soc_write(codec, SGTL5000_CHIP_LINREG_CTRL,
- cache[SGTL5000_CHIP_LINREG_CTRL >> 1]);
+ cache[SGTL5000_CHIP_LINREG_CTRL]);
snd_soc_write(codec, SGTL5000_CHIP_ANA_POWER,
- cache[SGTL5000_CHIP_ANA_POWER >> 1]);
+ cache[SGTL5000_CHIP_ANA_POWER]);
snd_soc_write(codec, SGTL5000_CHIP_CLK_CTRL,
- cache[SGTL5000_CHIP_CLK_CTRL >> 1]);
+ cache[SGTL5000_CHIP_CLK_CTRL]);
snd_soc_write(codec, SGTL5000_CHIP_REF_CTRL,
- cache[SGTL5000_CHIP_REF_CTRL >> 1]);
+ cache[SGTL5000_CHIP_REF_CTRL]);
snd_soc_write(codec, SGTL5000_CHIP_LINE_OUT_CTRL,
- cache[SGTL5000_CHIP_LINE_OUT_CTRL >> 1]);
+ cache[SGTL5000_CHIP_LINE_OUT_CTRL]);
return 0;
}
@@ -1454,16 +1406,6 @@
if (!sgtl5000)
return -ENOMEM;
- /*
- * copy DAP default values to default value array.
- * sgtl5000 register space has a big hole, merge it
- * at init phase makes life easy.
- * FIXME: should we drop 'const' of sgtl5000_regs?
- */
- memcpy((void *)(&sgtl5000_regs[0] + (SGTL5000_DAP_REG_OFFSET >> 1)),
- sgtl5000_dap_regs,
- SGTL5000_MAX_REG_OFFSET - SGTL5000_DAP_REG_OFFSET);
-
i2c_set_clientdata(client, sgtl5000);
ret = snd_soc_register_codec(&client->dev,
diff --git a/sound/soc/codecs/wm8750.c b/sound/soc/codecs/wm8750.c
index 38f38fd..d0003cc 100644
--- a/sound/soc/codecs/wm8750.c
+++ b/sound/soc/codecs/wm8750.c
@@ -778,11 +778,19 @@
return 0;
}
+static const struct spi_device_id wm8750_spi_ids[] = {
+ { "wm8750", 0 },
+ { "wm8987", 0 },
+ { },
+};
+MODULE_DEVICE_TABLE(spi, wm8750_spi_ids);
+
static struct spi_driver wm8750_spi_driver = {
.driver = {
.name = "wm8750-codec",
.owner = THIS_MODULE,
},
+ .id_table = wm8750_spi_ids,
.probe = wm8750_spi_probe,
.remove = __devexit_p(wm8750_spi_remove),
};
diff --git a/sound/soc/codecs/wm8903.c b/sound/soc/codecs/wm8903.c
index 43e3d76..4ad8ebd 100644
--- a/sound/soc/codecs/wm8903.c
+++ b/sound/soc/codecs/wm8903.c
@@ -2046,8 +2046,13 @@
/* power down chip */
static int wm8903_remove(struct snd_soc_codec *codec)
{
+ struct wm8903_priv *wm8903 = snd_soc_codec_get_drvdata(codec);
+
wm8903_free_gpio(codec);
wm8903_set_bias_level(codec, SND_SOC_BIAS_OFF);
+ if (wm8903->irq)
+ free_irq(wm8903->irq, codec);
+
return 0;
}
diff --git a/sound/soc/codecs/wm8915.c b/sound/soc/codecs/wm8915.c
deleted file mode 100644
index 423baa9..0000000
--- a/sound/soc/codecs/wm8915.c
+++ /dev/null
@@ -1,2995 +0,0 @@
-/*
- * wm8915.c - WM8915 audio codec interface
- *
- * Copyright 2011 Wolfson Microelectronics PLC.
- * Author: Mark Brown <broonie@opensource.wolfsonmicro.com>
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the
- * Free Software Foundation; either version 2 of the License, or (at your
- * option) any later version.
- */
-
-#include <linux/module.h>
-#include <linux/moduleparam.h>
-#include <linux/init.h>
-#include <linux/completion.h>
-#include <linux/delay.h>
-#include <linux/pm.h>
-#include <linux/gcd.h>
-#include <linux/gpio.h>
-#include <linux/i2c.h>
-#include <linux/regulator/consumer.h>
-#include <linux/slab.h>
-#include <linux/workqueue.h>
-#include <sound/core.h>
-#include <sound/jack.h>
-#include <sound/pcm.h>
-#include <sound/pcm_params.h>
-#include <sound/soc.h>
-#include <sound/initval.h>
-#include <sound/tlv.h>
-#include <trace/events/asoc.h>
-
-#include <sound/wm8915.h>
-#include "wm8915.h"
-
-#define WM8915_AIFS 2
-
-#define HPOUT1L 1
-#define HPOUT1R 2
-#define HPOUT2L 4
-#define HPOUT2R 8
-
-#define WM8915_NUM_SUPPLIES 4
-static const char *wm8915_supply_names[WM8915_NUM_SUPPLIES] = {
- "DBVDD",
- "AVDD1",
- "AVDD2",
- "CPVDD",
-};
-
-struct wm8915_priv {
- struct snd_soc_codec *codec;
-
- int ldo1ena;
-
- int sysclk;
- int sysclk_src;
-
- int fll_src;
- int fll_fref;
- int fll_fout;
-
- struct completion fll_lock;
-
- u16 dcs_pending;
- struct completion dcs_done;
-
- u16 hpout_ena;
- u16 hpout_pending;
-
- struct regulator_bulk_data supplies[WM8915_NUM_SUPPLIES];
- struct notifier_block disable_nb[WM8915_NUM_SUPPLIES];
-
- struct wm8915_pdata pdata;
-
- int rx_rate[WM8915_AIFS];
- int bclk_rate[WM8915_AIFS];
-
- /* Platform dependant ReTune mobile configuration */
- int num_retune_mobile_texts;
- const char **retune_mobile_texts;
- int retune_mobile_cfg[2];
- struct soc_enum retune_mobile_enum;
-
- struct snd_soc_jack *jack;
- bool detecting;
- bool jack_mic;
- wm8915_polarity_fn polarity_cb;
-
-#ifdef CONFIG_GPIOLIB
- struct gpio_chip gpio_chip;
-#endif
-};
-
-/* We can't use the same notifier block for more than one supply and
- * there's no way I can see to get from a callback to the caller
- * except container_of().
- */
-#define WM8915_REGULATOR_EVENT(n) \
-static int wm8915_regulator_event_##n(struct notifier_block *nb, \
- unsigned long event, void *data) \
-{ \
- struct wm8915_priv *wm8915 = container_of(nb, struct wm8915_priv, \
- disable_nb[n]); \
- if (event & REGULATOR_EVENT_DISABLE) { \
- wm8915->codec->cache_sync = 1; \
- } \
- return 0; \
-}
-
-WM8915_REGULATOR_EVENT(0)
-WM8915_REGULATOR_EVENT(1)
-WM8915_REGULATOR_EVENT(2)
-WM8915_REGULATOR_EVENT(3)
-
-static const u16 wm8915_reg[WM8915_MAX_REGISTER] = {
- [WM8915_SOFTWARE_RESET] = 0x8915,
- [WM8915_POWER_MANAGEMENT_7] = 0x10,
- [WM8915_DAC1_HPOUT1_VOLUME] = 0x88,
- [WM8915_DAC2_HPOUT2_VOLUME] = 0x88,
- [WM8915_DAC1_LEFT_VOLUME] = 0x2c0,
- [WM8915_DAC1_RIGHT_VOLUME] = 0x2c0,
- [WM8915_DAC2_LEFT_VOLUME] = 0x2c0,
- [WM8915_DAC2_RIGHT_VOLUME] = 0x2c0,
- [WM8915_OUTPUT1_LEFT_VOLUME] = 0x80,
- [WM8915_OUTPUT1_RIGHT_VOLUME] = 0x80,
- [WM8915_OUTPUT2_LEFT_VOLUME] = 0x80,
- [WM8915_OUTPUT2_RIGHT_VOLUME] = 0x80,
- [WM8915_MICBIAS_1] = 0x39,
- [WM8915_MICBIAS_2] = 0x39,
- [WM8915_LDO_1] = 0x3,
- [WM8915_LDO_2] = 0x13,
- [WM8915_ACCESSORY_DETECT_MODE_1] = 0x4,
- [WM8915_HEADPHONE_DETECT_1] = 0x20,
- [WM8915_MIC_DETECT_1] = 0x7600,
- [WM8915_MIC_DETECT_2] = 0xbf,
- [WM8915_CHARGE_PUMP_1] = 0x1f25,
- [WM8915_CHARGE_PUMP_2] = 0xab19,
- [WM8915_DC_SERVO_5] = 0x2a2a,
- [WM8915_CONTROL_INTERFACE_1] = 0x8004,
- [WM8915_CLOCKING_1] = 0x10,
- [WM8915_AIF_RATE] = 0x83,
- [WM8915_FLL_CONTROL_4] = 0x5dc0,
- [WM8915_FLL_CONTROL_5] = 0xc84,
- [WM8915_FLL_EFS_2] = 0x2,
- [WM8915_AIF1_TX_LRCLK_1] = 0x80,
- [WM8915_AIF1_TX_LRCLK_2] = 0x8,
- [WM8915_AIF1_RX_LRCLK_1] = 0x80,
- [WM8915_AIF1TX_DATA_CONFIGURATION_1] = 0x1818,
- [WM8915_AIF1RX_DATA_CONFIGURATION] = 0x1818,
- [WM8915_AIF1TX_TEST] = 0x7,
- [WM8915_AIF2_TX_LRCLK_1] = 0x80,
- [WM8915_AIF2_TX_LRCLK_2] = 0x8,
- [WM8915_AIF2_RX_LRCLK_1] = 0x80,
- [WM8915_AIF2TX_DATA_CONFIGURATION_1] = 0x1818,
- [WM8915_AIF2RX_DATA_CONFIGURATION] = 0x1818,
- [WM8915_AIF2TX_TEST] = 0x1,
- [WM8915_DSP1_TX_LEFT_VOLUME] = 0xc0,
- [WM8915_DSP1_TX_RIGHT_VOLUME] = 0xc0,
- [WM8915_DSP1_RX_LEFT_VOLUME] = 0xc0,
- [WM8915_DSP1_RX_RIGHT_VOLUME] = 0xc0,
- [WM8915_DSP1_TX_FILTERS] = 0x2000,
- [WM8915_DSP1_RX_FILTERS_1] = 0x200,
- [WM8915_DSP1_RX_FILTERS_2] = 0x10,
- [WM8915_DSP1_DRC_1] = 0x98,
- [WM8915_DSP1_DRC_2] = 0x845,
- [WM8915_DSP1_RX_EQ_GAINS_1] = 0x6318,
- [WM8915_DSP1_RX_EQ_GAINS_2] = 0x6300,
- [WM8915_DSP1_RX_EQ_BAND_1_A] = 0xfca,
- [WM8915_DSP1_RX_EQ_BAND_1_B] = 0x400,
- [WM8915_DSP1_RX_EQ_BAND_1_PG] = 0xd8,
- [WM8915_DSP1_RX_EQ_BAND_2_A] = 0x1eb5,
- [WM8915_DSP1_RX_EQ_BAND_2_B] = 0xf145,
- [WM8915_DSP1_RX_EQ_BAND_2_C] = 0xb75,
- [WM8915_DSP1_RX_EQ_BAND_2_PG] = 0x1c5,
- [WM8915_DSP1_RX_EQ_BAND_3_A] = 0x1c58,
- [WM8915_DSP1_RX_EQ_BAND_3_B] = 0xf373,
- [WM8915_DSP1_RX_EQ_BAND_3_C] = 0xa54,
- [WM8915_DSP1_RX_EQ_BAND_3_PG] = 0x558,
- [WM8915_DSP1_RX_EQ_BAND_4_A] = 0x168e,
- [WM8915_DSP1_RX_EQ_BAND_4_B] = 0xf829,
- [WM8915_DSP1_RX_EQ_BAND_4_C] = 0x7ad,
- [WM8915_DSP1_RX_EQ_BAND_4_PG] = 0x1103,
- [WM8915_DSP1_RX_EQ_BAND_5_A] = 0x564,
- [WM8915_DSP1_RX_EQ_BAND_5_B] = 0x559,
- [WM8915_DSP1_RX_EQ_BAND_5_PG] = 0x4000,
- [WM8915_DSP2_TX_LEFT_VOLUME] = 0xc0,
- [WM8915_DSP2_TX_RIGHT_VOLUME] = 0xc0,
- [WM8915_DSP2_RX_LEFT_VOLUME] = 0xc0,
- [WM8915_DSP2_RX_RIGHT_VOLUME] = 0xc0,
- [WM8915_DSP2_TX_FILTERS] = 0x2000,
- [WM8915_DSP2_RX_FILTERS_1] = 0x200,
- [WM8915_DSP2_RX_FILTERS_2] = 0x10,
- [WM8915_DSP2_DRC_1] = 0x98,
- [WM8915_DSP2_DRC_2] = 0x845,
- [WM8915_DSP2_RX_EQ_GAINS_1] = 0x6318,
- [WM8915_DSP2_RX_EQ_GAINS_2] = 0x6300,
- [WM8915_DSP2_RX_EQ_BAND_1_A] = 0xfca,
- [WM8915_DSP2_RX_EQ_BAND_1_B] = 0x400,
- [WM8915_DSP2_RX_EQ_BAND_1_PG] = 0xd8,
- [WM8915_DSP2_RX_EQ_BAND_2_A] = 0x1eb5,
- [WM8915_DSP2_RX_EQ_BAND_2_B] = 0xf145,
- [WM8915_DSP2_RX_EQ_BAND_2_C] = 0xb75,
- [WM8915_DSP2_RX_EQ_BAND_2_PG] = 0x1c5,
- [WM8915_DSP2_RX_EQ_BAND_3_A] = 0x1c58,
- [WM8915_DSP2_RX_EQ_BAND_3_B] = 0xf373,
- [WM8915_DSP2_RX_EQ_BAND_3_C] = 0xa54,
- [WM8915_DSP2_RX_EQ_BAND_3_PG] = 0x558,
- [WM8915_DSP2_RX_EQ_BAND_4_A] = 0x168e,
- [WM8915_DSP2_RX_EQ_BAND_4_B] = 0xf829,
- [WM8915_DSP2_RX_EQ_BAND_4_C] = 0x7ad,
- [WM8915_DSP2_RX_EQ_BAND_4_PG] = 0x1103,
- [WM8915_DSP2_RX_EQ_BAND_5_A] = 0x564,
- [WM8915_DSP2_RX_EQ_BAND_5_B] = 0x559,
- [WM8915_DSP2_RX_EQ_BAND_5_PG] = 0x4000,
- [WM8915_OVERSAMPLING] = 0xd,
- [WM8915_SIDETONE] = 0x1040,
- [WM8915_GPIO_1] = 0xa101,
- [WM8915_GPIO_2] = 0xa101,
- [WM8915_GPIO_3] = 0xa101,
- [WM8915_GPIO_4] = 0xa101,
- [WM8915_GPIO_5] = 0xa101,
- [WM8915_PULL_CONTROL_2] = 0x140,
- [WM8915_INTERRUPT_STATUS_1_MASK] = 0x1f,
- [WM8915_INTERRUPT_STATUS_2_MASK] = 0x1ecf,
- [WM8915_RIGHT_PDM_SPEAKER] = 0x1,
- [WM8915_PDM_SPEAKER_MUTE_SEQUENCE] = 0x69,
- [WM8915_PDM_SPEAKER_VOLUME] = 0x66,
- [WM8915_WRITE_SEQUENCER_0] = 0x1,
- [WM8915_WRITE_SEQUENCER_1] = 0x1,
- [WM8915_WRITE_SEQUENCER_3] = 0x6,
- [WM8915_WRITE_SEQUENCER_4] = 0x40,
- [WM8915_WRITE_SEQUENCER_5] = 0x1,
- [WM8915_WRITE_SEQUENCER_6] = 0xf,
- [WM8915_WRITE_SEQUENCER_7] = 0x6,
- [WM8915_WRITE_SEQUENCER_8] = 0x1,
- [WM8915_WRITE_SEQUENCER_9] = 0x3,
- [WM8915_WRITE_SEQUENCER_10] = 0x104,
- [WM8915_WRITE_SEQUENCER_12] = 0x60,
- [WM8915_WRITE_SEQUENCER_13] = 0x11,
- [WM8915_WRITE_SEQUENCER_14] = 0x401,
- [WM8915_WRITE_SEQUENCER_16] = 0x50,
- [WM8915_WRITE_SEQUENCER_17] = 0x3,
- [WM8915_WRITE_SEQUENCER_18] = 0x100,
- [WM8915_WRITE_SEQUENCER_20] = 0x51,
- [WM8915_WRITE_SEQUENCER_21] = 0x3,
- [WM8915_WRITE_SEQUENCER_22] = 0x104,
- [WM8915_WRITE_SEQUENCER_23] = 0xa,
- [WM8915_WRITE_SEQUENCER_24] = 0x60,
- [WM8915_WRITE_SEQUENCER_25] = 0x3b,
- [WM8915_WRITE_SEQUENCER_26] = 0x502,
- [WM8915_WRITE_SEQUENCER_27] = 0x100,
- [WM8915_WRITE_SEQUENCER_28] = 0x2fff,
- [WM8915_WRITE_SEQUENCER_32] = 0x2fff,
- [WM8915_WRITE_SEQUENCER_36] = 0x2fff,
- [WM8915_WRITE_SEQUENCER_40] = 0x2fff,
- [WM8915_WRITE_SEQUENCER_44] = 0x2fff,
- [WM8915_WRITE_SEQUENCER_48] = 0x2fff,
- [WM8915_WRITE_SEQUENCER_52] = 0x2fff,
- [WM8915_WRITE_SEQUENCER_56] = 0x2fff,
- [WM8915_WRITE_SEQUENCER_60] = 0x2fff,
- [WM8915_WRITE_SEQUENCER_64] = 0x1,
- [WM8915_WRITE_SEQUENCER_65] = 0x1,
- [WM8915_WRITE_SEQUENCER_67] = 0x6,
- [WM8915_WRITE_SEQUENCER_68] = 0x40,
- [WM8915_WRITE_SEQUENCER_69] = 0x1,
- [WM8915_WRITE_SEQUENCER_70] = 0xf,
- [WM8915_WRITE_SEQUENCER_71] = 0x6,
- [WM8915_WRITE_SEQUENCER_72] = 0x1,
- [WM8915_WRITE_SEQUENCER_73] = 0x3,
- [WM8915_WRITE_SEQUENCER_74] = 0x104,
- [WM8915_WRITE_SEQUENCER_76] = 0x60,
- [WM8915_WRITE_SEQUENCER_77] = 0x11,
- [WM8915_WRITE_SEQUENCER_78] = 0x401,
- [WM8915_WRITE_SEQUENCER_80] = 0x50,
- [WM8915_WRITE_SEQUENCER_81] = 0x3,
- [WM8915_WRITE_SEQUENCER_82] = 0x100,
- [WM8915_WRITE_SEQUENCER_84] = 0x60,
- [WM8915_WRITE_SEQUENCER_85] = 0x3b,
- [WM8915_WRITE_SEQUENCER_86] = 0x502,
- [WM8915_WRITE_SEQUENCER_87] = 0x100,
- [WM8915_WRITE_SEQUENCER_88] = 0x2fff,
- [WM8915_WRITE_SEQUENCER_92] = 0x2fff,
- [WM8915_WRITE_SEQUENCER_96] = 0x2fff,
- [WM8915_WRITE_SEQUENCER_100] = 0x2fff,
- [WM8915_WRITE_SEQUENCER_104] = 0x2fff,
- [WM8915_WRITE_SEQUENCER_108] = 0x2fff,
- [WM8915_WRITE_SEQUENCER_112] = 0x2fff,
- [WM8915_WRITE_SEQUENCER_116] = 0x2fff,
- [WM8915_WRITE_SEQUENCER_120] = 0x2fff,
- [WM8915_WRITE_SEQUENCER_124] = 0x2fff,
- [WM8915_WRITE_SEQUENCER_128] = 0x1,
- [WM8915_WRITE_SEQUENCER_129] = 0x1,
- [WM8915_WRITE_SEQUENCER_131] = 0x6,
- [WM8915_WRITE_SEQUENCER_132] = 0x40,
- [WM8915_WRITE_SEQUENCER_133] = 0x1,
- [WM8915_WRITE_SEQUENCER_134] = 0xf,
- [WM8915_WRITE_SEQUENCER_135] = 0x6,
- [WM8915_WRITE_SEQUENCER_136] = 0x1,
- [WM8915_WRITE_SEQUENCER_137] = 0x3,
- [WM8915_WRITE_SEQUENCER_138] = 0x106,
- [WM8915_WRITE_SEQUENCER_140] = 0x61,
- [WM8915_WRITE_SEQUENCER_141] = 0x11,
- [WM8915_WRITE_SEQUENCER_142] = 0x401,
- [WM8915_WRITE_SEQUENCER_144] = 0x50,
- [WM8915_WRITE_SEQUENCER_145] = 0x3,
- [WM8915_WRITE_SEQUENCER_146] = 0x102,
- [WM8915_WRITE_SEQUENCER_148] = 0x51,
- [WM8915_WRITE_SEQUENCER_149] = 0x3,
- [WM8915_WRITE_SEQUENCER_150] = 0x106,
- [WM8915_WRITE_SEQUENCER_151] = 0xa,
- [WM8915_WRITE_SEQUENCER_152] = 0x61,
- [WM8915_WRITE_SEQUENCER_153] = 0x3b,
- [WM8915_WRITE_SEQUENCER_154] = 0x502,
- [WM8915_WRITE_SEQUENCER_155] = 0x100,
- [WM8915_WRITE_SEQUENCER_156] = 0x2fff,
- [WM8915_WRITE_SEQUENCER_160] = 0x2fff,
- [WM8915_WRITE_SEQUENCER_164] = 0x2fff,
- [WM8915_WRITE_SEQUENCER_168] = 0x2fff,
- [WM8915_WRITE_SEQUENCER_172] = 0x2fff,
- [WM8915_WRITE_SEQUENCER_176] = 0x2fff,
- [WM8915_WRITE_SEQUENCER_180] = 0x2fff,
- [WM8915_WRITE_SEQUENCER_184] = 0x2fff,
- [WM8915_WRITE_SEQUENCER_188] = 0x2fff,
- [WM8915_WRITE_SEQUENCER_192] = 0x1,
- [WM8915_WRITE_SEQUENCER_193] = 0x1,
- [WM8915_WRITE_SEQUENCER_195] = 0x6,
- [WM8915_WRITE_SEQUENCER_196] = 0x40,
- [WM8915_WRITE_SEQUENCER_197] = 0x1,
- [WM8915_WRITE_SEQUENCER_198] = 0xf,
- [WM8915_WRITE_SEQUENCER_199] = 0x6,
- [WM8915_WRITE_SEQUENCER_200] = 0x1,
- [WM8915_WRITE_SEQUENCER_201] = 0x3,
- [WM8915_WRITE_SEQUENCER_202] = 0x106,
- [WM8915_WRITE_SEQUENCER_204] = 0x61,
- [WM8915_WRITE_SEQUENCER_205] = 0x11,
- [WM8915_WRITE_SEQUENCER_206] = 0x401,
- [WM8915_WRITE_SEQUENCER_208] = 0x50,
- [WM8915_WRITE_SEQUENCER_209] = 0x3,
- [WM8915_WRITE_SEQUENCER_210] = 0x102,
- [WM8915_WRITE_SEQUENCER_212] = 0x61,
- [WM8915_WRITE_SEQUENCER_213] = 0x3b,
- [WM8915_WRITE_SEQUENCER_214] = 0x502,
- [WM8915_WRITE_SEQUENCER_215] = 0x100,
- [WM8915_WRITE_SEQUENCER_216] = 0x2fff,
- [WM8915_WRITE_SEQUENCER_220] = 0x2fff,
- [WM8915_WRITE_SEQUENCER_224] = 0x2fff,
- [WM8915_WRITE_SEQUENCER_228] = 0x2fff,
- [WM8915_WRITE_SEQUENCER_232] = 0x2fff,
- [WM8915_WRITE_SEQUENCER_236] = 0x2fff,
- [WM8915_WRITE_SEQUENCER_240] = 0x2fff,
- [WM8915_WRITE_SEQUENCER_244] = 0x2fff,
- [WM8915_WRITE_SEQUENCER_248] = 0x2fff,
- [WM8915_WRITE_SEQUENCER_252] = 0x2fff,
- [WM8915_WRITE_SEQUENCER_256] = 0x60,
- [WM8915_WRITE_SEQUENCER_258] = 0x601,
- [WM8915_WRITE_SEQUENCER_260] = 0x50,
- [WM8915_WRITE_SEQUENCER_262] = 0x100,
- [WM8915_WRITE_SEQUENCER_264] = 0x1,
- [WM8915_WRITE_SEQUENCER_266] = 0x104,
- [WM8915_WRITE_SEQUENCER_267] = 0x100,
- [WM8915_WRITE_SEQUENCER_268] = 0x2fff,
- [WM8915_WRITE_SEQUENCER_272] = 0x2fff,
- [WM8915_WRITE_SEQUENCER_276] = 0x2fff,
- [WM8915_WRITE_SEQUENCER_280] = 0x2fff,
- [WM8915_WRITE_SEQUENCER_284] = 0x2fff,
- [WM8915_WRITE_SEQUENCER_288] = 0x2fff,
- [WM8915_WRITE_SEQUENCER_292] = 0x2fff,
- [WM8915_WRITE_SEQUENCER_296] = 0x2fff,
- [WM8915_WRITE_SEQUENCER_300] = 0x2fff,
- [WM8915_WRITE_SEQUENCER_304] = 0x2fff,
- [WM8915_WRITE_SEQUENCER_308] = 0x2fff,
- [WM8915_WRITE_SEQUENCER_312] = 0x2fff,
- [WM8915_WRITE_SEQUENCER_316] = 0x2fff,
- [WM8915_WRITE_SEQUENCER_320] = 0x61,
- [WM8915_WRITE_SEQUENCER_322] = 0x601,
- [WM8915_WRITE_SEQUENCER_324] = 0x50,
- [WM8915_WRITE_SEQUENCER_326] = 0x102,
- [WM8915_WRITE_SEQUENCER_328] = 0x1,
- [WM8915_WRITE_SEQUENCER_330] = 0x106,
- [WM8915_WRITE_SEQUENCER_331] = 0x100,
- [WM8915_WRITE_SEQUENCER_332] = 0x2fff,
- [WM8915_WRITE_SEQUENCER_336] = 0x2fff,
- [WM8915_WRITE_SEQUENCER_340] = 0x2fff,
- [WM8915_WRITE_SEQUENCER_344] = 0x2fff,
- [WM8915_WRITE_SEQUENCER_348] = 0x2fff,
- [WM8915_WRITE_SEQUENCER_352] = 0x2fff,
- [WM8915_WRITE_SEQUENCER_356] = 0x2fff,
- [WM8915_WRITE_SEQUENCER_360] = 0x2fff,
- [WM8915_WRITE_SEQUENCER_364] = 0x2fff,
- [WM8915_WRITE_SEQUENCER_368] = 0x2fff,
- [WM8915_WRITE_SEQUENCER_372] = 0x2fff,
- [WM8915_WRITE_SEQUENCER_376] = 0x2fff,
- [WM8915_WRITE_SEQUENCER_380] = 0x2fff,
- [WM8915_WRITE_SEQUENCER_384] = 0x60,
- [WM8915_WRITE_SEQUENCER_386] = 0x601,
- [WM8915_WRITE_SEQUENCER_388] = 0x61,
- [WM8915_WRITE_SEQUENCER_390] = 0x601,
- [WM8915_WRITE_SEQUENCER_392] = 0x50,
- [WM8915_WRITE_SEQUENCER_394] = 0x300,
- [WM8915_WRITE_SEQUENCER_396] = 0x1,
- [WM8915_WRITE_SEQUENCER_398] = 0x304,
- [WM8915_WRITE_SEQUENCER_400] = 0x40,
- [WM8915_WRITE_SEQUENCER_402] = 0xf,
- [WM8915_WRITE_SEQUENCER_404] = 0x1,
- [WM8915_WRITE_SEQUENCER_407] = 0x100,
-};
-
-static const DECLARE_TLV_DB_SCALE(inpga_tlv, 0, 100, 0);
-static const DECLARE_TLV_DB_SCALE(sidetone_tlv, -3600, 150, 0);
-static const DECLARE_TLV_DB_SCALE(digital_tlv, -7200, 75, 1);
-static const DECLARE_TLV_DB_SCALE(out_digital_tlv, -1200, 150, 0);
-static const DECLARE_TLV_DB_SCALE(out_tlv, -900, 75, 0);
-static const DECLARE_TLV_DB_SCALE(spk_tlv, -900, 150, 0);
-static const DECLARE_TLV_DB_SCALE(eq_tlv, -1200, 100, 0);
-
-static const char *sidetone_hpf_text[] = {
- "2.9kHz", "1.5kHz", "735Hz", "403Hz", "196Hz", "98Hz", "49Hz"
-};
-
-static const struct soc_enum sidetone_hpf =
- SOC_ENUM_SINGLE(WM8915_SIDETONE, 7, 6, sidetone_hpf_text);
-
-static const char *hpf_mode_text[] = {
- "HiFi", "Custom", "Voice"
-};
-
-static const struct soc_enum dsp1tx_hpf_mode =
- SOC_ENUM_SINGLE(WM8915_DSP1_TX_FILTERS, 3, 3, hpf_mode_text);
-
-static const struct soc_enum dsp2tx_hpf_mode =
- SOC_ENUM_SINGLE(WM8915_DSP2_TX_FILTERS, 3, 3, hpf_mode_text);
-
-static const char *hpf_cutoff_text[] = {
- "50Hz", "75Hz", "100Hz", "150Hz", "200Hz", "300Hz", "400Hz"
-};
-
-static const struct soc_enum dsp1tx_hpf_cutoff =
- SOC_ENUM_SINGLE(WM8915_DSP1_TX_FILTERS, 0, 7, hpf_cutoff_text);
-
-static const struct soc_enum dsp2tx_hpf_cutoff =
- SOC_ENUM_SINGLE(WM8915_DSP2_TX_FILTERS, 0, 7, hpf_cutoff_text);
-
-static void wm8915_set_retune_mobile(struct snd_soc_codec *codec, int block)
-{
- struct wm8915_priv *wm8915 = snd_soc_codec_get_drvdata(codec);
- struct wm8915_pdata *pdata = &wm8915->pdata;
- int base, best, best_val, save, i, cfg, iface;
-
- if (!wm8915->num_retune_mobile_texts)
- return;
-
- switch (block) {
- case 0:
- base = WM8915_DSP1_RX_EQ_GAINS_1;
- if (snd_soc_read(codec, WM8915_POWER_MANAGEMENT_8) &
- WM8915_DSP1RX_SRC)
- iface = 1;
- else
- iface = 0;
- break;
- case 1:
- base = WM8915_DSP1_RX_EQ_GAINS_2;
- if (snd_soc_read(codec, WM8915_POWER_MANAGEMENT_8) &
- WM8915_DSP2RX_SRC)
- iface = 1;
- else
- iface = 0;
- break;
- default:
- return;
- }
-
- /* Find the version of the currently selected configuration
- * with the nearest sample rate. */
- cfg = wm8915->retune_mobile_cfg[block];
- best = 0;
- best_val = INT_MAX;
- for (i = 0; i < pdata->num_retune_mobile_cfgs; i++) {
- if (strcmp(pdata->retune_mobile_cfgs[i].name,
- wm8915->retune_mobile_texts[cfg]) == 0 &&
- abs(pdata->retune_mobile_cfgs[i].rate
- - wm8915->rx_rate[iface]) < best_val) {
- best = i;
- best_val = abs(pdata->retune_mobile_cfgs[i].rate
- - wm8915->rx_rate[iface]);
- }
- }
-
- dev_dbg(codec->dev, "ReTune Mobile %d %s/%dHz for %dHz sample rate\n",
- block,
- pdata->retune_mobile_cfgs[best].name,
- pdata->retune_mobile_cfgs[best].rate,
- wm8915->rx_rate[iface]);
-
- /* The EQ will be disabled while reconfiguring it, remember the
- * current configuration.
- */
- save = snd_soc_read(codec, base);
- save &= WM8915_DSP1RX_EQ_ENA;
-
- for (i = 0; i < ARRAY_SIZE(pdata->retune_mobile_cfgs[best].regs); i++)
- snd_soc_update_bits(codec, base + i, 0xffff,
- pdata->retune_mobile_cfgs[best].regs[i]);
-
- snd_soc_update_bits(codec, base, WM8915_DSP1RX_EQ_ENA, save);
-}
-
-/* Icky as hell but saves code duplication */
-static int wm8915_get_retune_mobile_block(const char *name)
-{
- if (strcmp(name, "DSP1 EQ Mode") == 0)
- return 0;
- if (strcmp(name, "DSP2 EQ Mode") == 0)
- return 1;
- return -EINVAL;
-}
-
-static int wm8915_put_retune_mobile_enum(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
- struct wm8915_priv *wm8915 = snd_soc_codec_get_drvdata(codec);
- struct wm8915_pdata *pdata = &wm8915->pdata;
- int block = wm8915_get_retune_mobile_block(kcontrol->id.name);
- int value = ucontrol->value.integer.value[0];
-
- if (block < 0)
- return block;
-
- if (value >= pdata->num_retune_mobile_cfgs)
- return -EINVAL;
-
- wm8915->retune_mobile_cfg[block] = value;
-
- wm8915_set_retune_mobile(codec, block);
-
- return 0;
-}
-
-static int wm8915_get_retune_mobile_enum(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
- struct wm8915_priv *wm8915 = snd_soc_codec_get_drvdata(codec);
- int block = wm8915_get_retune_mobile_block(kcontrol->id.name);
-
- ucontrol->value.enumerated.item[0] = wm8915->retune_mobile_cfg[block];
-
- return 0;
-}
-
-static const struct snd_kcontrol_new wm8915_snd_controls[] = {
-SOC_DOUBLE_R_TLV("Capture Volume", WM8915_LEFT_LINE_INPUT_VOLUME,
- WM8915_RIGHT_LINE_INPUT_VOLUME, 0, 31, 0, inpga_tlv),
-SOC_DOUBLE_R("Capture ZC Switch", WM8915_LEFT_LINE_INPUT_VOLUME,
- WM8915_RIGHT_LINE_INPUT_VOLUME, 5, 1, 0),
-
-SOC_DOUBLE_TLV("DAC1 Sidetone Volume", WM8915_DAC1_MIXER_VOLUMES,
- 0, 5, 24, 0, sidetone_tlv),
-SOC_DOUBLE_TLV("DAC2 Sidetone Volume", WM8915_DAC2_MIXER_VOLUMES,
- 0, 5, 24, 0, sidetone_tlv),
-SOC_SINGLE("Sidetone LPF Switch", WM8915_SIDETONE, 12, 1, 0),
-SOC_ENUM("Sidetone HPF Cut-off", sidetone_hpf),
-SOC_SINGLE("Sidetone HPF Switch", WM8915_SIDETONE, 6, 1, 0),
-
-SOC_DOUBLE_R_TLV("DSP1 Capture Volume", WM8915_DSP1_TX_LEFT_VOLUME,
- WM8915_DSP1_TX_RIGHT_VOLUME, 1, 96, 0, digital_tlv),
-SOC_DOUBLE_R_TLV("DSP2 Capture Volume", WM8915_DSP2_TX_LEFT_VOLUME,
- WM8915_DSP2_TX_RIGHT_VOLUME, 1, 96, 0, digital_tlv),
-
-SOC_SINGLE("DSP1 Capture Notch Filter Switch", WM8915_DSP1_TX_FILTERS,
- 13, 1, 0),
-SOC_DOUBLE("DSP1 Capture HPF Switch", WM8915_DSP1_TX_FILTERS, 12, 11, 1, 0),
-SOC_ENUM("DSP1 Capture HPF Mode", dsp1tx_hpf_mode),
-SOC_ENUM("DSP1 Capture HPF Cutoff", dsp1tx_hpf_cutoff),
-
-SOC_SINGLE("DSP2 Capture Notch Filter Switch", WM8915_DSP2_TX_FILTERS,
- 13, 1, 0),
-SOC_DOUBLE("DSP2 Capture HPF Switch", WM8915_DSP2_TX_FILTERS, 12, 11, 1, 0),
-SOC_ENUM("DSP2 Capture HPF Mode", dsp2tx_hpf_mode),
-SOC_ENUM("DSP2 Capture HPF Cutoff", dsp2tx_hpf_cutoff),
-
-SOC_DOUBLE_R_TLV("DSP1 Playback Volume", WM8915_DSP1_RX_LEFT_VOLUME,
- WM8915_DSP1_RX_RIGHT_VOLUME, 1, 112, 0, digital_tlv),
-SOC_SINGLE("DSP1 Playback Switch", WM8915_DSP1_RX_FILTERS_1, 9, 1, 1),
-
-SOC_DOUBLE_R_TLV("DSP2 Playback Volume", WM8915_DSP2_RX_LEFT_VOLUME,
- WM8915_DSP2_RX_RIGHT_VOLUME, 1, 112, 0, digital_tlv),
-SOC_SINGLE("DSP2 Playback Switch", WM8915_DSP2_RX_FILTERS_1, 9, 1, 1),
-
-SOC_DOUBLE_R_TLV("DAC1 Volume", WM8915_DAC1_LEFT_VOLUME,
- WM8915_DAC1_RIGHT_VOLUME, 1, 112, 0, digital_tlv),
-SOC_DOUBLE_R("DAC1 Switch", WM8915_DAC1_LEFT_VOLUME,
- WM8915_DAC1_RIGHT_VOLUME, 9, 1, 1),
-
-SOC_DOUBLE_R_TLV("DAC2 Volume", WM8915_DAC2_LEFT_VOLUME,
- WM8915_DAC2_RIGHT_VOLUME, 1, 112, 0, digital_tlv),
-SOC_DOUBLE_R("DAC2 Switch", WM8915_DAC2_LEFT_VOLUME,
- WM8915_DAC2_RIGHT_VOLUME, 9, 1, 1),
-
-SOC_SINGLE("Speaker High Performance Switch", WM8915_OVERSAMPLING, 3, 1, 0),
-SOC_SINGLE("DMIC High Performance Switch", WM8915_OVERSAMPLING, 2, 1, 0),
-SOC_SINGLE("ADC High Performance Switch", WM8915_OVERSAMPLING, 1, 1, 0),
-SOC_SINGLE("DAC High Performance Switch", WM8915_OVERSAMPLING, 0, 1, 0),
-
-SOC_SINGLE("DAC Soft Mute Switch", WM8915_DAC_SOFTMUTE, 1, 1, 0),
-SOC_SINGLE("DAC Slow Soft Mute Switch", WM8915_DAC_SOFTMUTE, 0, 1, 0),
-
-SOC_DOUBLE_TLV("Digital Output 1 Volume", WM8915_DAC1_HPOUT1_VOLUME, 0, 4,
- 8, 0, out_digital_tlv),
-SOC_DOUBLE_TLV("Digital Output 2 Volume", WM8915_DAC2_HPOUT2_VOLUME, 0, 4,
- 8, 0, out_digital_tlv),
-
-SOC_DOUBLE_R_TLV("Output 1 Volume", WM8915_OUTPUT1_LEFT_VOLUME,
- WM8915_OUTPUT1_RIGHT_VOLUME, 0, 12, 0, out_tlv),
-SOC_DOUBLE_R("Output 1 ZC Switch", WM8915_OUTPUT1_LEFT_VOLUME,
- WM8915_OUTPUT1_RIGHT_VOLUME, 7, 1, 0),
-
-SOC_DOUBLE_R_TLV("Output 2 Volume", WM8915_OUTPUT2_LEFT_VOLUME,
- WM8915_OUTPUT2_RIGHT_VOLUME, 0, 12, 0, out_tlv),
-SOC_DOUBLE_R("Output 2 ZC Switch", WM8915_OUTPUT2_LEFT_VOLUME,
- WM8915_OUTPUT2_RIGHT_VOLUME, 7, 1, 0),
-
-SOC_DOUBLE_TLV("Speaker Volume", WM8915_PDM_SPEAKER_VOLUME, 0, 4, 8, 0,
- spk_tlv),
-SOC_DOUBLE_R("Speaker Switch", WM8915_LEFT_PDM_SPEAKER,
- WM8915_RIGHT_PDM_SPEAKER, 3, 1, 1),
-SOC_DOUBLE_R("Speaker ZC Switch", WM8915_LEFT_PDM_SPEAKER,
- WM8915_RIGHT_PDM_SPEAKER, 2, 1, 0),
-
-SOC_SINGLE("DSP1 EQ Switch", WM8915_DSP1_RX_EQ_GAINS_1, 0, 1, 0),
-SOC_SINGLE("DSP2 EQ Switch", WM8915_DSP2_RX_EQ_GAINS_1, 0, 1, 0),
-};
-
-static const struct snd_kcontrol_new wm8915_eq_controls[] = {
-SOC_SINGLE_TLV("DSP1 EQ B1 Volume", WM8915_DSP1_RX_EQ_GAINS_1, 11, 31, 0,
- eq_tlv),
-SOC_SINGLE_TLV("DSP1 EQ B2 Volume", WM8915_DSP1_RX_EQ_GAINS_1, 6, 31, 0,
- eq_tlv),
-SOC_SINGLE_TLV("DSP1 EQ B3 Volume", WM8915_DSP1_RX_EQ_GAINS_1, 1, 31, 0,
- eq_tlv),
-SOC_SINGLE_TLV("DSP1 EQ B4 Volume", WM8915_DSP1_RX_EQ_GAINS_2, 11, 31, 0,
- eq_tlv),
-SOC_SINGLE_TLV("DSP1 EQ B5 Volume", WM8915_DSP1_RX_EQ_GAINS_2, 6, 31, 0,
- eq_tlv),
-
-SOC_SINGLE_TLV("DSP2 EQ B1 Volume", WM8915_DSP2_RX_EQ_GAINS_1, 11, 31, 0,
- eq_tlv),
-SOC_SINGLE_TLV("DSP2 EQ B2 Volume", WM8915_DSP2_RX_EQ_GAINS_1, 6, 31, 0,
- eq_tlv),
-SOC_SINGLE_TLV("DSP2 EQ B3 Volume", WM8915_DSP2_RX_EQ_GAINS_1, 1, 31, 0,
- eq_tlv),
-SOC_SINGLE_TLV("DSP2 EQ B4 Volume", WM8915_DSP2_RX_EQ_GAINS_2, 11, 31, 0,
- eq_tlv),
-SOC_SINGLE_TLV("DSP2 EQ B5 Volume", WM8915_DSP2_RX_EQ_GAINS_2, 6, 31, 0,
- eq_tlv),
-};
-
-static int cp_event(struct snd_soc_dapm_widget *w,
- struct snd_kcontrol *kcontrol, int event)
-{
- switch (event) {
- case SND_SOC_DAPM_POST_PMU:
- msleep(5);
- break;
- default:
- BUG();
- return -EINVAL;
- }
-
- return 0;
-}
-
-static int rmv_short_event(struct snd_soc_dapm_widget *w,
- struct snd_kcontrol *kcontrol, int event)
-{
- struct wm8915_priv *wm8915 = snd_soc_codec_get_drvdata(w->codec);
-
- /* Record which outputs we enabled */
- switch (event) {
- case SND_SOC_DAPM_PRE_PMD:
- wm8915->hpout_pending &= ~w->shift;
- break;
- case SND_SOC_DAPM_PRE_PMU:
- wm8915->hpout_pending |= w->shift;
- break;
- default:
- BUG();
- return -EINVAL;
- }
-
- return 0;
-}
-
-static void wait_for_dc_servo(struct snd_soc_codec *codec, u16 mask)
-{
- struct i2c_client *i2c = to_i2c_client(codec->dev);
- struct wm8915_priv *wm8915 = snd_soc_codec_get_drvdata(codec);
- int i, ret;
- unsigned long timeout = 200;
-
- snd_soc_write(codec, WM8915_DC_SERVO_2, mask);
-
- /* Use the interrupt if possible */
- do {
- if (i2c->irq) {
- timeout = wait_for_completion_timeout(&wm8915->dcs_done,
- msecs_to_jiffies(200));
- if (timeout == 0)
- dev_err(codec->dev, "DC servo timed out\n");
-
- } else {
- msleep(1);
- if (--i) {
- timeout = 0;
- break;
- }
- }
-
- ret = snd_soc_read(codec, WM8915_DC_SERVO_2);
- dev_dbg(codec->dev, "DC servo state: %x\n", ret);
- } while (ret & mask);
-
- if (timeout == 0)
- dev_err(codec->dev, "DC servo timed out for %x\n", mask);
- else
- dev_dbg(codec->dev, "DC servo complete for %x\n", mask);
-}
-
-static void wm8915_seq_notifier(struct snd_soc_dapm_context *dapm,
- enum snd_soc_dapm_type event, int subseq)
-{
- struct snd_soc_codec *codec = container_of(dapm,
- struct snd_soc_codec, dapm);
- struct wm8915_priv *wm8915 = snd_soc_codec_get_drvdata(codec);
- u16 val, mask;
-
- /* Complete any pending DC servo starts */
- if (wm8915->dcs_pending) {
- dev_dbg(codec->dev, "Starting DC servo for %x\n",
- wm8915->dcs_pending);
-
- /* Trigger a startup sequence */
- wait_for_dc_servo(codec, wm8915->dcs_pending
- << WM8915_DCS_TRIG_STARTUP_0_SHIFT);
-
- wm8915->dcs_pending = 0;
- }
-
- if (wm8915->hpout_pending != wm8915->hpout_ena) {
- dev_dbg(codec->dev, "Applying RMV_SHORTs %x->%x\n",
- wm8915->hpout_ena, wm8915->hpout_pending);
-
- val = 0;
- mask = 0;
- if (wm8915->hpout_pending & HPOUT1L) {
- val |= WM8915_HPOUT1L_RMV_SHORT;
- mask |= WM8915_HPOUT1L_RMV_SHORT;
- } else {
- mask |= WM8915_HPOUT1L_RMV_SHORT |
- WM8915_HPOUT1L_OUTP |
- WM8915_HPOUT1L_DLY;
- }
-
- if (wm8915->hpout_pending & HPOUT1R) {
- val |= WM8915_HPOUT1R_RMV_SHORT;
- mask |= WM8915_HPOUT1R_RMV_SHORT;
- } else {
- mask |= WM8915_HPOUT1R_RMV_SHORT |
- WM8915_HPOUT1R_OUTP |
- WM8915_HPOUT1R_DLY;
- }
-
- snd_soc_update_bits(codec, WM8915_ANALOGUE_HP_1, mask, val);
-
- val = 0;
- mask = 0;
- if (wm8915->hpout_pending & HPOUT2L) {
- val |= WM8915_HPOUT2L_RMV_SHORT;
- mask |= WM8915_HPOUT2L_RMV_SHORT;
- } else {
- mask |= WM8915_HPOUT2L_RMV_SHORT |
- WM8915_HPOUT2L_OUTP |
- WM8915_HPOUT2L_DLY;
- }
-
- if (wm8915->hpout_pending & HPOUT2R) {
- val |= WM8915_HPOUT2R_RMV_SHORT;
- mask |= WM8915_HPOUT2R_RMV_SHORT;
- } else {
- mask |= WM8915_HPOUT2R_RMV_SHORT |
- WM8915_HPOUT2R_OUTP |
- WM8915_HPOUT2R_DLY;
- }
-
- snd_soc_update_bits(codec, WM8915_ANALOGUE_HP_2, mask, val);
-
- wm8915->hpout_ena = wm8915->hpout_pending;
- }
-}
-
-static int dcs_start(struct snd_soc_dapm_widget *w,
- struct snd_kcontrol *kcontrol, int event)
-{
- struct wm8915_priv *wm8915 = snd_soc_codec_get_drvdata(w->codec);
-
- switch (event) {
- case SND_SOC_DAPM_POST_PMU:
- wm8915->dcs_pending |= 1 << w->shift;
- break;
- default:
- BUG();
- return -EINVAL;
- }
-
- return 0;
-}
-
-static const char *sidetone_text[] = {
- "IN1", "IN2",
-};
-
-static const struct soc_enum left_sidetone_enum =
- SOC_ENUM_SINGLE(WM8915_SIDETONE, 0, 2, sidetone_text);
-
-static const struct snd_kcontrol_new left_sidetone =
- SOC_DAPM_ENUM("Left Sidetone", left_sidetone_enum);
-
-static const struct soc_enum right_sidetone_enum =
- SOC_ENUM_SINGLE(WM8915_SIDETONE, 1, 2, sidetone_text);
-
-static const struct snd_kcontrol_new right_sidetone =
- SOC_DAPM_ENUM("Right Sidetone", right_sidetone_enum);
-
-static const char *spk_text[] = {
- "DAC1L", "DAC1R", "DAC2L", "DAC2R"
-};
-
-static const struct soc_enum spkl_enum =
- SOC_ENUM_SINGLE(WM8915_LEFT_PDM_SPEAKER, 0, 4, spk_text);
-
-static const struct snd_kcontrol_new spkl_mux =
- SOC_DAPM_ENUM("SPKL", spkl_enum);
-
-static const struct soc_enum spkr_enum =
- SOC_ENUM_SINGLE(WM8915_RIGHT_PDM_SPEAKER, 0, 4, spk_text);
-
-static const struct snd_kcontrol_new spkr_mux =
- SOC_DAPM_ENUM("SPKR", spkr_enum);
-
-static const char *dsp1rx_text[] = {
- "AIF1", "AIF2"
-};
-
-static const struct soc_enum dsp1rx_enum =
- SOC_ENUM_SINGLE(WM8915_POWER_MANAGEMENT_8, 0, 2, dsp1rx_text);
-
-static const struct snd_kcontrol_new dsp1rx =
- SOC_DAPM_ENUM("DSP1RX", dsp1rx_enum);
-
-static const char *dsp2rx_text[] = {
- "AIF2", "AIF1"
-};
-
-static const struct soc_enum dsp2rx_enum =
- SOC_ENUM_SINGLE(WM8915_POWER_MANAGEMENT_8, 4, 2, dsp2rx_text);
-
-static const struct snd_kcontrol_new dsp2rx =
- SOC_DAPM_ENUM("DSP2RX", dsp2rx_enum);
-
-static const char *aif2tx_text[] = {
- "DSP2", "DSP1", "AIF1"
-};
-
-static const struct soc_enum aif2tx_enum =
- SOC_ENUM_SINGLE(WM8915_POWER_MANAGEMENT_8, 6, 3, aif2tx_text);
-
-static const struct snd_kcontrol_new aif2tx =
- SOC_DAPM_ENUM("AIF2TX", aif2tx_enum);
-
-static const char *inmux_text[] = {
- "ADC", "DMIC1", "DMIC2"
-};
-
-static const struct soc_enum in1_enum =
- SOC_ENUM_SINGLE(WM8915_POWER_MANAGEMENT_7, 0, 3, inmux_text);
-
-static const struct snd_kcontrol_new in1_mux =
- SOC_DAPM_ENUM("IN1 Mux", in1_enum);
-
-static const struct soc_enum in2_enum =
- SOC_ENUM_SINGLE(WM8915_POWER_MANAGEMENT_7, 4, 3, inmux_text);
-
-static const struct snd_kcontrol_new in2_mux =
- SOC_DAPM_ENUM("IN2 Mux", in2_enum);
-
-static const struct snd_kcontrol_new dac2r_mix[] = {
-SOC_DAPM_SINGLE("Right Sidetone Switch", WM8915_DAC2_RIGHT_MIXER_ROUTING,
- 5, 1, 0),
-SOC_DAPM_SINGLE("Left Sidetone Switch", WM8915_DAC2_RIGHT_MIXER_ROUTING,
- 4, 1, 0),
-SOC_DAPM_SINGLE("DSP2 Switch", WM8915_DAC2_RIGHT_MIXER_ROUTING, 1, 1, 0),
-SOC_DAPM_SINGLE("DSP1 Switch", WM8915_DAC2_RIGHT_MIXER_ROUTING, 0, 1, 0),
-};
-
-static const struct snd_kcontrol_new dac2l_mix[] = {
-SOC_DAPM_SINGLE("Right Sidetone Switch", WM8915_DAC2_LEFT_MIXER_ROUTING,
- 5, 1, 0),
-SOC_DAPM_SINGLE("Left Sidetone Switch", WM8915_DAC2_LEFT_MIXER_ROUTING,
- 4, 1, 0),
-SOC_DAPM_SINGLE("DSP2 Switch", WM8915_DAC2_LEFT_MIXER_ROUTING, 1, 1, 0),
-SOC_DAPM_SINGLE("DSP1 Switch", WM8915_DAC2_LEFT_MIXER_ROUTING, 0, 1, 0),
-};
-
-static const struct snd_kcontrol_new dac1r_mix[] = {
-SOC_DAPM_SINGLE("Right Sidetone Switch", WM8915_DAC1_RIGHT_MIXER_ROUTING,
- 5, 1, 0),
-SOC_DAPM_SINGLE("Left Sidetone Switch", WM8915_DAC1_RIGHT_MIXER_ROUTING,
- 4, 1, 0),
-SOC_DAPM_SINGLE("DSP2 Switch", WM8915_DAC1_RIGHT_MIXER_ROUTING, 1, 1, 0),
-SOC_DAPM_SINGLE("DSP1 Switch", WM8915_DAC1_RIGHT_MIXER_ROUTING, 0, 1, 0),
-};
-
-static const struct snd_kcontrol_new dac1l_mix[] = {
-SOC_DAPM_SINGLE("Right Sidetone Switch", WM8915_DAC1_LEFT_MIXER_ROUTING,
- 5, 1, 0),
-SOC_DAPM_SINGLE("Left Sidetone Switch", WM8915_DAC1_LEFT_MIXER_ROUTING,
- 4, 1, 0),
-SOC_DAPM_SINGLE("DSP2 Switch", WM8915_DAC1_LEFT_MIXER_ROUTING, 1, 1, 0),
-SOC_DAPM_SINGLE("DSP1 Switch", WM8915_DAC1_LEFT_MIXER_ROUTING, 0, 1, 0),
-};
-
-static const struct snd_kcontrol_new dsp1txl[] = {
-SOC_DAPM_SINGLE("IN1 Switch", WM8915_DSP1_TX_LEFT_MIXER_ROUTING,
- 1, 1, 0),
-SOC_DAPM_SINGLE("DAC Switch", WM8915_DSP1_TX_LEFT_MIXER_ROUTING,
- 0, 1, 0),
-};
-
-static const struct snd_kcontrol_new dsp1txr[] = {
-SOC_DAPM_SINGLE("IN1 Switch", WM8915_DSP1_TX_RIGHT_MIXER_ROUTING,
- 1, 1, 0),
-SOC_DAPM_SINGLE("DAC Switch", WM8915_DSP1_TX_RIGHT_MIXER_ROUTING,
- 0, 1, 0),
-};
-
-static const struct snd_kcontrol_new dsp2txl[] = {
-SOC_DAPM_SINGLE("IN1 Switch", WM8915_DSP2_TX_LEFT_MIXER_ROUTING,
- 1, 1, 0),
-SOC_DAPM_SINGLE("DAC Switch", WM8915_DSP2_TX_LEFT_MIXER_ROUTING,
- 0, 1, 0),
-};
-
-static const struct snd_kcontrol_new dsp2txr[] = {
-SOC_DAPM_SINGLE("IN1 Switch", WM8915_DSP2_TX_RIGHT_MIXER_ROUTING,
- 1, 1, 0),
-SOC_DAPM_SINGLE("DAC Switch", WM8915_DSP2_TX_RIGHT_MIXER_ROUTING,
- 0, 1, 0),
-};
-
-
-static const struct snd_soc_dapm_widget wm8915_dapm_widgets[] = {
-SND_SOC_DAPM_INPUT("IN1LN"),
-SND_SOC_DAPM_INPUT("IN1LP"),
-SND_SOC_DAPM_INPUT("IN1RN"),
-SND_SOC_DAPM_INPUT("IN1RP"),
-
-SND_SOC_DAPM_INPUT("IN2LN"),
-SND_SOC_DAPM_INPUT("IN2LP"),
-SND_SOC_DAPM_INPUT("IN2RN"),
-SND_SOC_DAPM_INPUT("IN2RP"),
-
-SND_SOC_DAPM_INPUT("DMIC1DAT"),
-SND_SOC_DAPM_INPUT("DMIC2DAT"),
-
-SND_SOC_DAPM_SUPPLY_S("SYSCLK", 1, WM8915_AIF_CLOCKING_1, 0, 0, NULL, 0),
-SND_SOC_DAPM_SUPPLY_S("SYSDSPCLK", 2, WM8915_CLOCKING_1, 1, 0, NULL, 0),
-SND_SOC_DAPM_SUPPLY_S("AIFCLK", 2, WM8915_CLOCKING_1, 2, 0, NULL, 0),
-SND_SOC_DAPM_SUPPLY_S("Charge Pump", 2, WM8915_CHARGE_PUMP_1, 15, 0, cp_event,
- SND_SOC_DAPM_POST_PMU),
-
-SND_SOC_DAPM_SUPPLY("LDO2", WM8915_POWER_MANAGEMENT_2, 1, 0, NULL, 0),
-SND_SOC_DAPM_MICBIAS("MICB2", WM8915_POWER_MANAGEMENT_1, 9, 0),
-SND_SOC_DAPM_MICBIAS("MICB1", WM8915_POWER_MANAGEMENT_1, 8, 0),
-
-SND_SOC_DAPM_PGA("IN1L PGA", WM8915_POWER_MANAGEMENT_2, 5, 0, NULL, 0),
-SND_SOC_DAPM_PGA("IN1R PGA", WM8915_POWER_MANAGEMENT_2, 4, 0, NULL, 0),
-
-SND_SOC_DAPM_MUX("IN1L Mux", SND_SOC_NOPM, 0, 0, &in1_mux),
-SND_SOC_DAPM_MUX("IN1R Mux", SND_SOC_NOPM, 0, 0, &in1_mux),
-SND_SOC_DAPM_MUX("IN2L Mux", SND_SOC_NOPM, 0, 0, &in2_mux),
-SND_SOC_DAPM_MUX("IN2R Mux", SND_SOC_NOPM, 0, 0, &in2_mux),
-
-SND_SOC_DAPM_PGA("IN1L", WM8915_POWER_MANAGEMENT_7, 2, 0, NULL, 0),
-SND_SOC_DAPM_PGA("IN1R", WM8915_POWER_MANAGEMENT_7, 3, 0, NULL, 0),
-SND_SOC_DAPM_PGA("IN2L", WM8915_POWER_MANAGEMENT_7, 6, 0, NULL, 0),
-SND_SOC_DAPM_PGA("IN2R", WM8915_POWER_MANAGEMENT_7, 7, 0, NULL, 0),
-
-SND_SOC_DAPM_SUPPLY("DMIC2", WM8915_POWER_MANAGEMENT_7, 9, 0, NULL, 0),
-SND_SOC_DAPM_SUPPLY("DMIC1", WM8915_POWER_MANAGEMENT_7, 8, 0, NULL, 0),
-
-SND_SOC_DAPM_ADC("DMIC2L", NULL, WM8915_POWER_MANAGEMENT_3, 5, 0),
-SND_SOC_DAPM_ADC("DMIC2R", NULL, WM8915_POWER_MANAGEMENT_3, 4, 0),
-SND_SOC_DAPM_ADC("DMIC1L", NULL, WM8915_POWER_MANAGEMENT_3, 3, 0),
-SND_SOC_DAPM_ADC("DMIC1R", NULL, WM8915_POWER_MANAGEMENT_3, 2, 0),
-
-SND_SOC_DAPM_ADC("ADCL", NULL, WM8915_POWER_MANAGEMENT_3, 1, 0),
-SND_SOC_DAPM_ADC("ADCR", NULL, WM8915_POWER_MANAGEMENT_3, 0, 0),
-
-SND_SOC_DAPM_MUX("Left Sidetone", SND_SOC_NOPM, 0, 0, &left_sidetone),
-SND_SOC_DAPM_MUX("Right Sidetone", SND_SOC_NOPM, 0, 0, &right_sidetone),
-
-SND_SOC_DAPM_AIF_IN("DSP2RXL", NULL, 0, WM8915_POWER_MANAGEMENT_3, 11, 0),
-SND_SOC_DAPM_AIF_IN("DSP2RXR", NULL, 1, WM8915_POWER_MANAGEMENT_3, 10, 0),
-SND_SOC_DAPM_AIF_IN("DSP1RXL", NULL, 0, WM8915_POWER_MANAGEMENT_3, 9, 0),
-SND_SOC_DAPM_AIF_IN("DSP1RXR", NULL, 1, WM8915_POWER_MANAGEMENT_3, 8, 0),
-
-SND_SOC_DAPM_MIXER("DSP2TXL", WM8915_POWER_MANAGEMENT_5, 11, 0,
- dsp2txl, ARRAY_SIZE(dsp2txl)),
-SND_SOC_DAPM_MIXER("DSP2TXR", WM8915_POWER_MANAGEMENT_5, 10, 0,
- dsp2txr, ARRAY_SIZE(dsp2txr)),
-SND_SOC_DAPM_MIXER("DSP1TXL", WM8915_POWER_MANAGEMENT_5, 9, 0,
- dsp1txl, ARRAY_SIZE(dsp1txl)),
-SND_SOC_DAPM_MIXER("DSP1TXR", WM8915_POWER_MANAGEMENT_5, 8, 0,
- dsp1txr, ARRAY_SIZE(dsp1txr)),
-
-SND_SOC_DAPM_MIXER("DAC2L Mixer", SND_SOC_NOPM, 0, 0,
- dac2l_mix, ARRAY_SIZE(dac2l_mix)),
-SND_SOC_DAPM_MIXER("DAC2R Mixer", SND_SOC_NOPM, 0, 0,
- dac2r_mix, ARRAY_SIZE(dac2r_mix)),
-SND_SOC_DAPM_MIXER("DAC1L Mixer", SND_SOC_NOPM, 0, 0,
- dac1l_mix, ARRAY_SIZE(dac1l_mix)),
-SND_SOC_DAPM_MIXER("DAC1R Mixer", SND_SOC_NOPM, 0, 0,
- dac1r_mix, ARRAY_SIZE(dac1r_mix)),
-
-SND_SOC_DAPM_DAC("DAC2L", NULL, WM8915_POWER_MANAGEMENT_5, 3, 0),
-SND_SOC_DAPM_DAC("DAC2R", NULL, WM8915_POWER_MANAGEMENT_5, 2, 0),
-SND_SOC_DAPM_DAC("DAC1L", NULL, WM8915_POWER_MANAGEMENT_5, 1, 0),
-SND_SOC_DAPM_DAC("DAC1R", NULL, WM8915_POWER_MANAGEMENT_5, 0, 0),
-
-SND_SOC_DAPM_AIF_IN("AIF2RX1", "AIF2 Playback", 1,
- WM8915_POWER_MANAGEMENT_4, 9, 0),
-SND_SOC_DAPM_AIF_IN("AIF2RX0", "AIF2 Playback", 2,
- WM8915_POWER_MANAGEMENT_4, 8, 0),
-
-SND_SOC_DAPM_AIF_IN("AIF2TX1", "AIF2 Capture", 1,
- WM8915_POWER_MANAGEMENT_6, 9, 0),
-SND_SOC_DAPM_AIF_IN("AIF2TX0", "AIF2 Capture", 2,
- WM8915_POWER_MANAGEMENT_6, 8, 0),
-
-SND_SOC_DAPM_AIF_IN("AIF1RX5", "AIF1 Playback", 5,
- WM8915_POWER_MANAGEMENT_4, 5, 0),
-SND_SOC_DAPM_AIF_IN("AIF1RX4", "AIF1 Playback", 4,
- WM8915_POWER_MANAGEMENT_4, 4, 0),
-SND_SOC_DAPM_AIF_IN("AIF1RX3", "AIF1 Playback", 3,
- WM8915_POWER_MANAGEMENT_4, 3, 0),
-SND_SOC_DAPM_AIF_IN("AIF1RX2", "AIF1 Playback", 2,
- WM8915_POWER_MANAGEMENT_4, 2, 0),
-SND_SOC_DAPM_AIF_IN("AIF1RX1", "AIF1 Playback", 1,
- WM8915_POWER_MANAGEMENT_4, 1, 0),
-SND_SOC_DAPM_AIF_IN("AIF1RX0", "AIF1 Playback", 0,
- WM8915_POWER_MANAGEMENT_4, 0, 0),
-
-SND_SOC_DAPM_AIF_OUT("AIF1TX5", "AIF1 Capture", 5,
- WM8915_POWER_MANAGEMENT_6, 5, 0),
-SND_SOC_DAPM_AIF_OUT("AIF1TX4", "AIF1 Capture", 4,
- WM8915_POWER_MANAGEMENT_6, 4, 0),
-SND_SOC_DAPM_AIF_OUT("AIF1TX3", "AIF1 Capture", 3,
- WM8915_POWER_MANAGEMENT_6, 3, 0),
-SND_SOC_DAPM_AIF_OUT("AIF1TX2", "AIF1 Capture", 2,
- WM8915_POWER_MANAGEMENT_6, 2, 0),
-SND_SOC_DAPM_AIF_OUT("AIF1TX1", "AIF1 Capture", 1,
- WM8915_POWER_MANAGEMENT_6, 1, 0),
-SND_SOC_DAPM_AIF_OUT("AIF1TX0", "AIF1 Capture", 0,
- WM8915_POWER_MANAGEMENT_6, 0, 0),
-
-/* We route as stereo pairs so define some dummy widgets to squash
- * things down for now. RXA = 0,1, RXB = 2,3 and so on */
-SND_SOC_DAPM_PGA("AIF1RXA", SND_SOC_NOPM, 0, 0, NULL, 0),
-SND_SOC_DAPM_PGA("AIF1RXB", SND_SOC_NOPM, 0, 0, NULL, 0),
-SND_SOC_DAPM_PGA("AIF1RXC", SND_SOC_NOPM, 0, 0, NULL, 0),
-SND_SOC_DAPM_PGA("AIF2RX", SND_SOC_NOPM, 0, 0, NULL, 0),
-SND_SOC_DAPM_PGA("DSP2TX", SND_SOC_NOPM, 0, 0, NULL, 0),
-
-SND_SOC_DAPM_MUX("DSP1RX", SND_SOC_NOPM, 0, 0, &dsp1rx),
-SND_SOC_DAPM_MUX("DSP2RX", SND_SOC_NOPM, 0, 0, &dsp2rx),
-SND_SOC_DAPM_MUX("AIF2TX", SND_SOC_NOPM, 0, 0, &aif2tx),
-
-SND_SOC_DAPM_MUX("SPKL", SND_SOC_NOPM, 0, 0, &spkl_mux),
-SND_SOC_DAPM_MUX("SPKR", SND_SOC_NOPM, 0, 0, &spkr_mux),
-SND_SOC_DAPM_PGA("SPKL PGA", WM8915_LEFT_PDM_SPEAKER, 4, 0, NULL, 0),
-SND_SOC_DAPM_PGA("SPKR PGA", WM8915_RIGHT_PDM_SPEAKER, 4, 0, NULL, 0),
-
-SND_SOC_DAPM_PGA_S("HPOUT2L PGA", 0, WM8915_POWER_MANAGEMENT_1, 7, 0, NULL, 0),
-SND_SOC_DAPM_PGA_S("HPOUT2L_DLY", 1, WM8915_ANALOGUE_HP_2, 5, 0, NULL, 0),
-SND_SOC_DAPM_PGA_S("HPOUT2L_DCS", 2, WM8915_DC_SERVO_1, 2, 0, dcs_start,
- SND_SOC_DAPM_POST_PMU),
-SND_SOC_DAPM_PGA_S("HPOUT2L_OUTP", 3, WM8915_ANALOGUE_HP_2, 6, 0, NULL, 0),
-SND_SOC_DAPM_PGA_S("HPOUT2L_RMV_SHORT", 3, SND_SOC_NOPM, HPOUT2L, 0,
- rmv_short_event,
- SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_PRE_PMD),
-
-SND_SOC_DAPM_PGA_S("HPOUT2R PGA", 0, WM8915_POWER_MANAGEMENT_1, 6, 0,NULL, 0),
-SND_SOC_DAPM_PGA_S("HPOUT2R_DLY", 1, WM8915_ANALOGUE_HP_2, 1, 0, NULL, 0),
-SND_SOC_DAPM_PGA_S("HPOUT2R_DCS", 2, WM8915_DC_SERVO_1, 3, 0, dcs_start,
- SND_SOC_DAPM_POST_PMU),
-SND_SOC_DAPM_PGA_S("HPOUT2R_OUTP", 3, WM8915_ANALOGUE_HP_2, 2, 0, NULL, 0),
-SND_SOC_DAPM_PGA_S("HPOUT2R_RMV_SHORT", 3, SND_SOC_NOPM, HPOUT2R, 0,
- rmv_short_event,
- SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_PRE_PMD),
-
-SND_SOC_DAPM_PGA_S("HPOUT1L PGA", 0, WM8915_POWER_MANAGEMENT_1, 5, 0, NULL, 0),
-SND_SOC_DAPM_PGA_S("HPOUT1L_DLY", 1, WM8915_ANALOGUE_HP_1, 5, 0, NULL, 0),
-SND_SOC_DAPM_PGA_S("HPOUT1L_DCS", 2, WM8915_DC_SERVO_1, 0, 0, dcs_start,
- SND_SOC_DAPM_POST_PMU),
-SND_SOC_DAPM_PGA_S("HPOUT1L_OUTP", 3, WM8915_ANALOGUE_HP_1, 6, 0, NULL, 0),
-SND_SOC_DAPM_PGA_S("HPOUT1L_RMV_SHORT", 3, SND_SOC_NOPM, HPOUT1L, 0,
- rmv_short_event,
- SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_PRE_PMD),
-
-SND_SOC_DAPM_PGA_S("HPOUT1R PGA", 0, WM8915_POWER_MANAGEMENT_1, 4, 0, NULL, 0),
-SND_SOC_DAPM_PGA_S("HPOUT1R_DLY", 1, WM8915_ANALOGUE_HP_1, 1, 0, NULL, 0),
-SND_SOC_DAPM_PGA_S("HPOUT1R_DCS", 2, WM8915_DC_SERVO_1, 1, 0, dcs_start,
- SND_SOC_DAPM_POST_PMU),
-SND_SOC_DAPM_PGA_S("HPOUT1R_OUTP", 3, WM8915_ANALOGUE_HP_1, 2, 0, NULL, 0),
-SND_SOC_DAPM_PGA_S("HPOUT1R_RMV_SHORT", 3, SND_SOC_NOPM, HPOUT1R, 0,
- rmv_short_event,
- SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_PRE_PMD),
-
-SND_SOC_DAPM_OUTPUT("HPOUT1L"),
-SND_SOC_DAPM_OUTPUT("HPOUT1R"),
-SND_SOC_DAPM_OUTPUT("HPOUT2L"),
-SND_SOC_DAPM_OUTPUT("HPOUT2R"),
-SND_SOC_DAPM_OUTPUT("SPKDAT"),
-};
-
-static const struct snd_soc_dapm_route wm8915_dapm_routes[] = {
- { "AIFCLK", NULL, "SYSCLK" },
- { "SYSDSPCLK", NULL, "SYSCLK" },
- { "Charge Pump", NULL, "SYSCLK" },
-
- { "MICB1", NULL, "LDO2" },
- { "MICB2", NULL, "LDO2" },
-
- { "IN1L PGA", NULL, "IN2LN" },
- { "IN1L PGA", NULL, "IN2LP" },
- { "IN1L PGA", NULL, "IN1LN" },
- { "IN1L PGA", NULL, "IN1LP" },
-
- { "IN1R PGA", NULL, "IN2RN" },
- { "IN1R PGA", NULL, "IN2RP" },
- { "IN1R PGA", NULL, "IN1RN" },
- { "IN1R PGA", NULL, "IN1RP" },
-
- { "ADCL", NULL, "IN1L PGA" },
-
- { "ADCR", NULL, "IN1R PGA" },
-
- { "DMIC1L", NULL, "DMIC1DAT" },
- { "DMIC1R", NULL, "DMIC1DAT" },
- { "DMIC2L", NULL, "DMIC2DAT" },
- { "DMIC2R", NULL, "DMIC2DAT" },
-
- { "DMIC2L", NULL, "DMIC2" },
- { "DMIC2R", NULL, "DMIC2" },
- { "DMIC1L", NULL, "DMIC1" },
- { "DMIC1R", NULL, "DMIC1" },
-
- { "IN1L Mux", "ADC", "ADCL" },
- { "IN1L Mux", "DMIC1", "DMIC1L" },
- { "IN1L Mux", "DMIC2", "DMIC2L" },
-
- { "IN1R Mux", "ADC", "ADCR" },
- { "IN1R Mux", "DMIC1", "DMIC1R" },
- { "IN1R Mux", "DMIC2", "DMIC2R" },
-
- { "IN2L Mux", "ADC", "ADCL" },
- { "IN2L Mux", "DMIC1", "DMIC1L" },
- { "IN2L Mux", "DMIC2", "DMIC2L" },
-
- { "IN2R Mux", "ADC", "ADCR" },
- { "IN2R Mux", "DMIC1", "DMIC1R" },
- { "IN2R Mux", "DMIC2", "DMIC2R" },
-
- { "Left Sidetone", "IN1", "IN1L Mux" },
- { "Left Sidetone", "IN2", "IN2L Mux" },
-
- { "Right Sidetone", "IN1", "IN1R Mux" },
- { "Right Sidetone", "IN2", "IN2R Mux" },
-
- { "DSP1TXL", "IN1 Switch", "IN1L Mux" },
- { "DSP1TXR", "IN1 Switch", "IN1R Mux" },
-
- { "DSP2TXL", "IN1 Switch", "IN2L Mux" },
- { "DSP2TXR", "IN1 Switch", "IN2R Mux" },
-
- { "AIF1TX0", NULL, "DSP1TXL" },
- { "AIF1TX1", NULL, "DSP1TXR" },
- { "AIF1TX2", NULL, "DSP2TXL" },
- { "AIF1TX3", NULL, "DSP2TXR" },
- { "AIF1TX4", NULL, "AIF2RX0" },
- { "AIF1TX5", NULL, "AIF2RX1" },
-
- { "AIF1RX0", NULL, "AIFCLK" },
- { "AIF1RX1", NULL, "AIFCLK" },
- { "AIF1RX2", NULL, "AIFCLK" },
- { "AIF1RX3", NULL, "AIFCLK" },
- { "AIF1RX4", NULL, "AIFCLK" },
- { "AIF1RX5", NULL, "AIFCLK" },
-
- { "AIF2RX0", NULL, "AIFCLK" },
- { "AIF2RX1", NULL, "AIFCLK" },
-
- { "DSP1RXL", NULL, "SYSDSPCLK" },
- { "DSP1RXR", NULL, "SYSDSPCLK" },
- { "DSP2RXL", NULL, "SYSDSPCLK" },
- { "DSP2RXR", NULL, "SYSDSPCLK" },
- { "DSP1TXL", NULL, "SYSDSPCLK" },
- { "DSP1TXR", NULL, "SYSDSPCLK" },
- { "DSP2TXL", NULL, "SYSDSPCLK" },
- { "DSP2TXR", NULL, "SYSDSPCLK" },
-
- { "AIF1RXA", NULL, "AIF1RX0" },
- { "AIF1RXA", NULL, "AIF1RX1" },
- { "AIF1RXB", NULL, "AIF1RX2" },
- { "AIF1RXB", NULL, "AIF1RX3" },
- { "AIF1RXC", NULL, "AIF1RX4" },
- { "AIF1RXC", NULL, "AIF1RX5" },
-
- { "AIF2RX", NULL, "AIF2RX0" },
- { "AIF2RX", NULL, "AIF2RX1" },
-
- { "AIF2TX", "DSP2", "DSP2TX" },
- { "AIF2TX", "DSP1", "DSP1RX" },
- { "AIF2TX", "AIF1", "AIF1RXC" },
-
- { "DSP1RXL", NULL, "DSP1RX" },
- { "DSP1RXR", NULL, "DSP1RX" },
- { "DSP2RXL", NULL, "DSP2RX" },
- { "DSP2RXR", NULL, "DSP2RX" },
-
- { "DSP2TX", NULL, "DSP2TXL" },
- { "DSP2TX", NULL, "DSP2TXR" },
-
- { "DSP1RX", "AIF1", "AIF1RXA" },
- { "DSP1RX", "AIF2", "AIF2RX" },
-
- { "DSP2RX", "AIF1", "AIF1RXB" },
- { "DSP2RX", "AIF2", "AIF2RX" },
-
- { "DAC2L Mixer", "DSP2 Switch", "DSP2RXL" },
- { "DAC2L Mixer", "DSP1 Switch", "DSP1RXL" },
- { "DAC2L Mixer", "Right Sidetone Switch", "Right Sidetone" },
- { "DAC2L Mixer", "Left Sidetone Switch", "Left Sidetone" },
-
- { "DAC2R Mixer", "DSP2 Switch", "DSP2RXR" },
- { "DAC2R Mixer", "DSP1 Switch", "DSP1RXR" },
- { "DAC2R Mixer", "Right Sidetone Switch", "Right Sidetone" },
- { "DAC2R Mixer", "Left Sidetone Switch", "Left Sidetone" },
-
- { "DAC1L Mixer", "DSP2 Switch", "DSP2RXL" },
- { "DAC1L Mixer", "DSP1 Switch", "DSP1RXL" },
- { "DAC1L Mixer", "Right Sidetone Switch", "Right Sidetone" },
- { "DAC1L Mixer", "Left Sidetone Switch", "Left Sidetone" },
-
- { "DAC1R Mixer", "DSP2 Switch", "DSP2RXR" },
- { "DAC1R Mixer", "DSP1 Switch", "DSP1RXR" },
- { "DAC1R Mixer", "Right Sidetone Switch", "Right Sidetone" },
- { "DAC1R Mixer", "Left Sidetone Switch", "Left Sidetone" },
-
- { "DAC1L", NULL, "DAC1L Mixer" },
- { "DAC1R", NULL, "DAC1R Mixer" },
- { "DAC2L", NULL, "DAC2L Mixer" },
- { "DAC2R", NULL, "DAC2R Mixer" },
-
- { "HPOUT2L PGA", NULL, "Charge Pump" },
- { "HPOUT2L PGA", NULL, "DAC2L" },
- { "HPOUT2L_DLY", NULL, "HPOUT2L PGA" },
- { "HPOUT2L_DCS", NULL, "HPOUT2L_DLY" },
- { "HPOUT2L_OUTP", NULL, "HPOUT2L_DCS" },
- { "HPOUT2L_RMV_SHORT", NULL, "HPOUT2L_OUTP" },
-
- { "HPOUT2R PGA", NULL, "Charge Pump" },
- { "HPOUT2R PGA", NULL, "DAC2R" },
- { "HPOUT2R_DLY", NULL, "HPOUT2R PGA" },
- { "HPOUT2R_DCS", NULL, "HPOUT2R_DLY" },
- { "HPOUT2R_OUTP", NULL, "HPOUT2R_DCS" },
- { "HPOUT2R_RMV_SHORT", NULL, "HPOUT2R_OUTP" },
-
- { "HPOUT1L PGA", NULL, "Charge Pump" },
- { "HPOUT1L PGA", NULL, "DAC1L" },
- { "HPOUT1L_DLY", NULL, "HPOUT1L PGA" },
- { "HPOUT1L_DCS", NULL, "HPOUT1L_DLY" },
- { "HPOUT1L_OUTP", NULL, "HPOUT1L_DCS" },
- { "HPOUT1L_RMV_SHORT", NULL, "HPOUT1L_OUTP" },
-
- { "HPOUT1R PGA", NULL, "Charge Pump" },
- { "HPOUT1R PGA", NULL, "DAC1R" },
- { "HPOUT1R_DLY", NULL, "HPOUT1R PGA" },
- { "HPOUT1R_DCS", NULL, "HPOUT1R_DLY" },
- { "HPOUT1R_OUTP", NULL, "HPOUT1R_DCS" },
- { "HPOUT1R_RMV_SHORT", NULL, "HPOUT1R_OUTP" },
-
- { "HPOUT2L", NULL, "HPOUT2L_RMV_SHORT" },
- { "HPOUT2R", NULL, "HPOUT2R_RMV_SHORT" },
- { "HPOUT1L", NULL, "HPOUT1L_RMV_SHORT" },
- { "HPOUT1R", NULL, "HPOUT1R_RMV_SHORT" },
-
- { "SPKL", "DAC1L", "DAC1L" },
- { "SPKL", "DAC1R", "DAC1R" },
- { "SPKL", "DAC2L", "DAC2L" },
- { "SPKL", "DAC2R", "DAC2R" },
-
- { "SPKR", "DAC1L", "DAC1L" },
- { "SPKR", "DAC1R", "DAC1R" },
- { "SPKR", "DAC2L", "DAC2L" },
- { "SPKR", "DAC2R", "DAC2R" },
-
- { "SPKL PGA", NULL, "SPKL" },
- { "SPKR PGA", NULL, "SPKR" },
-
- { "SPKDAT", NULL, "SPKL PGA" },
- { "SPKDAT", NULL, "SPKR PGA" },
-};
-
-static int wm8915_readable_register(struct snd_soc_codec *codec,
- unsigned int reg)
-{
- /* Due to the sparseness of the register map the compiler
- * output from an explicit switch statement ends up being much
- * more efficient than a table.
- */
- switch (reg) {
- case WM8915_SOFTWARE_RESET:
- case WM8915_POWER_MANAGEMENT_1:
- case WM8915_POWER_MANAGEMENT_2:
- case WM8915_POWER_MANAGEMENT_3:
- case WM8915_POWER_MANAGEMENT_4:
- case WM8915_POWER_MANAGEMENT_5:
- case WM8915_POWER_MANAGEMENT_6:
- case WM8915_POWER_MANAGEMENT_7:
- case WM8915_POWER_MANAGEMENT_8:
- case WM8915_LEFT_LINE_INPUT_VOLUME:
- case WM8915_RIGHT_LINE_INPUT_VOLUME:
- case WM8915_LINE_INPUT_CONTROL:
- case WM8915_DAC1_HPOUT1_VOLUME:
- case WM8915_DAC2_HPOUT2_VOLUME:
- case WM8915_DAC1_LEFT_VOLUME:
- case WM8915_DAC1_RIGHT_VOLUME:
- case WM8915_DAC2_LEFT_VOLUME:
- case WM8915_DAC2_RIGHT_VOLUME:
- case WM8915_OUTPUT1_LEFT_VOLUME:
- case WM8915_OUTPUT1_RIGHT_VOLUME:
- case WM8915_OUTPUT2_LEFT_VOLUME:
- case WM8915_OUTPUT2_RIGHT_VOLUME:
- case WM8915_MICBIAS_1:
- case WM8915_MICBIAS_2:
- case WM8915_LDO_1:
- case WM8915_LDO_2:
- case WM8915_ACCESSORY_DETECT_MODE_1:
- case WM8915_ACCESSORY_DETECT_MODE_2:
- case WM8915_HEADPHONE_DETECT_1:
- case WM8915_HEADPHONE_DETECT_2:
- case WM8915_MIC_DETECT_1:
- case WM8915_MIC_DETECT_2:
- case WM8915_MIC_DETECT_3:
- case WM8915_CHARGE_PUMP_1:
- case WM8915_CHARGE_PUMP_2:
- case WM8915_DC_SERVO_1:
- case WM8915_DC_SERVO_2:
- case WM8915_DC_SERVO_3:
- case WM8915_DC_SERVO_5:
- case WM8915_DC_SERVO_6:
- case WM8915_DC_SERVO_7:
- case WM8915_DC_SERVO_READBACK_0:
- case WM8915_ANALOGUE_HP_1:
- case WM8915_ANALOGUE_HP_2:
- case WM8915_CHIP_REVISION:
- case WM8915_CONTROL_INTERFACE_1:
- case WM8915_WRITE_SEQUENCER_CTRL_1:
- case WM8915_WRITE_SEQUENCER_CTRL_2:
- case WM8915_AIF_CLOCKING_1:
- case WM8915_AIF_CLOCKING_2:
- case WM8915_CLOCKING_1:
- case WM8915_CLOCKING_2:
- case WM8915_AIF_RATE:
- case WM8915_FLL_CONTROL_1:
- case WM8915_FLL_CONTROL_2:
- case WM8915_FLL_CONTROL_3:
- case WM8915_FLL_CONTROL_4:
- case WM8915_FLL_CONTROL_5:
- case WM8915_FLL_CONTROL_6:
- case WM8915_FLL_EFS_1:
- case WM8915_FLL_EFS_2:
- case WM8915_AIF1_CONTROL:
- case WM8915_AIF1_BCLK:
- case WM8915_AIF1_TX_LRCLK_1:
- case WM8915_AIF1_TX_LRCLK_2:
- case WM8915_AIF1_RX_LRCLK_1:
- case WM8915_AIF1_RX_LRCLK_2:
- case WM8915_AIF1TX_DATA_CONFIGURATION_1:
- case WM8915_AIF1TX_DATA_CONFIGURATION_2:
- case WM8915_AIF1RX_DATA_CONFIGURATION:
- case WM8915_AIF1TX_CHANNEL_0_CONFIGURATION:
- case WM8915_AIF1TX_CHANNEL_1_CONFIGURATION:
- case WM8915_AIF1TX_CHANNEL_2_CONFIGURATION:
- case WM8915_AIF1TX_CHANNEL_3_CONFIGURATION:
- case WM8915_AIF1TX_CHANNEL_4_CONFIGURATION:
- case WM8915_AIF1TX_CHANNEL_5_CONFIGURATION:
- case WM8915_AIF1RX_CHANNEL_0_CONFIGURATION:
- case WM8915_AIF1RX_CHANNEL_1_CONFIGURATION:
- case WM8915_AIF1RX_CHANNEL_2_CONFIGURATION:
- case WM8915_AIF1RX_CHANNEL_3_CONFIGURATION:
- case WM8915_AIF1RX_CHANNEL_4_CONFIGURATION:
- case WM8915_AIF1RX_CHANNEL_5_CONFIGURATION:
- case WM8915_AIF1RX_MONO_CONFIGURATION:
- case WM8915_AIF1TX_TEST:
- case WM8915_AIF2_CONTROL:
- case WM8915_AIF2_BCLK:
- case WM8915_AIF2_TX_LRCLK_1:
- case WM8915_AIF2_TX_LRCLK_2:
- case WM8915_AIF2_RX_LRCLK_1:
- case WM8915_AIF2_RX_LRCLK_2:
- case WM8915_AIF2TX_DATA_CONFIGURATION_1:
- case WM8915_AIF2TX_DATA_CONFIGURATION_2:
- case WM8915_AIF2RX_DATA_CONFIGURATION:
- case WM8915_AIF2TX_CHANNEL_0_CONFIGURATION:
- case WM8915_AIF2TX_CHANNEL_1_CONFIGURATION:
- case WM8915_AIF2RX_CHANNEL_0_CONFIGURATION:
- case WM8915_AIF2RX_CHANNEL_1_CONFIGURATION:
- case WM8915_AIF2RX_MONO_CONFIGURATION:
- case WM8915_AIF2TX_TEST:
- case WM8915_DSP1_TX_LEFT_VOLUME:
- case WM8915_DSP1_TX_RIGHT_VOLUME:
- case WM8915_DSP1_RX_LEFT_VOLUME:
- case WM8915_DSP1_RX_RIGHT_VOLUME:
- case WM8915_DSP1_TX_FILTERS:
- case WM8915_DSP1_RX_FILTERS_1:
- case WM8915_DSP1_RX_FILTERS_2:
- case WM8915_DSP1_DRC_1:
- case WM8915_DSP1_DRC_2:
- case WM8915_DSP1_DRC_3:
- case WM8915_DSP1_DRC_4:
- case WM8915_DSP1_DRC_5:
- case WM8915_DSP1_RX_EQ_GAINS_1:
- case WM8915_DSP1_RX_EQ_GAINS_2:
- case WM8915_DSP1_RX_EQ_BAND_1_A:
- case WM8915_DSP1_RX_EQ_BAND_1_B:
- case WM8915_DSP1_RX_EQ_BAND_1_PG:
- case WM8915_DSP1_RX_EQ_BAND_2_A:
- case WM8915_DSP1_RX_EQ_BAND_2_B:
- case WM8915_DSP1_RX_EQ_BAND_2_C:
- case WM8915_DSP1_RX_EQ_BAND_2_PG:
- case WM8915_DSP1_RX_EQ_BAND_3_A:
- case WM8915_DSP1_RX_EQ_BAND_3_B:
- case WM8915_DSP1_RX_EQ_BAND_3_C:
- case WM8915_DSP1_RX_EQ_BAND_3_PG:
- case WM8915_DSP1_RX_EQ_BAND_4_A:
- case WM8915_DSP1_RX_EQ_BAND_4_B:
- case WM8915_DSP1_RX_EQ_BAND_4_C:
- case WM8915_DSP1_RX_EQ_BAND_4_PG:
- case WM8915_DSP1_RX_EQ_BAND_5_A:
- case WM8915_DSP1_RX_EQ_BAND_5_B:
- case WM8915_DSP1_RX_EQ_BAND_5_PG:
- case WM8915_DSP2_TX_LEFT_VOLUME:
- case WM8915_DSP2_TX_RIGHT_VOLUME:
- case WM8915_DSP2_RX_LEFT_VOLUME:
- case WM8915_DSP2_RX_RIGHT_VOLUME:
- case WM8915_DSP2_TX_FILTERS:
- case WM8915_DSP2_RX_FILTERS_1:
- case WM8915_DSP2_RX_FILTERS_2:
- case WM8915_DSP2_DRC_1:
- case WM8915_DSP2_DRC_2:
- case WM8915_DSP2_DRC_3:
- case WM8915_DSP2_DRC_4:
- case WM8915_DSP2_DRC_5:
- case WM8915_DSP2_RX_EQ_GAINS_1:
- case WM8915_DSP2_RX_EQ_GAINS_2:
- case WM8915_DSP2_RX_EQ_BAND_1_A:
- case WM8915_DSP2_RX_EQ_BAND_1_B:
- case WM8915_DSP2_RX_EQ_BAND_1_PG:
- case WM8915_DSP2_RX_EQ_BAND_2_A:
- case WM8915_DSP2_RX_EQ_BAND_2_B:
- case WM8915_DSP2_RX_EQ_BAND_2_C:
- case WM8915_DSP2_RX_EQ_BAND_2_PG:
- case WM8915_DSP2_RX_EQ_BAND_3_A:
- case WM8915_DSP2_RX_EQ_BAND_3_B:
- case WM8915_DSP2_RX_EQ_BAND_3_C:
- case WM8915_DSP2_RX_EQ_BAND_3_PG:
- case WM8915_DSP2_RX_EQ_BAND_4_A:
- case WM8915_DSP2_RX_EQ_BAND_4_B:
- case WM8915_DSP2_RX_EQ_BAND_4_C:
- case WM8915_DSP2_RX_EQ_BAND_4_PG:
- case WM8915_DSP2_RX_EQ_BAND_5_A:
- case WM8915_DSP2_RX_EQ_BAND_5_B:
- case WM8915_DSP2_RX_EQ_BAND_5_PG:
- case WM8915_DAC1_MIXER_VOLUMES:
- case WM8915_DAC1_LEFT_MIXER_ROUTING:
- case WM8915_DAC1_RIGHT_MIXER_ROUTING:
- case WM8915_DAC2_MIXER_VOLUMES:
- case WM8915_DAC2_LEFT_MIXER_ROUTING:
- case WM8915_DAC2_RIGHT_MIXER_ROUTING:
- case WM8915_DSP1_TX_LEFT_MIXER_ROUTING:
- case WM8915_DSP1_TX_RIGHT_MIXER_ROUTING:
- case WM8915_DSP2_TX_LEFT_MIXER_ROUTING:
- case WM8915_DSP2_TX_RIGHT_MIXER_ROUTING:
- case WM8915_DSP_TX_MIXER_SELECT:
- case WM8915_DAC_SOFTMUTE:
- case WM8915_OVERSAMPLING:
- case WM8915_SIDETONE:
- case WM8915_GPIO_1:
- case WM8915_GPIO_2:
- case WM8915_GPIO_3:
- case WM8915_GPIO_4:
- case WM8915_GPIO_5:
- case WM8915_PULL_CONTROL_1:
- case WM8915_PULL_CONTROL_2:
- case WM8915_INTERRUPT_STATUS_1:
- case WM8915_INTERRUPT_STATUS_2:
- case WM8915_INTERRUPT_RAW_STATUS_2:
- case WM8915_INTERRUPT_STATUS_1_MASK:
- case WM8915_INTERRUPT_STATUS_2_MASK:
- case WM8915_INTERRUPT_CONTROL:
- case WM8915_LEFT_PDM_SPEAKER:
- case WM8915_RIGHT_PDM_SPEAKER:
- case WM8915_PDM_SPEAKER_MUTE_SEQUENCE:
- case WM8915_PDM_SPEAKER_VOLUME:
- return 1;
- default:
- return 0;
- }
-}
-
-static int wm8915_volatile_register(struct snd_soc_codec *codec,
- unsigned int reg)
-{
- switch (reg) {
- case WM8915_SOFTWARE_RESET:
- case WM8915_CHIP_REVISION:
- case WM8915_LDO_1:
- case WM8915_LDO_2:
- case WM8915_INTERRUPT_STATUS_1:
- case WM8915_INTERRUPT_STATUS_2:
- case WM8915_INTERRUPT_RAW_STATUS_2:
- case WM8915_DC_SERVO_READBACK_0:
- case WM8915_DC_SERVO_2:
- case WM8915_DC_SERVO_6:
- case WM8915_DC_SERVO_7:
- case WM8915_FLL_CONTROL_6:
- case WM8915_MIC_DETECT_3:
- case WM8915_HEADPHONE_DETECT_1:
- case WM8915_HEADPHONE_DETECT_2:
- return 1;
- default:
- return 0;
- }
-}
-
-static int wm8915_reset(struct snd_soc_codec *codec)
-{
- return snd_soc_write(codec, WM8915_SOFTWARE_RESET, 0x8915);
-}
-
-static const int bclk_divs[] = {
- 1, 2, 3, 4, 6, 8, 12, 16, 24, 32, 48, 64, 96
-};
-
-static void wm8915_update_bclk(struct snd_soc_codec *codec)
-{
- struct wm8915_priv *wm8915 = snd_soc_codec_get_drvdata(codec);
- int aif, best, cur_val, bclk_rate, bclk_reg, i;
-
- /* Don't bother if we're in a low frequency idle mode that
- * can't support audio.
- */
- if (wm8915->sysclk < 64000)
- return;
-
- for (aif = 0; aif < WM8915_AIFS; aif++) {
- switch (aif) {
- case 0:
- bclk_reg = WM8915_AIF1_BCLK;
- break;
- case 1:
- bclk_reg = WM8915_AIF2_BCLK;
- break;
- }
-
- bclk_rate = wm8915->bclk_rate[aif];
-
- /* Pick a divisor for BCLK as close as we can get to ideal */
- best = 0;
- for (i = 0; i < ARRAY_SIZE(bclk_divs); i++) {
- cur_val = (wm8915->sysclk / bclk_divs[i]) - bclk_rate;
- if (cur_val < 0) /* BCLK table is sorted */
- break;
- best = i;
- }
- bclk_rate = wm8915->sysclk / bclk_divs[best];
- dev_dbg(codec->dev, "Using BCLK_DIV %d for actual BCLK %dHz\n",
- bclk_divs[best], bclk_rate);
-
- snd_soc_update_bits(codec, bclk_reg,
- WM8915_AIF1_BCLK_DIV_MASK, best);
- }
-}
-
-static int wm8915_set_bias_level(struct snd_soc_codec *codec,
- enum snd_soc_bias_level level)
-{
- struct wm8915_priv *wm8915 = snd_soc_codec_get_drvdata(codec);
- int ret;
-
- switch (level) {
- case SND_SOC_BIAS_ON:
- break;
-
- case SND_SOC_BIAS_PREPARE:
- if (codec->dapm.bias_level == SND_SOC_BIAS_STANDBY) {
- snd_soc_update_bits(codec, WM8915_POWER_MANAGEMENT_1,
- WM8915_BG_ENA, WM8915_BG_ENA);
- msleep(2);
- }
- break;
-
- case SND_SOC_BIAS_STANDBY:
- if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) {
- ret = regulator_bulk_enable(ARRAY_SIZE(wm8915->supplies),
- wm8915->supplies);
- if (ret != 0) {
- dev_err(codec->dev,
- "Failed to enable supplies: %d\n",
- ret);
- return ret;
- }
-
- if (wm8915->pdata.ldo_ena >= 0) {
- gpio_set_value_cansleep(wm8915->pdata.ldo_ena,
- 1);
- msleep(5);
- }
-
- codec->cache_only = false;
- snd_soc_cache_sync(codec);
- }
-
- snd_soc_update_bits(codec, WM8915_POWER_MANAGEMENT_1,
- WM8915_BG_ENA, 0);
- break;
-
- case SND_SOC_BIAS_OFF:
- codec->cache_only = true;
- if (wm8915->pdata.ldo_ena >= 0)
- gpio_set_value_cansleep(wm8915->pdata.ldo_ena, 0);
- regulator_bulk_disable(ARRAY_SIZE(wm8915->supplies),
- wm8915->supplies);
- break;
- }
-
- codec->dapm.bias_level = level;
-
- return 0;
-}
-
-static int wm8915_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
-{
- struct snd_soc_codec *codec = dai->codec;
- int aifctrl = 0;
- int bclk = 0;
- int lrclk_tx = 0;
- int lrclk_rx = 0;
- int aifctrl_reg, bclk_reg, lrclk_tx_reg, lrclk_rx_reg;
-
- switch (dai->id) {
- case 0:
- aifctrl_reg = WM8915_AIF1_CONTROL;
- bclk_reg = WM8915_AIF1_BCLK;
- lrclk_tx_reg = WM8915_AIF1_TX_LRCLK_2;
- lrclk_rx_reg = WM8915_AIF1_RX_LRCLK_2;
- break;
- case 1:
- aifctrl_reg = WM8915_AIF2_CONTROL;
- bclk_reg = WM8915_AIF2_BCLK;
- lrclk_tx_reg = WM8915_AIF2_TX_LRCLK_2;
- lrclk_rx_reg = WM8915_AIF2_RX_LRCLK_2;
- break;
- default:
- BUG();
- return -EINVAL;
- }
-
- switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
- case SND_SOC_DAIFMT_NB_NF:
- break;
- case SND_SOC_DAIFMT_IB_NF:
- bclk |= WM8915_AIF1_BCLK_INV;
- break;
- case SND_SOC_DAIFMT_NB_IF:
- lrclk_tx |= WM8915_AIF1TX_LRCLK_INV;
- lrclk_rx |= WM8915_AIF1RX_LRCLK_INV;
- break;
- case SND_SOC_DAIFMT_IB_IF:
- bclk |= WM8915_AIF1_BCLK_INV;
- lrclk_tx |= WM8915_AIF1TX_LRCLK_INV;
- lrclk_rx |= WM8915_AIF1RX_LRCLK_INV;
- break;
- }
-
- switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
- case SND_SOC_DAIFMT_CBS_CFS:
- break;
- case SND_SOC_DAIFMT_CBS_CFM:
- lrclk_tx |= WM8915_AIF1TX_LRCLK_MSTR;
- lrclk_rx |= WM8915_AIF1RX_LRCLK_MSTR;
- break;
- case SND_SOC_DAIFMT_CBM_CFS:
- bclk |= WM8915_AIF1_BCLK_MSTR;
- break;
- case SND_SOC_DAIFMT_CBM_CFM:
- bclk |= WM8915_AIF1_BCLK_MSTR;
- lrclk_tx |= WM8915_AIF1TX_LRCLK_MSTR;
- lrclk_rx |= WM8915_AIF1RX_LRCLK_MSTR;
- break;
- default:
- return -EINVAL;
- }
-
- switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
- case SND_SOC_DAIFMT_DSP_A:
- break;
- case SND_SOC_DAIFMT_DSP_B:
- aifctrl |= 1;
- break;
- case SND_SOC_DAIFMT_I2S:
- aifctrl |= 2;
- break;
- case SND_SOC_DAIFMT_LEFT_J:
- aifctrl |= 3;
- break;
- default:
- return -EINVAL;
- }
-
- snd_soc_update_bits(codec, aifctrl_reg, WM8915_AIF1_FMT_MASK, aifctrl);
- snd_soc_update_bits(codec, bclk_reg,
- WM8915_AIF1_BCLK_INV | WM8915_AIF1_BCLK_MSTR,
- bclk);
- snd_soc_update_bits(codec, lrclk_tx_reg,
- WM8915_AIF1TX_LRCLK_INV |
- WM8915_AIF1TX_LRCLK_MSTR,
- lrclk_tx);
- snd_soc_update_bits(codec, lrclk_rx_reg,
- WM8915_AIF1RX_LRCLK_INV |
- WM8915_AIF1RX_LRCLK_MSTR,
- lrclk_rx);
-
- return 0;
-}
-
-static const int dsp_divs[] = {
- 48000, 32000, 16000, 8000
-};
-
-static int wm8915_hw_params(struct snd_pcm_substream *substream,
- struct snd_pcm_hw_params *params,
- struct snd_soc_dai *dai)
-{
- struct snd_soc_codec *codec = dai->codec;
- struct wm8915_priv *wm8915 = snd_soc_codec_get_drvdata(codec);
- int bits, i, bclk_rate;
- int aifdata = 0;
- int lrclk = 0;
- int dsp = 0;
- int aifdata_reg, lrclk_reg, dsp_shift;
-
- switch (dai->id) {
- case 0:
- if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK ||
- (snd_soc_read(codec, WM8915_GPIO_1)) & WM8915_GP1_FN_MASK) {
- aifdata_reg = WM8915_AIF1RX_DATA_CONFIGURATION;
- lrclk_reg = WM8915_AIF1_RX_LRCLK_1;
- } else {
- aifdata_reg = WM8915_AIF1TX_DATA_CONFIGURATION_1;
- lrclk_reg = WM8915_AIF1_TX_LRCLK_1;
- }
- dsp_shift = 0;
- break;
- case 1:
- if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK ||
- (snd_soc_read(codec, WM8915_GPIO_2)) & WM8915_GP2_FN_MASK) {
- aifdata_reg = WM8915_AIF2RX_DATA_CONFIGURATION;
- lrclk_reg = WM8915_AIF2_RX_LRCLK_1;
- } else {
- aifdata_reg = WM8915_AIF2TX_DATA_CONFIGURATION_1;
- lrclk_reg = WM8915_AIF2_TX_LRCLK_1;
- }
- dsp_shift = WM8915_DSP2_DIV_SHIFT;
- break;
- default:
- BUG();
- return -EINVAL;
- }
-
- bclk_rate = snd_soc_params_to_bclk(params);
- if (bclk_rate < 0) {
- dev_err(codec->dev, "Unsupported BCLK rate: %d\n", bclk_rate);
- return bclk_rate;
- }
-
- wm8915->bclk_rate[dai->id] = bclk_rate;
- wm8915->rx_rate[dai->id] = params_rate(params);
-
- /* Needs looking at for TDM */
- bits = snd_pcm_format_width(params_format(params));
- if (bits < 0)
- return bits;
- aifdata |= (bits << WM8915_AIF1TX_WL_SHIFT) | bits;
-
- for (i = 0; i < ARRAY_SIZE(dsp_divs); i++) {
- if (dsp_divs[i] == params_rate(params))
- break;
- }
- if (i == ARRAY_SIZE(dsp_divs)) {
- dev_err(codec->dev, "Unsupported sample rate %dHz\n",
- params_rate(params));
- return -EINVAL;
- }
- dsp |= i << dsp_shift;
-
- wm8915_update_bclk(codec);
-
- lrclk = bclk_rate / params_rate(params);
- dev_dbg(dai->dev, "Using LRCLK rate %d for actual LRCLK %dHz\n",
- lrclk, bclk_rate / lrclk);
-
- snd_soc_update_bits(codec, aifdata_reg,
- WM8915_AIF1TX_WL_MASK |
- WM8915_AIF1TX_SLOT_LEN_MASK,
- aifdata);
- snd_soc_update_bits(codec, lrclk_reg, WM8915_AIF1RX_RATE_MASK,
- lrclk);
- snd_soc_update_bits(codec, WM8915_AIF_CLOCKING_2,
- WM8915_DSP1_DIV_SHIFT << dsp_shift, dsp);
-
- return 0;
-}
-
-static int wm8915_set_sysclk(struct snd_soc_dai *dai,
- int clk_id, unsigned int freq, int dir)
-{
- struct snd_soc_codec *codec = dai->codec;
- struct wm8915_priv *wm8915 = snd_soc_codec_get_drvdata(codec);
- int lfclk = 0;
- int ratediv = 0;
- int src;
- int old;
-
- if (freq == wm8915->sysclk && clk_id == wm8915->sysclk_src)
- return 0;
-
- /* Disable SYSCLK while we reconfigure */
- old = snd_soc_read(codec, WM8915_AIF_CLOCKING_1) & WM8915_SYSCLK_ENA;
- snd_soc_update_bits(codec, WM8915_AIF_CLOCKING_1,
- WM8915_SYSCLK_ENA, 0);
-
- switch (clk_id) {
- case WM8915_SYSCLK_MCLK1:
- wm8915->sysclk = freq;
- src = 0;
- break;
- case WM8915_SYSCLK_MCLK2:
- wm8915->sysclk = freq;
- src = 1;
- break;
- case WM8915_SYSCLK_FLL:
- wm8915->sysclk = freq;
- src = 2;
- break;
- default:
- dev_err(codec->dev, "Unsupported clock source %d\n", clk_id);
- return -EINVAL;
- }
-
- switch (wm8915->sysclk) {
- case 6144000:
- snd_soc_update_bits(codec, WM8915_AIF_RATE,
- WM8915_SYSCLK_RATE, 0);
- break;
- case 24576000:
- ratediv = WM8915_SYSCLK_DIV;
- case 12288000:
- snd_soc_update_bits(codec, WM8915_AIF_RATE,
- WM8915_SYSCLK_RATE, WM8915_SYSCLK_RATE);
- break;
- case 32000:
- case 32768:
- lfclk = WM8915_LFCLK_ENA;
- break;
- default:
- dev_warn(codec->dev, "Unsupported clock rate %dHz\n",
- wm8915->sysclk);
- return -EINVAL;
- }
-
- wm8915_update_bclk(codec);
-
- snd_soc_update_bits(codec, WM8915_AIF_CLOCKING_1,
- WM8915_SYSCLK_SRC_MASK | WM8915_SYSCLK_DIV_MASK,
- src << WM8915_SYSCLK_SRC_SHIFT | ratediv);
- snd_soc_update_bits(codec, WM8915_CLOCKING_1, WM8915_LFCLK_ENA, lfclk);
- snd_soc_update_bits(codec, WM8915_AIF_CLOCKING_1,
- WM8915_SYSCLK_ENA, old);
-
- wm8915->sysclk_src = clk_id;
-
- return 0;
-}
-
-struct _fll_div {
- u16 fll_fratio;
- u16 fll_outdiv;
- u16 fll_refclk_div;
- u16 fll_loop_gain;
- u16 fll_ref_freq;
- u16 n;
- u16 theta;
- u16 lambda;
-};
-
-static struct {
- unsigned int min;
- unsigned int max;
- u16 fll_fratio;
- int ratio;
-} fll_fratios[] = {
- { 0, 64000, 4, 16 },
- { 64000, 128000, 3, 8 },
- { 128000, 256000, 2, 4 },
- { 256000, 1000000, 1, 2 },
- { 1000000, 13500000, 0, 1 },
-};
-
-static int fll_factors(struct _fll_div *fll_div, unsigned int Fref,
- unsigned int Fout)
-{
- unsigned int target;
- unsigned int div;
- unsigned int fratio, gcd_fll;
- int i;
-
- /* Fref must be <=13.5MHz */
- div = 1;
- fll_div->fll_refclk_div = 0;
- while ((Fref / div) > 13500000) {
- div *= 2;
- fll_div->fll_refclk_div++;
-
- if (div > 8) {
- pr_err("Can't scale %dMHz input down to <=13.5MHz\n",
- Fref);
- return -EINVAL;
- }
- }
-
- pr_debug("FLL Fref=%u Fout=%u\n", Fref, Fout);
-
- /* Apply the division for our remaining calculations */
- Fref /= div;
-
- if (Fref >= 3000000)
- fll_div->fll_loop_gain = 5;
- else
- fll_div->fll_loop_gain = 0;
-
- if (Fref >= 48000)
- fll_div->fll_ref_freq = 0;
- else
- fll_div->fll_ref_freq = 1;
-
- /* Fvco should be 90-100MHz; don't check the upper bound */
- div = 2;
- while (Fout * div < 90000000) {
- div++;
- if (div > 64) {
- pr_err("Unable to find FLL_OUTDIV for Fout=%uHz\n",
- Fout);
- return -EINVAL;
- }
- }
- target = Fout * div;
- fll_div->fll_outdiv = div - 1;
-
- pr_debug("FLL Fvco=%dHz\n", target);
-
- /* Find an appropraite FLL_FRATIO and factor it out of the target */
- for (i = 0; i < ARRAY_SIZE(fll_fratios); i++) {
- if (fll_fratios[i].min <= Fref && Fref <= fll_fratios[i].max) {
- fll_div->fll_fratio = fll_fratios[i].fll_fratio;
- fratio = fll_fratios[i].ratio;
- break;
- }
- }
- if (i == ARRAY_SIZE(fll_fratios)) {
- pr_err("Unable to find FLL_FRATIO for Fref=%uHz\n", Fref);
- return -EINVAL;
- }
-
- fll_div->n = target / (fratio * Fref);
-
- if (target % Fref == 0) {
- fll_div->theta = 0;
- fll_div->lambda = 0;
- } else {
- gcd_fll = gcd(target, fratio * Fref);
-
- fll_div->theta = (target - (fll_div->n * fratio * Fref))
- / gcd_fll;
- fll_div->lambda = (fratio * Fref) / gcd_fll;
- }
-
- pr_debug("FLL N=%x THETA=%x LAMBDA=%x\n",
- fll_div->n, fll_div->theta, fll_div->lambda);
- pr_debug("FLL_FRATIO=%x FLL_OUTDIV=%x FLL_REFCLK_DIV=%x\n",
- fll_div->fll_fratio, fll_div->fll_outdiv,
- fll_div->fll_refclk_div);
-
- return 0;
-}
-
-static int wm8915_set_fll(struct snd_soc_codec *codec, int fll_id, int source,
- unsigned int Fref, unsigned int Fout)
-{
- struct wm8915_priv *wm8915 = snd_soc_codec_get_drvdata(codec);
- struct i2c_client *i2c = to_i2c_client(codec->dev);
- struct _fll_div fll_div;
- unsigned long timeout;
- int ret, reg;
-
- /* Any change? */
- if (source == wm8915->fll_src && Fref == wm8915->fll_fref &&
- Fout == wm8915->fll_fout)
- return 0;
-
- if (Fout == 0) {
- dev_dbg(codec->dev, "FLL disabled\n");
-
- wm8915->fll_fref = 0;
- wm8915->fll_fout = 0;
-
- snd_soc_update_bits(codec, WM8915_FLL_CONTROL_1,
- WM8915_FLL_ENA, 0);
-
- return 0;
- }
-
- ret = fll_factors(&fll_div, Fref, Fout);
- if (ret != 0)
- return ret;
-
- switch (source) {
- case WM8915_FLL_MCLK1:
- reg = 0;
- break;
- case WM8915_FLL_MCLK2:
- reg = 1;
- break;
- case WM8915_FLL_DACLRCLK1:
- reg = 2;
- break;
- case WM8915_FLL_BCLK1:
- reg = 3;
- break;
- default:
- dev_err(codec->dev, "Unknown FLL source %d\n", ret);
- return -EINVAL;
- }
-
- reg |= fll_div.fll_refclk_div << WM8915_FLL_REFCLK_DIV_SHIFT;
- reg |= fll_div.fll_ref_freq << WM8915_FLL_REF_FREQ_SHIFT;
-
- snd_soc_update_bits(codec, WM8915_FLL_CONTROL_5,
- WM8915_FLL_REFCLK_DIV_MASK | WM8915_FLL_REF_FREQ |
- WM8915_FLL_REFCLK_SRC_MASK, reg);
-
- reg = 0;
- if (fll_div.theta || fll_div.lambda)
- reg |= WM8915_FLL_EFS_ENA | (3 << WM8915_FLL_LFSR_SEL_SHIFT);
- else
- reg |= 1 << WM8915_FLL_LFSR_SEL_SHIFT;
- snd_soc_write(codec, WM8915_FLL_EFS_2, reg);
-
- snd_soc_update_bits(codec, WM8915_FLL_CONTROL_2,
- WM8915_FLL_OUTDIV_MASK |
- WM8915_FLL_FRATIO_MASK,
- (fll_div.fll_outdiv << WM8915_FLL_OUTDIV_SHIFT) |
- (fll_div.fll_fratio));
-
- snd_soc_write(codec, WM8915_FLL_CONTROL_3, fll_div.theta);
-
- snd_soc_update_bits(codec, WM8915_FLL_CONTROL_4,
- WM8915_FLL_N_MASK | WM8915_FLL_LOOP_GAIN_MASK,
- (fll_div.n << WM8915_FLL_N_SHIFT) |
- fll_div.fll_loop_gain);
-
- snd_soc_write(codec, WM8915_FLL_EFS_1, fll_div.lambda);
-
- snd_soc_update_bits(codec, WM8915_FLL_CONTROL_1,
- WM8915_FLL_ENA, WM8915_FLL_ENA);
-
- /* The FLL supports live reconfiguration - kick that in case we were
- * already enabled.
- */
- snd_soc_write(codec, WM8915_FLL_CONTROL_6, WM8915_FLL_SWITCH_CLK);
-
- /* Wait for the FLL to lock, using the interrupt if possible */
- if (Fref > 1000000)
- timeout = usecs_to_jiffies(300);
- else
- timeout = msecs_to_jiffies(2);
-
- /* Allow substantially longer if we've actually got the IRQ */
- if (i2c->irq)
- timeout *= 1000;
-
- ret = wait_for_completion_timeout(&wm8915->fll_lock, timeout);
-
- if (ret == 0 && i2c->irq) {
- dev_err(codec->dev, "Timed out waiting for FLL\n");
- ret = -ETIMEDOUT;
- } else {
- ret = 0;
- }
-
- dev_dbg(codec->dev, "FLL configured for %dHz->%dHz\n", Fref, Fout);
-
- wm8915->fll_fref = Fref;
- wm8915->fll_fout = Fout;
- wm8915->fll_src = source;
-
- return ret;
-}
-
-#ifdef CONFIG_GPIOLIB
-static inline struct wm8915_priv *gpio_to_wm8915(struct gpio_chip *chip)
-{
- return container_of(chip, struct wm8915_priv, gpio_chip);
-}
-
-static void wm8915_gpio_set(struct gpio_chip *chip, unsigned offset, int value)
-{
- struct wm8915_priv *wm8915 = gpio_to_wm8915(chip);
- struct snd_soc_codec *codec = wm8915->codec;
-
- snd_soc_update_bits(codec, WM8915_GPIO_1 + offset,
- WM8915_GP1_LVL, !!value << WM8915_GP1_LVL_SHIFT);
-}
-
-static int wm8915_gpio_direction_out(struct gpio_chip *chip,
- unsigned offset, int value)
-{
- struct wm8915_priv *wm8915 = gpio_to_wm8915(chip);
- struct snd_soc_codec *codec = wm8915->codec;
- int val;
-
- val = (1 << WM8915_GP1_FN_SHIFT) | (!!value << WM8915_GP1_LVL_SHIFT);
-
- return snd_soc_update_bits(codec, WM8915_GPIO_1 + offset,
- WM8915_GP1_FN_MASK | WM8915_GP1_DIR |
- WM8915_GP1_LVL, val);
-}
-
-static int wm8915_gpio_get(struct gpio_chip *chip, unsigned offset)
-{
- struct wm8915_priv *wm8915 = gpio_to_wm8915(chip);
- struct snd_soc_codec *codec = wm8915->codec;
- int ret;
-
- ret = snd_soc_read(codec, WM8915_GPIO_1 + offset);
- if (ret < 0)
- return ret;
-
- return (ret & WM8915_GP1_LVL) != 0;
-}
-
-static int wm8915_gpio_direction_in(struct gpio_chip *chip, unsigned offset)
-{
- struct wm8915_priv *wm8915 = gpio_to_wm8915(chip);
- struct snd_soc_codec *codec = wm8915->codec;
-
- return snd_soc_update_bits(codec, WM8915_GPIO_1 + offset,
- WM8915_GP1_FN_MASK | WM8915_GP1_DIR,
- (1 << WM8915_GP1_FN_SHIFT) |
- (1 << WM8915_GP1_DIR_SHIFT));
-}
-
-static struct gpio_chip wm8915_template_chip = {
- .label = "wm8915",
- .owner = THIS_MODULE,
- .direction_output = wm8915_gpio_direction_out,
- .set = wm8915_gpio_set,
- .direction_input = wm8915_gpio_direction_in,
- .get = wm8915_gpio_get,
- .can_sleep = 1,
-};
-
-static void wm8915_init_gpio(struct snd_soc_codec *codec)
-{
- struct wm8915_priv *wm8915 = snd_soc_codec_get_drvdata(codec);
- int ret;
-
- wm8915->gpio_chip = wm8915_template_chip;
- wm8915->gpio_chip.ngpio = 5;
- wm8915->gpio_chip.dev = codec->dev;
-
- if (wm8915->pdata.gpio_base)
- wm8915->gpio_chip.base = wm8915->pdata.gpio_base;
- else
- wm8915->gpio_chip.base = -1;
-
- ret = gpiochip_add(&wm8915->gpio_chip);
- if (ret != 0)
- dev_err(codec->dev, "Failed to add GPIOs: %d\n", ret);
-}
-
-static void wm8915_free_gpio(struct snd_soc_codec *codec)
-{
- struct wm8915_priv *wm8915 = snd_soc_codec_get_drvdata(codec);
- int ret;
-
- ret = gpiochip_remove(&wm8915->gpio_chip);
- if (ret != 0)
- dev_err(codec->dev, "Failed to remove GPIOs: %d\n", ret);
-}
-#else
-static void wm8915_init_gpio(struct snd_soc_codec *codec)
-{
-}
-
-static void wm8915_free_gpio(struct snd_soc_codec *codec)
-{
-}
-#endif
-
-/**
- * wm8915_detect - Enable default WM8915 jack detection
- *
- * The WM8915 has advanced accessory detection support for headsets.
- * This function provides a default implementation which integrates
- * the majority of this functionality with minimal user configuration.
- *
- * This will detect headset, headphone and short circuit button and
- * will also detect inverted microphone ground connections and update
- * the polarity of the connections.
- */
-int wm8915_detect(struct snd_soc_codec *codec, struct snd_soc_jack *jack,
- wm8915_polarity_fn polarity_cb)
-{
- struct wm8915_priv *wm8915 = snd_soc_codec_get_drvdata(codec);
-
- wm8915->jack = jack;
- wm8915->detecting = true;
- wm8915->polarity_cb = polarity_cb;
-
- if (wm8915->polarity_cb)
- wm8915->polarity_cb(codec, 0);
-
- /* Clear discarge to avoid noise during detection */
- snd_soc_update_bits(codec, WM8915_MICBIAS_1,
- WM8915_MICB1_DISCH, 0);
- snd_soc_update_bits(codec, WM8915_MICBIAS_2,
- WM8915_MICB2_DISCH, 0);
-
- /* LDO2 powers the microphones, SYSCLK clocks detection */
- snd_soc_dapm_force_enable_pin(&codec->dapm, "LDO2");
- snd_soc_dapm_force_enable_pin(&codec->dapm, "SYSCLK");
-
- /* We start off just enabling microphone detection - even a
- * plain headphone will trigger detection.
- */
- snd_soc_update_bits(codec, WM8915_MIC_DETECT_1,
- WM8915_MICD_ENA, WM8915_MICD_ENA);
-
- /* Slowest detection rate, gives debounce for initial detection */
- snd_soc_update_bits(codec, WM8915_MIC_DETECT_1,
- WM8915_MICD_RATE_MASK,
- WM8915_MICD_RATE_MASK);
-
- /* Enable interrupts and we're off */
- snd_soc_update_bits(codec, WM8915_INTERRUPT_STATUS_2_MASK,
- WM8915_IM_MICD_EINT, 0);
-
- return 0;
-}
-EXPORT_SYMBOL_GPL(wm8915_detect);
-
-static void wm8915_micd(struct snd_soc_codec *codec)
-{
- struct wm8915_priv *wm8915 = snd_soc_codec_get_drvdata(codec);
- int val, reg;
-
- val = snd_soc_read(codec, WM8915_MIC_DETECT_3);
-
- dev_dbg(codec->dev, "Microphone event: %x\n", val);
-
- if (!(val & WM8915_MICD_VALID)) {
- dev_warn(codec->dev, "Microphone detection state invalid\n");
- return;
- }
-
- /* No accessory, reset everything and report removal */
- if (!(val & WM8915_MICD_STS)) {
- dev_dbg(codec->dev, "Jack removal detected\n");
- wm8915->jack_mic = false;
- wm8915->detecting = true;
- snd_soc_jack_report(wm8915->jack, 0,
- SND_JACK_HEADSET | SND_JACK_BTN_0);
- snd_soc_update_bits(codec, WM8915_MIC_DETECT_1,
- WM8915_MICD_RATE_MASK,
- WM8915_MICD_RATE_MASK);
- return;
- }
-
- /* If the measurement is very high we've got a microphone but
- * do a little debounce to account for mechanical issues.
- */
- if (val & 0x400) {
- dev_dbg(codec->dev, "Microphone detected\n");
- snd_soc_jack_report(wm8915->jack, SND_JACK_HEADSET,
- SND_JACK_HEADSET | SND_JACK_BTN_0);
- wm8915->jack_mic = true;
- wm8915->detecting = false;
-
- /* Increase poll rate to give better responsiveness
- * for buttons */
- snd_soc_update_bits(codec, WM8915_MIC_DETECT_1,
- WM8915_MICD_RATE_MASK,
- 5 << WM8915_MICD_RATE_SHIFT);
- }
-
- /* If we detected a lower impedence during initial startup
- * then we probably have the wrong polarity, flip it. Don't
- * do this for the lowest impedences to speed up detection of
- * plain headphones.
- */
- if (wm8915->detecting && (val & 0x3f0)) {
- reg = snd_soc_read(codec, WM8915_ACCESSORY_DETECT_MODE_2);
- reg ^= WM8915_HPOUT1FB_SRC | WM8915_MICD_SRC |
- WM8915_MICD_BIAS_SRC;
- snd_soc_update_bits(codec, WM8915_ACCESSORY_DETECT_MODE_2,
- WM8915_HPOUT1FB_SRC | WM8915_MICD_SRC |
- WM8915_MICD_BIAS_SRC, reg);
-
- if (wm8915->polarity_cb)
- wm8915->polarity_cb(codec,
- (reg & WM8915_MICD_SRC) != 0);
-
- dev_dbg(codec->dev, "Set microphone polarity to %d\n",
- (reg & WM8915_MICD_SRC) != 0);
-
- return;
- }
-
- /* Don't distinguish between buttons, just report any low
- * impedence as BTN_0.
- */
- if (val & 0x3fc) {
- if (wm8915->jack_mic) {
- dev_dbg(codec->dev, "Mic button detected\n");
- snd_soc_jack_report(wm8915->jack,
- SND_JACK_HEADSET | SND_JACK_BTN_0,
- SND_JACK_HEADSET | SND_JACK_BTN_0);
- } else {
- dev_dbg(codec->dev, "Headphone detected\n");
- snd_soc_jack_report(wm8915->jack,
- SND_JACK_HEADPHONE,
- SND_JACK_HEADSET |
- SND_JACK_BTN_0);
-
- /* Increase the detection rate a bit for
- * responsiveness.
- */
- snd_soc_update_bits(codec, WM8915_MIC_DETECT_1,
- WM8915_MICD_RATE_MASK,
- 7 << WM8915_MICD_RATE_SHIFT);
-
- wm8915->detecting = false;
- }
- }
-}
-
-static irqreturn_t wm8915_irq(int irq, void *data)
-{
- struct snd_soc_codec *codec = data;
- struct wm8915_priv *wm8915 = snd_soc_codec_get_drvdata(codec);
- int irq_val;
-
- irq_val = snd_soc_read(codec, WM8915_INTERRUPT_STATUS_2);
- if (irq_val < 0) {
- dev_err(codec->dev, "Failed to read IRQ status: %d\n",
- irq_val);
- return IRQ_NONE;
- }
- irq_val &= ~snd_soc_read(codec, WM8915_INTERRUPT_STATUS_2_MASK);
-
- if (irq_val & (WM8915_DCS_DONE_01_EINT | WM8915_DCS_DONE_23_EINT)) {
- dev_dbg(codec->dev, "DC servo IRQ\n");
- complete(&wm8915->dcs_done);
- }
-
- if (irq_val & WM8915_FIFOS_ERR_EINT)
- dev_err(codec->dev, "Digital core FIFO error\n");
-
- if (irq_val & WM8915_FLL_LOCK_EINT) {
- dev_dbg(codec->dev, "FLL locked\n");
- complete(&wm8915->fll_lock);
- }
-
- if (irq_val & WM8915_MICD_EINT)
- wm8915_micd(codec);
-
- if (irq_val) {
- snd_soc_write(codec, WM8915_INTERRUPT_STATUS_2, irq_val);
-
- return IRQ_HANDLED;
- } else {
- return IRQ_NONE;
- }
-}
-
-static irqreturn_t wm8915_edge_irq(int irq, void *data)
-{
- irqreturn_t ret = IRQ_NONE;
- irqreturn_t val;
-
- do {
- val = wm8915_irq(irq, data);
- if (val != IRQ_NONE)
- ret = val;
- } while (val != IRQ_NONE);
-
- return ret;
-}
-
-static void wm8915_retune_mobile_pdata(struct snd_soc_codec *codec)
-{
- struct wm8915_priv *wm8915 = snd_soc_codec_get_drvdata(codec);
- struct wm8915_pdata *pdata = &wm8915->pdata;
-
- struct snd_kcontrol_new controls[] = {
- SOC_ENUM_EXT("DSP1 EQ Mode",
- wm8915->retune_mobile_enum,
- wm8915_get_retune_mobile_enum,
- wm8915_put_retune_mobile_enum),
- SOC_ENUM_EXT("DSP2 EQ Mode",
- wm8915->retune_mobile_enum,
- wm8915_get_retune_mobile_enum,
- wm8915_put_retune_mobile_enum),
- };
- int ret, i, j;
- const char **t;
-
- /* We need an array of texts for the enum API but the number
- * of texts is likely to be less than the number of
- * configurations due to the sample rate dependency of the
- * configurations. */
- wm8915->num_retune_mobile_texts = 0;
- wm8915->retune_mobile_texts = NULL;
- for (i = 0; i < pdata->num_retune_mobile_cfgs; i++) {
- for (j = 0; j < wm8915->num_retune_mobile_texts; j++) {
- if (strcmp(pdata->retune_mobile_cfgs[i].name,
- wm8915->retune_mobile_texts[j]) == 0)
- break;
- }
-
- if (j != wm8915->num_retune_mobile_texts)
- continue;
-
- /* Expand the array... */
- t = krealloc(wm8915->retune_mobile_texts,
- sizeof(char *) *
- (wm8915->num_retune_mobile_texts + 1),
- GFP_KERNEL);
- if (t == NULL)
- continue;
-
- /* ...store the new entry... */
- t[wm8915->num_retune_mobile_texts] =
- pdata->retune_mobile_cfgs[i].name;
-
- /* ...and remember the new version. */
- wm8915->num_retune_mobile_texts++;
- wm8915->retune_mobile_texts = t;
- }
-
- dev_dbg(codec->dev, "Allocated %d unique ReTune Mobile names\n",
- wm8915->num_retune_mobile_texts);
-
- wm8915->retune_mobile_enum.max = wm8915->num_retune_mobile_texts;
- wm8915->retune_mobile_enum.texts = wm8915->retune_mobile_texts;
-
- ret = snd_soc_add_controls(codec, controls, ARRAY_SIZE(controls));
- if (ret != 0)
- dev_err(codec->dev,
- "Failed to add ReTune Mobile controls: %d\n", ret);
-}
-
-static int wm8915_probe(struct snd_soc_codec *codec)
-{
- int ret;
- struct wm8915_priv *wm8915 = snd_soc_codec_get_drvdata(codec);
- struct i2c_client *i2c = to_i2c_client(codec->dev);
- struct snd_soc_dapm_context *dapm = &codec->dapm;
- int i, irq_flags;
-
- wm8915->codec = codec;
-
- init_completion(&wm8915->dcs_done);
- init_completion(&wm8915->fll_lock);
-
- dapm->idle_bias_off = true;
- dapm->bias_level = SND_SOC_BIAS_OFF;
-
- ret = snd_soc_codec_set_cache_io(codec, 16, 16, SND_SOC_I2C);
- if (ret != 0) {
- dev_err(codec->dev, "Failed to set cache I/O: %d\n", ret);
- goto err;
- }
-
- for (i = 0; i < ARRAY_SIZE(wm8915->supplies); i++)
- wm8915->supplies[i].supply = wm8915_supply_names[i];
-
- ret = regulator_bulk_get(codec->dev, ARRAY_SIZE(wm8915->supplies),
- wm8915->supplies);
- if (ret != 0) {
- dev_err(codec->dev, "Failed to request supplies: %d\n", ret);
- goto err;
- }
-
- wm8915->disable_nb[0].notifier_call = wm8915_regulator_event_0;
- wm8915->disable_nb[1].notifier_call = wm8915_regulator_event_1;
- wm8915->disable_nb[2].notifier_call = wm8915_regulator_event_2;
- wm8915->disable_nb[3].notifier_call = wm8915_regulator_event_3;
-
- /* This should really be moved into the regulator core */
- for (i = 0; i < ARRAY_SIZE(wm8915->supplies); i++) {
- ret = regulator_register_notifier(wm8915->supplies[i].consumer,
- &wm8915->disable_nb[i]);
- if (ret != 0) {
- dev_err(codec->dev,
- "Failed to register regulator notifier: %d\n",
- ret);
- }
- }
-
- ret = regulator_bulk_enable(ARRAY_SIZE(wm8915->supplies),
- wm8915->supplies);
- if (ret != 0) {
- dev_err(codec->dev, "Failed to enable supplies: %d\n", ret);
- goto err_get;
- }
-
- if (wm8915->pdata.ldo_ena >= 0) {
- gpio_set_value_cansleep(wm8915->pdata.ldo_ena, 1);
- msleep(5);
- }
-
- ret = snd_soc_read(codec, WM8915_SOFTWARE_RESET);
- if (ret < 0) {
- dev_err(codec->dev, "Failed to read ID register: %d\n", ret);
- goto err_enable;
- }
- if (ret != 0x8915) {
- dev_err(codec->dev, "Device is not a WM8915, ID %x\n", ret);
- ret = -EINVAL;
- goto err_enable;
- }
-
- ret = snd_soc_read(codec, WM8915_CHIP_REVISION);
- if (ret < 0) {
- dev_err(codec->dev, "Failed to read device revision: %d\n",
- ret);
- goto err_enable;
- }
-
- dev_info(codec->dev, "revision %c\n",
- (ret & WM8915_CHIP_REV_MASK) + 'A');
-
- if (wm8915->pdata.ldo_ena >= 0) {
- gpio_set_value_cansleep(wm8915->pdata.ldo_ena, 0);
- } else {
- ret = wm8915_reset(codec);
- if (ret < 0) {
- dev_err(codec->dev, "Failed to issue reset\n");
- goto err_enable;
- }
- }
-
- codec->cache_only = true;
-
- /* Apply platform data settings */
- snd_soc_update_bits(codec, WM8915_LINE_INPUT_CONTROL,
- WM8915_INL_MODE_MASK | WM8915_INR_MODE_MASK,
- wm8915->pdata.inl_mode << WM8915_INL_MODE_SHIFT |
- wm8915->pdata.inr_mode);
-
- for (i = 0; i < ARRAY_SIZE(wm8915->pdata.gpio_default); i++) {
- if (!wm8915->pdata.gpio_default[i])
- continue;
-
- snd_soc_write(codec, WM8915_GPIO_1 + i,
- wm8915->pdata.gpio_default[i] & 0xffff);
- }
-
- if (wm8915->pdata.spkmute_seq)
- snd_soc_update_bits(codec, WM8915_PDM_SPEAKER_MUTE_SEQUENCE,
- WM8915_SPK_MUTE_ENDIAN |
- WM8915_SPK_MUTE_SEQ1_MASK,
- wm8915->pdata.spkmute_seq);
-
- snd_soc_update_bits(codec, WM8915_ACCESSORY_DETECT_MODE_2,
- WM8915_MICD_BIAS_SRC | WM8915_HPOUT1FB_SRC |
- WM8915_MICD_SRC, wm8915->pdata.micdet_def);
-
- /* Latch volume update bits */
- snd_soc_update_bits(codec, WM8915_LEFT_LINE_INPUT_VOLUME,
- WM8915_IN1_VU, WM8915_IN1_VU);
- snd_soc_update_bits(codec, WM8915_RIGHT_LINE_INPUT_VOLUME,
- WM8915_IN1_VU, WM8915_IN1_VU);
-
- snd_soc_update_bits(codec, WM8915_DAC1_LEFT_VOLUME,
- WM8915_DAC1_VU, WM8915_DAC1_VU);
- snd_soc_update_bits(codec, WM8915_DAC1_RIGHT_VOLUME,
- WM8915_DAC1_VU, WM8915_DAC1_VU);
- snd_soc_update_bits(codec, WM8915_DAC2_LEFT_VOLUME,
- WM8915_DAC2_VU, WM8915_DAC2_VU);
- snd_soc_update_bits(codec, WM8915_DAC2_RIGHT_VOLUME,
- WM8915_DAC2_VU, WM8915_DAC2_VU);
-
- snd_soc_update_bits(codec, WM8915_OUTPUT1_LEFT_VOLUME,
- WM8915_DAC1_VU, WM8915_DAC1_VU);
- snd_soc_update_bits(codec, WM8915_OUTPUT1_RIGHT_VOLUME,
- WM8915_DAC1_VU, WM8915_DAC1_VU);
- snd_soc_update_bits(codec, WM8915_OUTPUT2_LEFT_VOLUME,
- WM8915_DAC2_VU, WM8915_DAC2_VU);
- snd_soc_update_bits(codec, WM8915_OUTPUT2_RIGHT_VOLUME,
- WM8915_DAC2_VU, WM8915_DAC2_VU);
-
- snd_soc_update_bits(codec, WM8915_DSP1_TX_LEFT_VOLUME,
- WM8915_DSP1TX_VU, WM8915_DSP1TX_VU);
- snd_soc_update_bits(codec, WM8915_DSP1_TX_RIGHT_VOLUME,
- WM8915_DSP1TX_VU, WM8915_DSP1TX_VU);
- snd_soc_update_bits(codec, WM8915_DSP2_TX_LEFT_VOLUME,
- WM8915_DSP2TX_VU, WM8915_DSP2TX_VU);
- snd_soc_update_bits(codec, WM8915_DSP2_TX_RIGHT_VOLUME,
- WM8915_DSP2TX_VU, WM8915_DSP2TX_VU);
-
- snd_soc_update_bits(codec, WM8915_DSP1_RX_LEFT_VOLUME,
- WM8915_DSP1RX_VU, WM8915_DSP1RX_VU);
- snd_soc_update_bits(codec, WM8915_DSP1_RX_RIGHT_VOLUME,
- WM8915_DSP1RX_VU, WM8915_DSP1RX_VU);
- snd_soc_update_bits(codec, WM8915_DSP2_RX_LEFT_VOLUME,
- WM8915_DSP2RX_VU, WM8915_DSP2RX_VU);
- snd_soc_update_bits(codec, WM8915_DSP2_RX_RIGHT_VOLUME,
- WM8915_DSP2RX_VU, WM8915_DSP2RX_VU);
-
- /* No support currently for the underclocked TDM modes and
- * pick a default TDM layout with each channel pair working with
- * slots 0 and 1. */
- snd_soc_update_bits(codec, WM8915_AIF1RX_CHANNEL_0_CONFIGURATION,
- WM8915_AIF1RX_CHAN0_SLOTS_MASK |
- WM8915_AIF1RX_CHAN0_START_SLOT_MASK,
- 1 << WM8915_AIF1RX_CHAN0_SLOTS_SHIFT | 0);
- snd_soc_update_bits(codec, WM8915_AIF1RX_CHANNEL_1_CONFIGURATION,
- WM8915_AIF1RX_CHAN1_SLOTS_MASK |
- WM8915_AIF1RX_CHAN1_START_SLOT_MASK,
- 1 << WM8915_AIF1RX_CHAN1_SLOTS_SHIFT | 1);
- snd_soc_update_bits(codec, WM8915_AIF1RX_CHANNEL_2_CONFIGURATION,
- WM8915_AIF1RX_CHAN2_SLOTS_MASK |
- WM8915_AIF1RX_CHAN2_START_SLOT_MASK,
- 1 << WM8915_AIF1RX_CHAN2_SLOTS_SHIFT | 0);
- snd_soc_update_bits(codec, WM8915_AIF1RX_CHANNEL_3_CONFIGURATION,
- WM8915_AIF1RX_CHAN3_SLOTS_MASK |
- WM8915_AIF1RX_CHAN0_START_SLOT_MASK,
- 1 << WM8915_AIF1RX_CHAN3_SLOTS_SHIFT | 1);
- snd_soc_update_bits(codec, WM8915_AIF1RX_CHANNEL_4_CONFIGURATION,
- WM8915_AIF1RX_CHAN4_SLOTS_MASK |
- WM8915_AIF1RX_CHAN0_START_SLOT_MASK,
- 1 << WM8915_AIF1RX_CHAN4_SLOTS_SHIFT | 0);
- snd_soc_update_bits(codec, WM8915_AIF1RX_CHANNEL_5_CONFIGURATION,
- WM8915_AIF1RX_CHAN5_SLOTS_MASK |
- WM8915_AIF1RX_CHAN0_START_SLOT_MASK,
- 1 << WM8915_AIF1RX_CHAN5_SLOTS_SHIFT | 1);
-
- snd_soc_update_bits(codec, WM8915_AIF2RX_CHANNEL_0_CONFIGURATION,
- WM8915_AIF2RX_CHAN0_SLOTS_MASK |
- WM8915_AIF2RX_CHAN0_START_SLOT_MASK,
- 1 << WM8915_AIF2RX_CHAN0_SLOTS_SHIFT | 0);
- snd_soc_update_bits(codec, WM8915_AIF2RX_CHANNEL_1_CONFIGURATION,
- WM8915_AIF2RX_CHAN1_SLOTS_MASK |
- WM8915_AIF2RX_CHAN1_START_SLOT_MASK,
- 1 << WM8915_AIF2RX_CHAN1_SLOTS_SHIFT | 1);
-
- snd_soc_update_bits(codec, WM8915_AIF1TX_CHANNEL_0_CONFIGURATION,
- WM8915_AIF1TX_CHAN0_SLOTS_MASK |
- WM8915_AIF1TX_CHAN0_START_SLOT_MASK,
- 1 << WM8915_AIF1TX_CHAN0_SLOTS_SHIFT | 0);
- snd_soc_update_bits(codec, WM8915_AIF1TX_CHANNEL_1_CONFIGURATION,
- WM8915_AIF1TX_CHAN1_SLOTS_MASK |
- WM8915_AIF1TX_CHAN0_START_SLOT_MASK,
- 1 << WM8915_AIF1TX_CHAN1_SLOTS_SHIFT | 1);
- snd_soc_update_bits(codec, WM8915_AIF1TX_CHANNEL_2_CONFIGURATION,
- WM8915_AIF1TX_CHAN2_SLOTS_MASK |
- WM8915_AIF1TX_CHAN0_START_SLOT_MASK,
- 1 << WM8915_AIF1TX_CHAN2_SLOTS_SHIFT | 0);
- snd_soc_update_bits(codec, WM8915_AIF1TX_CHANNEL_3_CONFIGURATION,
- WM8915_AIF1TX_CHAN3_SLOTS_MASK |
- WM8915_AIF1TX_CHAN0_START_SLOT_MASK,
- 1 << WM8915_AIF1TX_CHAN3_SLOTS_SHIFT | 1);
- snd_soc_update_bits(codec, WM8915_AIF1TX_CHANNEL_4_CONFIGURATION,
- WM8915_AIF1TX_CHAN4_SLOTS_MASK |
- WM8915_AIF1TX_CHAN0_START_SLOT_MASK,
- 1 << WM8915_AIF1TX_CHAN4_SLOTS_SHIFT | 0);
- snd_soc_update_bits(codec, WM8915_AIF1TX_CHANNEL_5_CONFIGURATION,
- WM8915_AIF1TX_CHAN5_SLOTS_MASK |
- WM8915_AIF1TX_CHAN0_START_SLOT_MASK,
- 1 << WM8915_AIF1TX_CHAN5_SLOTS_SHIFT | 1);
-
- snd_soc_update_bits(codec, WM8915_AIF2TX_CHANNEL_0_CONFIGURATION,
- WM8915_AIF2TX_CHAN0_SLOTS_MASK |
- WM8915_AIF2TX_CHAN0_START_SLOT_MASK,
- 1 << WM8915_AIF2TX_CHAN0_SLOTS_SHIFT | 0);
- snd_soc_update_bits(codec, WM8915_AIF1TX_CHANNEL_1_CONFIGURATION,
- WM8915_AIF2TX_CHAN1_SLOTS_MASK |
- WM8915_AIF2TX_CHAN1_START_SLOT_MASK,
- 1 << WM8915_AIF1TX_CHAN1_SLOTS_SHIFT | 1);
-
- if (wm8915->pdata.num_retune_mobile_cfgs)
- wm8915_retune_mobile_pdata(codec);
- else
- snd_soc_add_controls(codec, wm8915_eq_controls,
- ARRAY_SIZE(wm8915_eq_controls));
-
- /* If the TX LRCLK pins are not in LRCLK mode configure the
- * AIFs to source their clocks from the RX LRCLKs.
- */
- if ((snd_soc_read(codec, WM8915_GPIO_1)))
- snd_soc_update_bits(codec, WM8915_AIF1_TX_LRCLK_2,
- WM8915_AIF1TX_LRCLK_MODE,
- WM8915_AIF1TX_LRCLK_MODE);
-
- if ((snd_soc_read(codec, WM8915_GPIO_2)))
- snd_soc_update_bits(codec, WM8915_AIF2_TX_LRCLK_2,
- WM8915_AIF2TX_LRCLK_MODE,
- WM8915_AIF2TX_LRCLK_MODE);
-
- regulator_bulk_disable(ARRAY_SIZE(wm8915->supplies), wm8915->supplies);
-
- wm8915_init_gpio(codec);
-
- if (i2c->irq) {
- if (wm8915->pdata.irq_flags)
- irq_flags = wm8915->pdata.irq_flags;
- else
- irq_flags = IRQF_TRIGGER_LOW;
-
- irq_flags |= IRQF_ONESHOT;
-
- if (irq_flags & (IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING))
- ret = request_threaded_irq(i2c->irq, NULL,
- wm8915_edge_irq,
- irq_flags, "wm8915", codec);
- else
- ret = request_threaded_irq(i2c->irq, NULL, wm8915_irq,
- irq_flags, "wm8915", codec);
-
- if (ret == 0) {
- /* Unmask the interrupt */
- snd_soc_update_bits(codec, WM8915_INTERRUPT_CONTROL,
- WM8915_IM_IRQ, 0);
-
- /* Enable error reporting and DC servo status */
- snd_soc_update_bits(codec,
- WM8915_INTERRUPT_STATUS_2_MASK,
- WM8915_IM_DCS_DONE_23_EINT |
- WM8915_IM_DCS_DONE_01_EINT |
- WM8915_IM_FLL_LOCK_EINT |
- WM8915_IM_FIFOS_ERR_EINT,
- 0);
- } else {
- dev_err(codec->dev, "Failed to request IRQ: %d\n",
- ret);
- }
- }
-
- return 0;
-
-err_enable:
- if (wm8915->pdata.ldo_ena >= 0)
- gpio_set_value_cansleep(wm8915->pdata.ldo_ena, 0);
-
- regulator_bulk_disable(ARRAY_SIZE(wm8915->supplies), wm8915->supplies);
-err_get:
- regulator_bulk_free(ARRAY_SIZE(wm8915->supplies), wm8915->supplies);
-err:
- return ret;
-}
-
-static int wm8915_remove(struct snd_soc_codec *codec)
-{
- struct wm8915_priv *wm8915 = snd_soc_codec_get_drvdata(codec);
- struct i2c_client *i2c = to_i2c_client(codec->dev);
- int i;
-
- snd_soc_update_bits(codec, WM8915_INTERRUPT_CONTROL,
- WM8915_IM_IRQ, WM8915_IM_IRQ);
-
- if (i2c->irq)
- free_irq(i2c->irq, codec);
-
- wm8915_free_gpio(codec);
-
- for (i = 0; i < ARRAY_SIZE(wm8915->supplies); i++)
- regulator_unregister_notifier(wm8915->supplies[i].consumer,
- &wm8915->disable_nb[i]);
- regulator_bulk_free(ARRAY_SIZE(wm8915->supplies), wm8915->supplies);
-
- return 0;
-}
-
-static struct snd_soc_codec_driver soc_codec_dev_wm8915 = {
- .probe = wm8915_probe,
- .remove = wm8915_remove,
- .set_bias_level = wm8915_set_bias_level,
- .seq_notifier = wm8915_seq_notifier,
- .reg_cache_size = WM8915_MAX_REGISTER + 1,
- .reg_word_size = sizeof(u16),
- .reg_cache_default = wm8915_reg,
- .volatile_register = wm8915_volatile_register,
- .readable_register = wm8915_readable_register,
- .compress_type = SND_SOC_RBTREE_COMPRESSION,
- .controls = wm8915_snd_controls,
- .num_controls = ARRAY_SIZE(wm8915_snd_controls),
- .dapm_widgets = wm8915_dapm_widgets,
- .num_dapm_widgets = ARRAY_SIZE(wm8915_dapm_widgets),
- .dapm_routes = wm8915_dapm_routes,
- .num_dapm_routes = ARRAY_SIZE(wm8915_dapm_routes),
- .set_pll = wm8915_set_fll,
-};
-
-#define WM8915_RATES (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |\
- SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000)
-#define WM8915_FORMATS (SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_S16_LE |\
- SNDRV_PCM_FMTBIT_S20_3LE | SNDRV_PCM_FMTBIT_S24_LE |\
- SNDRV_PCM_FMTBIT_S32_LE)
-
-static struct snd_soc_dai_ops wm8915_dai_ops = {
- .set_fmt = wm8915_set_fmt,
- .hw_params = wm8915_hw_params,
- .set_sysclk = wm8915_set_sysclk,
-};
-
-static struct snd_soc_dai_driver wm8915_dai[] = {
- {
- .name = "wm8915-aif1",
- .playback = {
- .stream_name = "AIF1 Playback",
- .channels_min = 1,
- .channels_max = 6,
- .rates = WM8915_RATES,
- .formats = WM8915_FORMATS,
- },
- .capture = {
- .stream_name = "AIF1 Capture",
- .channels_min = 1,
- .channels_max = 6,
- .rates = WM8915_RATES,
- .formats = WM8915_FORMATS,
- },
- .ops = &wm8915_dai_ops,
- },
- {
- .name = "wm8915-aif2",
- .playback = {
- .stream_name = "AIF2 Playback",
- .channels_min = 1,
- .channels_max = 2,
- .rates = WM8915_RATES,
- .formats = WM8915_FORMATS,
- },
- .capture = {
- .stream_name = "AIF2 Capture",
- .channels_min = 1,
- .channels_max = 2,
- .rates = WM8915_RATES,
- .formats = WM8915_FORMATS,
- },
- .ops = &wm8915_dai_ops,
- },
-};
-
-static __devinit int wm8915_i2c_probe(struct i2c_client *i2c,
- const struct i2c_device_id *id)
-{
- struct wm8915_priv *wm8915;
- int ret;
-
- wm8915 = kzalloc(sizeof(struct wm8915_priv), GFP_KERNEL);
- if (wm8915 == NULL)
- return -ENOMEM;
-
- i2c_set_clientdata(i2c, wm8915);
-
- if (dev_get_platdata(&i2c->dev))
- memcpy(&wm8915->pdata, dev_get_platdata(&i2c->dev),
- sizeof(wm8915->pdata));
-
- if (wm8915->pdata.ldo_ena > 0) {
- ret = gpio_request_one(wm8915->pdata.ldo_ena,
- GPIOF_OUT_INIT_LOW, "WM8915 ENA");
- if (ret < 0) {
- dev_err(&i2c->dev, "Failed to request GPIO %d: %d\n",
- wm8915->pdata.ldo_ena, ret);
- goto err;
- }
- }
-
- ret = snd_soc_register_codec(&i2c->dev,
- &soc_codec_dev_wm8915, wm8915_dai,
- ARRAY_SIZE(wm8915_dai));
- if (ret < 0)
- goto err_gpio;
-
- return ret;
-
-err_gpio:
- if (wm8915->pdata.ldo_ena > 0)
- gpio_free(wm8915->pdata.ldo_ena);
-err:
- kfree(wm8915);
-
- return ret;
-}
-
-static __devexit int wm8915_i2c_remove(struct i2c_client *client)
-{
- struct wm8915_priv *wm8915 = i2c_get_clientdata(client);
-
- snd_soc_unregister_codec(&client->dev);
- if (wm8915->pdata.ldo_ena > 0)
- gpio_free(wm8915->pdata.ldo_ena);
- kfree(i2c_get_clientdata(client));
- return 0;
-}
-
-static const struct i2c_device_id wm8915_i2c_id[] = {
- { "wm8915", 0 },
- { }
-};
-MODULE_DEVICE_TABLE(i2c, wm8915_i2c_id);
-
-static struct i2c_driver wm8915_i2c_driver = {
- .driver = {
- .name = "wm8915",
- .owner = THIS_MODULE,
- },
- .probe = wm8915_i2c_probe,
- .remove = __devexit_p(wm8915_i2c_remove),
- .id_table = wm8915_i2c_id,
-};
-
-static int __init wm8915_modinit(void)
-{
- int ret;
-
- ret = i2c_add_driver(&wm8915_i2c_driver);
- if (ret != 0) {
- printk(KERN_ERR "Failed to register WM8915 I2C driver: %d\n",
- ret);
- }
-
- return ret;
-}
-module_init(wm8915_modinit);
-
-static void __exit wm8915_exit(void)
-{
- i2c_del_driver(&wm8915_i2c_driver);
-}
-module_exit(wm8915_exit);
-
-MODULE_DESCRIPTION("ASoC WM8915 driver");
-MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>");
-MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/wm8915.h b/sound/soc/codecs/wm8915.h
deleted file mode 100644
index 200ffd7..0000000
--- a/sound/soc/codecs/wm8915.h
+++ /dev/null
@@ -1,3717 +0,0 @@
-/*
- * wm8915.h - WM8915 audio codec interface
- *
- * Copyright 2011 Wolfson Microelectronics PLC.
- * Author: Mark Brown <broonie@opensource.wolfsonmicro.com>
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the
- * Free Software Foundation; either version 2 of the License, or (at your
- * option) any later version.
- */
-
-#ifndef _WM8915_H
-#define _WM8915_H
-
-#define WM8915_SYSCLK_MCLK1 1
-#define WM8915_SYSCLK_MCLK2 2
-#define WM8915_SYSCLK_FLL 3
-
-#define WM8915_FLL_MCLK1 1
-#define WM8915_FLL_MCLK2 2
-#define WM8915_FLL_DACLRCLK1 3
-#define WM8915_FLL_BCLK1 4
-
-typedef void (*wm8915_polarity_fn)(struct snd_soc_codec *codec, int polarity);
-
-int wm8915_detect(struct snd_soc_codec *codec, struct snd_soc_jack *jack,
- wm8915_polarity_fn polarity_cb);
-
-/*
- * Register values.
- */
-#define WM8915_SOFTWARE_RESET 0x00
-#define WM8915_POWER_MANAGEMENT_1 0x01
-#define WM8915_POWER_MANAGEMENT_2 0x02
-#define WM8915_POWER_MANAGEMENT_3 0x03
-#define WM8915_POWER_MANAGEMENT_4 0x04
-#define WM8915_POWER_MANAGEMENT_5 0x05
-#define WM8915_POWER_MANAGEMENT_6 0x06
-#define WM8915_POWER_MANAGEMENT_7 0x07
-#define WM8915_POWER_MANAGEMENT_8 0x08
-#define WM8915_LEFT_LINE_INPUT_VOLUME 0x10
-#define WM8915_RIGHT_LINE_INPUT_VOLUME 0x11
-#define WM8915_LINE_INPUT_CONTROL 0x12
-#define WM8915_DAC1_HPOUT1_VOLUME 0x15
-#define WM8915_DAC2_HPOUT2_VOLUME 0x16
-#define WM8915_DAC1_LEFT_VOLUME 0x18
-#define WM8915_DAC1_RIGHT_VOLUME 0x19
-#define WM8915_DAC2_LEFT_VOLUME 0x1A
-#define WM8915_DAC2_RIGHT_VOLUME 0x1B
-#define WM8915_OUTPUT1_LEFT_VOLUME 0x1C
-#define WM8915_OUTPUT1_RIGHT_VOLUME 0x1D
-#define WM8915_OUTPUT2_LEFT_VOLUME 0x1E
-#define WM8915_OUTPUT2_RIGHT_VOLUME 0x1F
-#define WM8915_MICBIAS_1 0x20
-#define WM8915_MICBIAS_2 0x21
-#define WM8915_LDO_1 0x28
-#define WM8915_LDO_2 0x29
-#define WM8915_ACCESSORY_DETECT_MODE_1 0x30
-#define WM8915_ACCESSORY_DETECT_MODE_2 0x31
-#define WM8915_HEADPHONE_DETECT_1 0x34
-#define WM8915_HEADPHONE_DETECT_2 0x35
-#define WM8915_MIC_DETECT_1 0x38
-#define WM8915_MIC_DETECT_2 0x39
-#define WM8915_MIC_DETECT_3 0x3A
-#define WM8915_CHARGE_PUMP_1 0x40
-#define WM8915_CHARGE_PUMP_2 0x41
-#define WM8915_DC_SERVO_1 0x50
-#define WM8915_DC_SERVO_2 0x51
-#define WM8915_DC_SERVO_3 0x52
-#define WM8915_DC_SERVO_5 0x54
-#define WM8915_DC_SERVO_6 0x55
-#define WM8915_DC_SERVO_7 0x56
-#define WM8915_DC_SERVO_READBACK_0 0x57
-#define WM8915_ANALOGUE_HP_1 0x60
-#define WM8915_ANALOGUE_HP_2 0x61
-#define WM8915_CHIP_REVISION 0x100
-#define WM8915_CONTROL_INTERFACE_1 0x101
-#define WM8915_WRITE_SEQUENCER_CTRL_1 0x110
-#define WM8915_WRITE_SEQUENCER_CTRL_2 0x111
-#define WM8915_AIF_CLOCKING_1 0x200
-#define WM8915_AIF_CLOCKING_2 0x201
-#define WM8915_CLOCKING_1 0x208
-#define WM8915_CLOCKING_2 0x209
-#define WM8915_AIF_RATE 0x210
-#define WM8915_FLL_CONTROL_1 0x220
-#define WM8915_FLL_CONTROL_2 0x221
-#define WM8915_FLL_CONTROL_3 0x222
-#define WM8915_FLL_CONTROL_4 0x223
-#define WM8915_FLL_CONTROL_5 0x224
-#define WM8915_FLL_CONTROL_6 0x225
-#define WM8915_FLL_EFS_1 0x226
-#define WM8915_FLL_EFS_2 0x227
-#define WM8915_AIF1_CONTROL 0x300
-#define WM8915_AIF1_BCLK 0x301
-#define WM8915_AIF1_TX_LRCLK_1 0x302
-#define WM8915_AIF1_TX_LRCLK_2 0x303
-#define WM8915_AIF1_RX_LRCLK_1 0x304
-#define WM8915_AIF1_RX_LRCLK_2 0x305
-#define WM8915_AIF1TX_DATA_CONFIGURATION_1 0x306
-#define WM8915_AIF1TX_DATA_CONFIGURATION_2 0x307
-#define WM8915_AIF1RX_DATA_CONFIGURATION 0x308
-#define WM8915_AIF1TX_CHANNEL_0_CONFIGURATION 0x309
-#define WM8915_AIF1TX_CHANNEL_1_CONFIGURATION 0x30A
-#define WM8915_AIF1TX_CHANNEL_2_CONFIGURATION 0x30B
-#define WM8915_AIF1TX_CHANNEL_3_CONFIGURATION 0x30C
-#define WM8915_AIF1TX_CHANNEL_4_CONFIGURATION 0x30D
-#define WM8915_AIF1TX_CHANNEL_5_CONFIGURATION 0x30E
-#define WM8915_AIF1RX_CHANNEL_0_CONFIGURATION 0x30F
-#define WM8915_AIF1RX_CHANNEL_1_CONFIGURATION 0x310
-#define WM8915_AIF1RX_CHANNEL_2_CONFIGURATION 0x311
-#define WM8915_AIF1RX_CHANNEL_3_CONFIGURATION 0x312
-#define WM8915_AIF1RX_CHANNEL_4_CONFIGURATION 0x313
-#define WM8915_AIF1RX_CHANNEL_5_CONFIGURATION 0x314
-#define WM8915_AIF1RX_MONO_CONFIGURATION 0x315
-#define WM8915_AIF1TX_TEST 0x31A
-#define WM8915_AIF2_CONTROL 0x320
-#define WM8915_AIF2_BCLK 0x321
-#define WM8915_AIF2_TX_LRCLK_1 0x322
-#define WM8915_AIF2_TX_LRCLK_2 0x323
-#define WM8915_AIF2_RX_LRCLK_1 0x324
-#define WM8915_AIF2_RX_LRCLK_2 0x325
-#define WM8915_AIF2TX_DATA_CONFIGURATION_1 0x326
-#define WM8915_AIF2TX_DATA_CONFIGURATION_2 0x327
-#define WM8915_AIF2RX_DATA_CONFIGURATION 0x328
-#define WM8915_AIF2TX_CHANNEL_0_CONFIGURATION 0x329
-#define WM8915_AIF2TX_CHANNEL_1_CONFIGURATION 0x32A
-#define WM8915_AIF2RX_CHANNEL_0_CONFIGURATION 0x32B
-#define WM8915_AIF2RX_CHANNEL_1_CONFIGURATION 0x32C
-#define WM8915_AIF2RX_MONO_CONFIGURATION 0x32D
-#define WM8915_AIF2TX_TEST 0x32F
-#define WM8915_DSP1_TX_LEFT_VOLUME 0x400
-#define WM8915_DSP1_TX_RIGHT_VOLUME 0x401
-#define WM8915_DSP1_RX_LEFT_VOLUME 0x402
-#define WM8915_DSP1_RX_RIGHT_VOLUME 0x403
-#define WM8915_DSP1_TX_FILTERS 0x410
-#define WM8915_DSP1_RX_FILTERS_1 0x420
-#define WM8915_DSP1_RX_FILTERS_2 0x421
-#define WM8915_DSP1_DRC_1 0x440
-#define WM8915_DSP1_DRC_2 0x441
-#define WM8915_DSP1_DRC_3 0x442
-#define WM8915_DSP1_DRC_4 0x443
-#define WM8915_DSP1_DRC_5 0x444
-#define WM8915_DSP1_RX_EQ_GAINS_1 0x480
-#define WM8915_DSP1_RX_EQ_GAINS_2 0x481
-#define WM8915_DSP1_RX_EQ_BAND_1_A 0x482
-#define WM8915_DSP1_RX_EQ_BAND_1_B 0x483
-#define WM8915_DSP1_RX_EQ_BAND_1_PG 0x484
-#define WM8915_DSP1_RX_EQ_BAND_2_A 0x485
-#define WM8915_DSP1_RX_EQ_BAND_2_B 0x486
-#define WM8915_DSP1_RX_EQ_BAND_2_C 0x487
-#define WM8915_DSP1_RX_EQ_BAND_2_PG 0x488
-#define WM8915_DSP1_RX_EQ_BAND_3_A 0x489
-#define WM8915_DSP1_RX_EQ_BAND_3_B 0x48A
-#define WM8915_DSP1_RX_EQ_BAND_3_C 0x48B
-#define WM8915_DSP1_RX_EQ_BAND_3_PG 0x48C
-#define WM8915_DSP1_RX_EQ_BAND_4_A 0x48D
-#define WM8915_DSP1_RX_EQ_BAND_4_B 0x48E
-#define WM8915_DSP1_RX_EQ_BAND_4_C 0x48F
-#define WM8915_DSP1_RX_EQ_BAND_4_PG 0x490
-#define WM8915_DSP1_RX_EQ_BAND_5_A 0x491
-#define WM8915_DSP1_RX_EQ_BAND_5_B 0x492
-#define WM8915_DSP1_RX_EQ_BAND_5_PG 0x493
-#define WM8915_DSP2_TX_LEFT_VOLUME 0x500
-#define WM8915_DSP2_TX_RIGHT_VOLUME 0x501
-#define WM8915_DSP2_RX_LEFT_VOLUME 0x502
-#define WM8915_DSP2_RX_RIGHT_VOLUME 0x503
-#define WM8915_DSP2_TX_FILTERS 0x510
-#define WM8915_DSP2_RX_FILTERS_1 0x520
-#define WM8915_DSP2_RX_FILTERS_2 0x521
-#define WM8915_DSP2_DRC_1 0x540
-#define WM8915_DSP2_DRC_2 0x541
-#define WM8915_DSP2_DRC_3 0x542
-#define WM8915_DSP2_DRC_4 0x543
-#define WM8915_DSP2_DRC_5 0x544
-#define WM8915_DSP2_RX_EQ_GAINS_1 0x580
-#define WM8915_DSP2_RX_EQ_GAINS_2 0x581
-#define WM8915_DSP2_RX_EQ_BAND_1_A 0x582
-#define WM8915_DSP2_RX_EQ_BAND_1_B 0x583
-#define WM8915_DSP2_RX_EQ_BAND_1_PG 0x584
-#define WM8915_DSP2_RX_EQ_BAND_2_A 0x585
-#define WM8915_DSP2_RX_EQ_BAND_2_B 0x586
-#define WM8915_DSP2_RX_EQ_BAND_2_C 0x587
-#define WM8915_DSP2_RX_EQ_BAND_2_PG 0x588
-#define WM8915_DSP2_RX_EQ_BAND_3_A 0x589
-#define WM8915_DSP2_RX_EQ_BAND_3_B 0x58A
-#define WM8915_DSP2_RX_EQ_BAND_3_C 0x58B
-#define WM8915_DSP2_RX_EQ_BAND_3_PG 0x58C
-#define WM8915_DSP2_RX_EQ_BAND_4_A 0x58D
-#define WM8915_DSP2_RX_EQ_BAND_4_B 0x58E
-#define WM8915_DSP2_RX_EQ_BAND_4_C 0x58F
-#define WM8915_DSP2_RX_EQ_BAND_4_PG 0x590
-#define WM8915_DSP2_RX_EQ_BAND_5_A 0x591
-#define WM8915_DSP2_RX_EQ_BAND_5_B 0x592
-#define WM8915_DSP2_RX_EQ_BAND_5_PG 0x593
-#define WM8915_DAC1_MIXER_VOLUMES 0x600
-#define WM8915_DAC1_LEFT_MIXER_ROUTING 0x601
-#define WM8915_DAC1_RIGHT_MIXER_ROUTING 0x602
-#define WM8915_DAC2_MIXER_VOLUMES 0x603
-#define WM8915_DAC2_LEFT_MIXER_ROUTING 0x604
-#define WM8915_DAC2_RIGHT_MIXER_ROUTING 0x605
-#define WM8915_DSP1_TX_LEFT_MIXER_ROUTING 0x606
-#define WM8915_DSP1_TX_RIGHT_MIXER_ROUTING 0x607
-#define WM8915_DSP2_TX_LEFT_MIXER_ROUTING 0x608
-#define WM8915_DSP2_TX_RIGHT_MIXER_ROUTING 0x609
-#define WM8915_DSP_TX_MIXER_SELECT 0x60A
-#define WM8915_DAC_SOFTMUTE 0x610
-#define WM8915_OVERSAMPLING 0x620
-#define WM8915_SIDETONE 0x621
-#define WM8915_GPIO_1 0x700
-#define WM8915_GPIO_2 0x701
-#define WM8915_GPIO_3 0x702
-#define WM8915_GPIO_4 0x703
-#define WM8915_GPIO_5 0x704
-#define WM8915_PULL_CONTROL_1 0x720
-#define WM8915_PULL_CONTROL_2 0x721
-#define WM8915_INTERRUPT_STATUS_1 0x730
-#define WM8915_INTERRUPT_STATUS_2 0x731
-#define WM8915_INTERRUPT_RAW_STATUS_2 0x732
-#define WM8915_INTERRUPT_STATUS_1_MASK 0x738
-#define WM8915_INTERRUPT_STATUS_2_MASK 0x739
-#define WM8915_INTERRUPT_CONTROL 0x740
-#define WM8915_LEFT_PDM_SPEAKER 0x800
-#define WM8915_RIGHT_PDM_SPEAKER 0x801
-#define WM8915_PDM_SPEAKER_MUTE_SEQUENCE 0x802
-#define WM8915_PDM_SPEAKER_VOLUME 0x803
-#define WM8915_WRITE_SEQUENCER_0 0x3000
-#define WM8915_WRITE_SEQUENCER_1 0x3001
-#define WM8915_WRITE_SEQUENCER_2 0x3002
-#define WM8915_WRITE_SEQUENCER_3 0x3003
-#define WM8915_WRITE_SEQUENCER_4 0x3004
-#define WM8915_WRITE_SEQUENCER_5 0x3005
-#define WM8915_WRITE_SEQUENCER_6 0x3006
-#define WM8915_WRITE_SEQUENCER_7 0x3007
-#define WM8915_WRITE_SEQUENCER_8 0x3008
-#define WM8915_WRITE_SEQUENCER_9 0x3009
-#define WM8915_WRITE_SEQUENCER_10 0x300A
-#define WM8915_WRITE_SEQUENCER_11 0x300B
-#define WM8915_WRITE_SEQUENCER_12 0x300C
-#define WM8915_WRITE_SEQUENCER_13 0x300D
-#define WM8915_WRITE_SEQUENCER_14 0x300E
-#define WM8915_WRITE_SEQUENCER_15 0x300F
-#define WM8915_WRITE_SEQUENCER_16 0x3010
-#define WM8915_WRITE_SEQUENCER_17 0x3011
-#define WM8915_WRITE_SEQUENCER_18 0x3012
-#define WM8915_WRITE_SEQUENCER_19 0x3013
-#define WM8915_WRITE_SEQUENCER_20 0x3014
-#define WM8915_WRITE_SEQUENCER_21 0x3015
-#define WM8915_WRITE_SEQUENCER_22 0x3016
-#define WM8915_WRITE_SEQUENCER_23 0x3017
-#define WM8915_WRITE_SEQUENCER_24 0x3018
-#define WM8915_WRITE_SEQUENCER_25 0x3019
-#define WM8915_WRITE_SEQUENCER_26 0x301A
-#define WM8915_WRITE_SEQUENCER_27 0x301B
-#define WM8915_WRITE_SEQUENCER_28 0x301C
-#define WM8915_WRITE_SEQUENCER_29 0x301D
-#define WM8915_WRITE_SEQUENCER_30 0x301E
-#define WM8915_WRITE_SEQUENCER_31 0x301F
-#define WM8915_WRITE_SEQUENCER_32 0x3020
-#define WM8915_WRITE_SEQUENCER_33 0x3021
-#define WM8915_WRITE_SEQUENCER_34 0x3022
-#define WM8915_WRITE_SEQUENCER_35 0x3023
-#define WM8915_WRITE_SEQUENCER_36 0x3024
-#define WM8915_WRITE_SEQUENCER_37 0x3025
-#define WM8915_WRITE_SEQUENCER_38 0x3026
-#define WM8915_WRITE_SEQUENCER_39 0x3027
-#define WM8915_WRITE_SEQUENCER_40 0x3028
-#define WM8915_WRITE_SEQUENCER_41 0x3029
-#define WM8915_WRITE_SEQUENCER_42 0x302A
-#define WM8915_WRITE_SEQUENCER_43 0x302B
-#define WM8915_WRITE_SEQUENCER_44 0x302C
-#define WM8915_WRITE_SEQUENCER_45 0x302D
-#define WM8915_WRITE_SEQUENCER_46 0x302E
-#define WM8915_WRITE_SEQUENCER_47 0x302F
-#define WM8915_WRITE_SEQUENCER_48 0x3030
-#define WM8915_WRITE_SEQUENCER_49 0x3031
-#define WM8915_WRITE_SEQUENCER_50 0x3032
-#define WM8915_WRITE_SEQUENCER_51 0x3033
-#define WM8915_WRITE_SEQUENCER_52 0x3034
-#define WM8915_WRITE_SEQUENCER_53 0x3035
-#define WM8915_WRITE_SEQUENCER_54 0x3036
-#define WM8915_WRITE_SEQUENCER_55 0x3037
-#define WM8915_WRITE_SEQUENCER_56 0x3038
-#define WM8915_WRITE_SEQUENCER_57 0x3039
-#define WM8915_WRITE_SEQUENCER_58 0x303A
-#define WM8915_WRITE_SEQUENCER_59 0x303B
-#define WM8915_WRITE_SEQUENCER_60 0x303C
-#define WM8915_WRITE_SEQUENCER_61 0x303D
-#define WM8915_WRITE_SEQUENCER_62 0x303E
-#define WM8915_WRITE_SEQUENCER_63 0x303F
-#define WM8915_WRITE_SEQUENCER_64 0x3040
-#define WM8915_WRITE_SEQUENCER_65 0x3041
-#define WM8915_WRITE_SEQUENCER_66 0x3042
-#define WM8915_WRITE_SEQUENCER_67 0x3043
-#define WM8915_WRITE_SEQUENCER_68 0x3044
-#define WM8915_WRITE_SEQUENCER_69 0x3045
-#define WM8915_WRITE_SEQUENCER_70 0x3046
-#define WM8915_WRITE_SEQUENCER_71 0x3047
-#define WM8915_WRITE_SEQUENCER_72 0x3048
-#define WM8915_WRITE_SEQUENCER_73 0x3049
-#define WM8915_WRITE_SEQUENCER_74 0x304A
-#define WM8915_WRITE_SEQUENCER_75 0x304B
-#define WM8915_WRITE_SEQUENCER_76 0x304C
-#define WM8915_WRITE_SEQUENCER_77 0x304D
-#define WM8915_WRITE_SEQUENCER_78 0x304E
-#define WM8915_WRITE_SEQUENCER_79 0x304F
-#define WM8915_WRITE_SEQUENCER_80 0x3050
-#define WM8915_WRITE_SEQUENCER_81 0x3051
-#define WM8915_WRITE_SEQUENCER_82 0x3052
-#define WM8915_WRITE_SEQUENCER_83 0x3053
-#define WM8915_WRITE_SEQUENCER_84 0x3054
-#define WM8915_WRITE_SEQUENCER_85 0x3055
-#define WM8915_WRITE_SEQUENCER_86 0x3056
-#define WM8915_WRITE_SEQUENCER_87 0x3057
-#define WM8915_WRITE_SEQUENCER_88 0x3058
-#define WM8915_WRITE_SEQUENCER_89 0x3059
-#define WM8915_WRITE_SEQUENCER_90 0x305A
-#define WM8915_WRITE_SEQUENCER_91 0x305B
-#define WM8915_WRITE_SEQUENCER_92 0x305C
-#define WM8915_WRITE_SEQUENCER_93 0x305D
-#define WM8915_WRITE_SEQUENCER_94 0x305E
-#define WM8915_WRITE_SEQUENCER_95 0x305F
-#define WM8915_WRITE_SEQUENCER_96 0x3060
-#define WM8915_WRITE_SEQUENCER_97 0x3061
-#define WM8915_WRITE_SEQUENCER_98 0x3062
-#define WM8915_WRITE_SEQUENCER_99 0x3063
-#define WM8915_WRITE_SEQUENCER_100 0x3064
-#define WM8915_WRITE_SEQUENCER_101 0x3065
-#define WM8915_WRITE_SEQUENCER_102 0x3066
-#define WM8915_WRITE_SEQUENCER_103 0x3067
-#define WM8915_WRITE_SEQUENCER_104 0x3068
-#define WM8915_WRITE_SEQUENCER_105 0x3069
-#define WM8915_WRITE_SEQUENCER_106 0x306A
-#define WM8915_WRITE_SEQUENCER_107 0x306B
-#define WM8915_WRITE_SEQUENCER_108 0x306C
-#define WM8915_WRITE_SEQUENCER_109 0x306D
-#define WM8915_WRITE_SEQUENCER_110 0x306E
-#define WM8915_WRITE_SEQUENCER_111 0x306F
-#define WM8915_WRITE_SEQUENCER_112 0x3070
-#define WM8915_WRITE_SEQUENCER_113 0x3071
-#define WM8915_WRITE_SEQUENCER_114 0x3072
-#define WM8915_WRITE_SEQUENCER_115 0x3073
-#define WM8915_WRITE_SEQUENCER_116 0x3074
-#define WM8915_WRITE_SEQUENCER_117 0x3075
-#define WM8915_WRITE_SEQUENCER_118 0x3076
-#define WM8915_WRITE_SEQUENCER_119 0x3077
-#define WM8915_WRITE_SEQUENCER_120 0x3078
-#define WM8915_WRITE_SEQUENCER_121 0x3079
-#define WM8915_WRITE_SEQUENCER_122 0x307A
-#define WM8915_WRITE_SEQUENCER_123 0x307B
-#define WM8915_WRITE_SEQUENCER_124 0x307C
-#define WM8915_WRITE_SEQUENCER_125 0x307D
-#define WM8915_WRITE_SEQUENCER_126 0x307E
-#define WM8915_WRITE_SEQUENCER_127 0x307F
-#define WM8915_WRITE_SEQUENCER_128 0x3080
-#define WM8915_WRITE_SEQUENCER_129 0x3081
-#define WM8915_WRITE_SEQUENCER_130 0x3082
-#define WM8915_WRITE_SEQUENCER_131 0x3083
-#define WM8915_WRITE_SEQUENCER_132 0x3084
-#define WM8915_WRITE_SEQUENCER_133 0x3085
-#define WM8915_WRITE_SEQUENCER_134 0x3086
-#define WM8915_WRITE_SEQUENCER_135 0x3087
-#define WM8915_WRITE_SEQUENCER_136 0x3088
-#define WM8915_WRITE_SEQUENCER_137 0x3089
-#define WM8915_WRITE_SEQUENCER_138 0x308A
-#define WM8915_WRITE_SEQUENCER_139 0x308B
-#define WM8915_WRITE_SEQUENCER_140 0x308C
-#define WM8915_WRITE_SEQUENCER_141 0x308D
-#define WM8915_WRITE_SEQUENCER_142 0x308E
-#define WM8915_WRITE_SEQUENCER_143 0x308F
-#define WM8915_WRITE_SEQUENCER_144 0x3090
-#define WM8915_WRITE_SEQUENCER_145 0x3091
-#define WM8915_WRITE_SEQUENCER_146 0x3092
-#define WM8915_WRITE_SEQUENCER_147 0x3093
-#define WM8915_WRITE_SEQUENCER_148 0x3094
-#define WM8915_WRITE_SEQUENCER_149 0x3095
-#define WM8915_WRITE_SEQUENCER_150 0x3096
-#define WM8915_WRITE_SEQUENCER_151 0x3097
-#define WM8915_WRITE_SEQUENCER_152 0x3098
-#define WM8915_WRITE_SEQUENCER_153 0x3099
-#define WM8915_WRITE_SEQUENCER_154 0x309A
-#define WM8915_WRITE_SEQUENCER_155 0x309B
-#define WM8915_WRITE_SEQUENCER_156 0x309C
-#define WM8915_WRITE_SEQUENCER_157 0x309D
-#define WM8915_WRITE_SEQUENCER_158 0x309E
-#define WM8915_WRITE_SEQUENCER_159 0x309F
-#define WM8915_WRITE_SEQUENCER_160 0x30A0
-#define WM8915_WRITE_SEQUENCER_161 0x30A1
-#define WM8915_WRITE_SEQUENCER_162 0x30A2
-#define WM8915_WRITE_SEQUENCER_163 0x30A3
-#define WM8915_WRITE_SEQUENCER_164 0x30A4
-#define WM8915_WRITE_SEQUENCER_165 0x30A5
-#define WM8915_WRITE_SEQUENCER_166 0x30A6
-#define WM8915_WRITE_SEQUENCER_167 0x30A7
-#define WM8915_WRITE_SEQUENCER_168 0x30A8
-#define WM8915_WRITE_SEQUENCER_169 0x30A9
-#define WM8915_WRITE_SEQUENCER_170 0x30AA
-#define WM8915_WRITE_SEQUENCER_171 0x30AB
-#define WM8915_WRITE_SEQUENCER_172 0x30AC
-#define WM8915_WRITE_SEQUENCER_173 0x30AD
-#define WM8915_WRITE_SEQUENCER_174 0x30AE
-#define WM8915_WRITE_SEQUENCER_175 0x30AF
-#define WM8915_WRITE_SEQUENCER_176 0x30B0
-#define WM8915_WRITE_SEQUENCER_177 0x30B1
-#define WM8915_WRITE_SEQUENCER_178 0x30B2
-#define WM8915_WRITE_SEQUENCER_179 0x30B3
-#define WM8915_WRITE_SEQUENCER_180 0x30B4
-#define WM8915_WRITE_SEQUENCER_181 0x30B5
-#define WM8915_WRITE_SEQUENCER_182 0x30B6
-#define WM8915_WRITE_SEQUENCER_183 0x30B7
-#define WM8915_WRITE_SEQUENCER_184 0x30B8
-#define WM8915_WRITE_SEQUENCER_185 0x30B9
-#define WM8915_WRITE_SEQUENCER_186 0x30BA
-#define WM8915_WRITE_SEQUENCER_187 0x30BB
-#define WM8915_WRITE_SEQUENCER_188 0x30BC
-#define WM8915_WRITE_SEQUENCER_189 0x30BD
-#define WM8915_WRITE_SEQUENCER_190 0x30BE
-#define WM8915_WRITE_SEQUENCER_191 0x30BF
-#define WM8915_WRITE_SEQUENCER_192 0x30C0
-#define WM8915_WRITE_SEQUENCER_193 0x30C1
-#define WM8915_WRITE_SEQUENCER_194 0x30C2
-#define WM8915_WRITE_SEQUENCER_195 0x30C3
-#define WM8915_WRITE_SEQUENCER_196 0x30C4
-#define WM8915_WRITE_SEQUENCER_197 0x30C5
-#define WM8915_WRITE_SEQUENCER_198 0x30C6
-#define WM8915_WRITE_SEQUENCER_199 0x30C7
-#define WM8915_WRITE_SEQUENCER_200 0x30C8
-#define WM8915_WRITE_SEQUENCER_201 0x30C9
-#define WM8915_WRITE_SEQUENCER_202 0x30CA
-#define WM8915_WRITE_SEQUENCER_203 0x30CB
-#define WM8915_WRITE_SEQUENCER_204 0x30CC
-#define WM8915_WRITE_SEQUENCER_205 0x30CD
-#define WM8915_WRITE_SEQUENCER_206 0x30CE
-#define WM8915_WRITE_SEQUENCER_207 0x30CF
-#define WM8915_WRITE_SEQUENCER_208 0x30D0
-#define WM8915_WRITE_SEQUENCER_209 0x30D1
-#define WM8915_WRITE_SEQUENCER_210 0x30D2
-#define WM8915_WRITE_SEQUENCER_211 0x30D3
-#define WM8915_WRITE_SEQUENCER_212 0x30D4
-#define WM8915_WRITE_SEQUENCER_213 0x30D5
-#define WM8915_WRITE_SEQUENCER_214 0x30D6
-#define WM8915_WRITE_SEQUENCER_215 0x30D7
-#define WM8915_WRITE_SEQUENCER_216 0x30D8
-#define WM8915_WRITE_SEQUENCER_217 0x30D9
-#define WM8915_WRITE_SEQUENCER_218 0x30DA
-#define WM8915_WRITE_SEQUENCER_219 0x30DB
-#define WM8915_WRITE_SEQUENCER_220 0x30DC
-#define WM8915_WRITE_SEQUENCER_221 0x30DD
-#define WM8915_WRITE_SEQUENCER_222 0x30DE
-#define WM8915_WRITE_SEQUENCER_223 0x30DF
-#define WM8915_WRITE_SEQUENCER_224 0x30E0
-#define WM8915_WRITE_SEQUENCER_225 0x30E1
-#define WM8915_WRITE_SEQUENCER_226 0x30E2
-#define WM8915_WRITE_SEQUENCER_227 0x30E3
-#define WM8915_WRITE_SEQUENCER_228 0x30E4
-#define WM8915_WRITE_SEQUENCER_229 0x30E5
-#define WM8915_WRITE_SEQUENCER_230 0x30E6
-#define WM8915_WRITE_SEQUENCER_231 0x30E7
-#define WM8915_WRITE_SEQUENCER_232 0x30E8
-#define WM8915_WRITE_SEQUENCER_233 0x30E9
-#define WM8915_WRITE_SEQUENCER_234 0x30EA
-#define WM8915_WRITE_SEQUENCER_235 0x30EB
-#define WM8915_WRITE_SEQUENCER_236 0x30EC
-#define WM8915_WRITE_SEQUENCER_237 0x30ED
-#define WM8915_WRITE_SEQUENCER_238 0x30EE
-#define WM8915_WRITE_SEQUENCER_239 0x30EF
-#define WM8915_WRITE_SEQUENCER_240 0x30F0
-#define WM8915_WRITE_SEQUENCER_241 0x30F1
-#define WM8915_WRITE_SEQUENCER_242 0x30F2
-#define WM8915_WRITE_SEQUENCER_243 0x30F3
-#define WM8915_WRITE_SEQUENCER_244 0x30F4
-#define WM8915_WRITE_SEQUENCER_245 0x30F5
-#define WM8915_WRITE_SEQUENCER_246 0x30F6
-#define WM8915_WRITE_SEQUENCER_247 0x30F7
-#define WM8915_WRITE_SEQUENCER_248 0x30F8
-#define WM8915_WRITE_SEQUENCER_249 0x30F9
-#define WM8915_WRITE_SEQUENCER_250 0x30FA
-#define WM8915_WRITE_SEQUENCER_251 0x30FB
-#define WM8915_WRITE_SEQUENCER_252 0x30FC
-#define WM8915_WRITE_SEQUENCER_253 0x30FD
-#define WM8915_WRITE_SEQUENCER_254 0x30FE
-#define WM8915_WRITE_SEQUENCER_255 0x30FF
-#define WM8915_WRITE_SEQUENCER_256 0x3100
-#define WM8915_WRITE_SEQUENCER_257 0x3101
-#define WM8915_WRITE_SEQUENCER_258 0x3102
-#define WM8915_WRITE_SEQUENCER_259 0x3103
-#define WM8915_WRITE_SEQUENCER_260 0x3104
-#define WM8915_WRITE_SEQUENCER_261 0x3105
-#define WM8915_WRITE_SEQUENCER_262 0x3106
-#define WM8915_WRITE_SEQUENCER_263 0x3107
-#define WM8915_WRITE_SEQUENCER_264 0x3108
-#define WM8915_WRITE_SEQUENCER_265 0x3109
-#define WM8915_WRITE_SEQUENCER_266 0x310A
-#define WM8915_WRITE_SEQUENCER_267 0x310B
-#define WM8915_WRITE_SEQUENCER_268 0x310C
-#define WM8915_WRITE_SEQUENCER_269 0x310D
-#define WM8915_WRITE_SEQUENCER_270 0x310E
-#define WM8915_WRITE_SEQUENCER_271 0x310F
-#define WM8915_WRITE_SEQUENCER_272 0x3110
-#define WM8915_WRITE_SEQUENCER_273 0x3111
-#define WM8915_WRITE_SEQUENCER_274 0x3112
-#define WM8915_WRITE_SEQUENCER_275 0x3113
-#define WM8915_WRITE_SEQUENCER_276 0x3114
-#define WM8915_WRITE_SEQUENCER_277 0x3115
-#define WM8915_WRITE_SEQUENCER_278 0x3116
-#define WM8915_WRITE_SEQUENCER_279 0x3117
-#define WM8915_WRITE_SEQUENCER_280 0x3118
-#define WM8915_WRITE_SEQUENCER_281 0x3119
-#define WM8915_WRITE_SEQUENCER_282 0x311A
-#define WM8915_WRITE_SEQUENCER_283 0x311B
-#define WM8915_WRITE_SEQUENCER_284 0x311C
-#define WM8915_WRITE_SEQUENCER_285 0x311D
-#define WM8915_WRITE_SEQUENCER_286 0x311E
-#define WM8915_WRITE_SEQUENCER_287 0x311F
-#define WM8915_WRITE_SEQUENCER_288 0x3120
-#define WM8915_WRITE_SEQUENCER_289 0x3121
-#define WM8915_WRITE_SEQUENCER_290 0x3122
-#define WM8915_WRITE_SEQUENCER_291 0x3123
-#define WM8915_WRITE_SEQUENCER_292 0x3124
-#define WM8915_WRITE_SEQUENCER_293 0x3125
-#define WM8915_WRITE_SEQUENCER_294 0x3126
-#define WM8915_WRITE_SEQUENCER_295 0x3127
-#define WM8915_WRITE_SEQUENCER_296 0x3128
-#define WM8915_WRITE_SEQUENCER_297 0x3129
-#define WM8915_WRITE_SEQUENCER_298 0x312A
-#define WM8915_WRITE_SEQUENCER_299 0x312B
-#define WM8915_WRITE_SEQUENCER_300 0x312C
-#define WM8915_WRITE_SEQUENCER_301 0x312D
-#define WM8915_WRITE_SEQUENCER_302 0x312E
-#define WM8915_WRITE_SEQUENCER_303 0x312F
-#define WM8915_WRITE_SEQUENCER_304 0x3130
-#define WM8915_WRITE_SEQUENCER_305 0x3131
-#define WM8915_WRITE_SEQUENCER_306 0x3132
-#define WM8915_WRITE_SEQUENCER_307 0x3133
-#define WM8915_WRITE_SEQUENCER_308 0x3134
-#define WM8915_WRITE_SEQUENCER_309 0x3135
-#define WM8915_WRITE_SEQUENCER_310 0x3136
-#define WM8915_WRITE_SEQUENCER_311 0x3137
-#define WM8915_WRITE_SEQUENCER_312 0x3138
-#define WM8915_WRITE_SEQUENCER_313 0x3139
-#define WM8915_WRITE_SEQUENCER_314 0x313A
-#define WM8915_WRITE_SEQUENCER_315 0x313B
-#define WM8915_WRITE_SEQUENCER_316 0x313C
-#define WM8915_WRITE_SEQUENCER_317 0x313D
-#define WM8915_WRITE_SEQUENCER_318 0x313E
-#define WM8915_WRITE_SEQUENCER_319 0x313F
-#define WM8915_WRITE_SEQUENCER_320 0x3140
-#define WM8915_WRITE_SEQUENCER_321 0x3141
-#define WM8915_WRITE_SEQUENCER_322 0x3142
-#define WM8915_WRITE_SEQUENCER_323 0x3143
-#define WM8915_WRITE_SEQUENCER_324 0x3144
-#define WM8915_WRITE_SEQUENCER_325 0x3145
-#define WM8915_WRITE_SEQUENCER_326 0x3146
-#define WM8915_WRITE_SEQUENCER_327 0x3147
-#define WM8915_WRITE_SEQUENCER_328 0x3148
-#define WM8915_WRITE_SEQUENCER_329 0x3149
-#define WM8915_WRITE_SEQUENCER_330 0x314A
-#define WM8915_WRITE_SEQUENCER_331 0x314B
-#define WM8915_WRITE_SEQUENCER_332 0x314C
-#define WM8915_WRITE_SEQUENCER_333 0x314D
-#define WM8915_WRITE_SEQUENCER_334 0x314E
-#define WM8915_WRITE_SEQUENCER_335 0x314F
-#define WM8915_WRITE_SEQUENCER_336 0x3150
-#define WM8915_WRITE_SEQUENCER_337 0x3151
-#define WM8915_WRITE_SEQUENCER_338 0x3152
-#define WM8915_WRITE_SEQUENCER_339 0x3153
-#define WM8915_WRITE_SEQUENCER_340 0x3154
-#define WM8915_WRITE_SEQUENCER_341 0x3155
-#define WM8915_WRITE_SEQUENCER_342 0x3156
-#define WM8915_WRITE_SEQUENCER_343 0x3157
-#define WM8915_WRITE_SEQUENCER_344 0x3158
-#define WM8915_WRITE_SEQUENCER_345 0x3159
-#define WM8915_WRITE_SEQUENCER_346 0x315A
-#define WM8915_WRITE_SEQUENCER_347 0x315B
-#define WM8915_WRITE_SEQUENCER_348 0x315C
-#define WM8915_WRITE_SEQUENCER_349 0x315D
-#define WM8915_WRITE_SEQUENCER_350 0x315E
-#define WM8915_WRITE_SEQUENCER_351 0x315F
-#define WM8915_WRITE_SEQUENCER_352 0x3160
-#define WM8915_WRITE_SEQUENCER_353 0x3161
-#define WM8915_WRITE_SEQUENCER_354 0x3162
-#define WM8915_WRITE_SEQUENCER_355 0x3163
-#define WM8915_WRITE_SEQUENCER_356 0x3164
-#define WM8915_WRITE_SEQUENCER_357 0x3165
-#define WM8915_WRITE_SEQUENCER_358 0x3166
-#define WM8915_WRITE_SEQUENCER_359 0x3167
-#define WM8915_WRITE_SEQUENCER_360 0x3168
-#define WM8915_WRITE_SEQUENCER_361 0x3169
-#define WM8915_WRITE_SEQUENCER_362 0x316A
-#define WM8915_WRITE_SEQUENCER_363 0x316B
-#define WM8915_WRITE_SEQUENCER_364 0x316C
-#define WM8915_WRITE_SEQUENCER_365 0x316D
-#define WM8915_WRITE_SEQUENCER_366 0x316E
-#define WM8915_WRITE_SEQUENCER_367 0x316F
-#define WM8915_WRITE_SEQUENCER_368 0x3170
-#define WM8915_WRITE_SEQUENCER_369 0x3171
-#define WM8915_WRITE_SEQUENCER_370 0x3172
-#define WM8915_WRITE_SEQUENCER_371 0x3173
-#define WM8915_WRITE_SEQUENCER_372 0x3174
-#define WM8915_WRITE_SEQUENCER_373 0x3175
-#define WM8915_WRITE_SEQUENCER_374 0x3176
-#define WM8915_WRITE_SEQUENCER_375 0x3177
-#define WM8915_WRITE_SEQUENCER_376 0x3178
-#define WM8915_WRITE_SEQUENCER_377 0x3179
-#define WM8915_WRITE_SEQUENCER_378 0x317A
-#define WM8915_WRITE_SEQUENCER_379 0x317B
-#define WM8915_WRITE_SEQUENCER_380 0x317C
-#define WM8915_WRITE_SEQUENCER_381 0x317D
-#define WM8915_WRITE_SEQUENCER_382 0x317E
-#define WM8915_WRITE_SEQUENCER_383 0x317F
-#define WM8915_WRITE_SEQUENCER_384 0x3180
-#define WM8915_WRITE_SEQUENCER_385 0x3181
-#define WM8915_WRITE_SEQUENCER_386 0x3182
-#define WM8915_WRITE_SEQUENCER_387 0x3183
-#define WM8915_WRITE_SEQUENCER_388 0x3184
-#define WM8915_WRITE_SEQUENCER_389 0x3185
-#define WM8915_WRITE_SEQUENCER_390 0x3186
-#define WM8915_WRITE_SEQUENCER_391 0x3187
-#define WM8915_WRITE_SEQUENCER_392 0x3188
-#define WM8915_WRITE_SEQUENCER_393 0x3189
-#define WM8915_WRITE_SEQUENCER_394 0x318A
-#define WM8915_WRITE_SEQUENCER_395 0x318B
-#define WM8915_WRITE_SEQUENCER_396 0x318C
-#define WM8915_WRITE_SEQUENCER_397 0x318D
-#define WM8915_WRITE_SEQUENCER_398 0x318E
-#define WM8915_WRITE_SEQUENCER_399 0x318F
-#define WM8915_WRITE_SEQUENCER_400 0x3190
-#define WM8915_WRITE_SEQUENCER_401 0x3191
-#define WM8915_WRITE_SEQUENCER_402 0x3192
-#define WM8915_WRITE_SEQUENCER_403 0x3193
-#define WM8915_WRITE_SEQUENCER_404 0x3194
-#define WM8915_WRITE_SEQUENCER_405 0x3195
-#define WM8915_WRITE_SEQUENCER_406 0x3196
-#define WM8915_WRITE_SEQUENCER_407 0x3197
-#define WM8915_WRITE_SEQUENCER_408 0x3198
-#define WM8915_WRITE_SEQUENCER_409 0x3199
-#define WM8915_WRITE_SEQUENCER_410 0x319A
-#define WM8915_WRITE_SEQUENCER_411 0x319B
-#define WM8915_WRITE_SEQUENCER_412 0x319C
-#define WM8915_WRITE_SEQUENCER_413 0x319D
-#define WM8915_WRITE_SEQUENCER_414 0x319E
-#define WM8915_WRITE_SEQUENCER_415 0x319F
-#define WM8915_WRITE_SEQUENCER_416 0x31A0
-#define WM8915_WRITE_SEQUENCER_417 0x31A1
-#define WM8915_WRITE_SEQUENCER_418 0x31A2
-#define WM8915_WRITE_SEQUENCER_419 0x31A3
-#define WM8915_WRITE_SEQUENCER_420 0x31A4
-#define WM8915_WRITE_SEQUENCER_421 0x31A5
-#define WM8915_WRITE_SEQUENCER_422 0x31A6
-#define WM8915_WRITE_SEQUENCER_423 0x31A7
-#define WM8915_WRITE_SEQUENCER_424 0x31A8
-#define WM8915_WRITE_SEQUENCER_425 0x31A9
-#define WM8915_WRITE_SEQUENCER_426 0x31AA
-#define WM8915_WRITE_SEQUENCER_427 0x31AB
-#define WM8915_WRITE_SEQUENCER_428 0x31AC
-#define WM8915_WRITE_SEQUENCER_429 0x31AD
-#define WM8915_WRITE_SEQUENCER_430 0x31AE
-#define WM8915_WRITE_SEQUENCER_431 0x31AF
-#define WM8915_WRITE_SEQUENCER_432 0x31B0
-#define WM8915_WRITE_SEQUENCER_433 0x31B1
-#define WM8915_WRITE_SEQUENCER_434 0x31B2
-#define WM8915_WRITE_SEQUENCER_435 0x31B3
-#define WM8915_WRITE_SEQUENCER_436 0x31B4
-#define WM8915_WRITE_SEQUENCER_437 0x31B5
-#define WM8915_WRITE_SEQUENCER_438 0x31B6
-#define WM8915_WRITE_SEQUENCER_439 0x31B7
-#define WM8915_WRITE_SEQUENCER_440 0x31B8
-#define WM8915_WRITE_SEQUENCER_441 0x31B9
-#define WM8915_WRITE_SEQUENCER_442 0x31BA
-#define WM8915_WRITE_SEQUENCER_443 0x31BB
-#define WM8915_WRITE_SEQUENCER_444 0x31BC
-#define WM8915_WRITE_SEQUENCER_445 0x31BD
-#define WM8915_WRITE_SEQUENCER_446 0x31BE
-#define WM8915_WRITE_SEQUENCER_447 0x31BF
-#define WM8915_WRITE_SEQUENCER_448 0x31C0
-#define WM8915_WRITE_SEQUENCER_449 0x31C1
-#define WM8915_WRITE_SEQUENCER_450 0x31C2
-#define WM8915_WRITE_SEQUENCER_451 0x31C3
-#define WM8915_WRITE_SEQUENCER_452 0x31C4
-#define WM8915_WRITE_SEQUENCER_453 0x31C5
-#define WM8915_WRITE_SEQUENCER_454 0x31C6
-#define WM8915_WRITE_SEQUENCER_455 0x31C7
-#define WM8915_WRITE_SEQUENCER_456 0x31C8
-#define WM8915_WRITE_SEQUENCER_457 0x31C9
-#define WM8915_WRITE_SEQUENCER_458 0x31CA
-#define WM8915_WRITE_SEQUENCER_459 0x31CB
-#define WM8915_WRITE_SEQUENCER_460 0x31CC
-#define WM8915_WRITE_SEQUENCER_461 0x31CD
-#define WM8915_WRITE_SEQUENCER_462 0x31CE
-#define WM8915_WRITE_SEQUENCER_463 0x31CF
-#define WM8915_WRITE_SEQUENCER_464 0x31D0
-#define WM8915_WRITE_SEQUENCER_465 0x31D1
-#define WM8915_WRITE_SEQUENCER_466 0x31D2
-#define WM8915_WRITE_SEQUENCER_467 0x31D3
-#define WM8915_WRITE_SEQUENCER_468 0x31D4
-#define WM8915_WRITE_SEQUENCER_469 0x31D5
-#define WM8915_WRITE_SEQUENCER_470 0x31D6
-#define WM8915_WRITE_SEQUENCER_471 0x31D7
-#define WM8915_WRITE_SEQUENCER_472 0x31D8
-#define WM8915_WRITE_SEQUENCER_473 0x31D9
-#define WM8915_WRITE_SEQUENCER_474 0x31DA
-#define WM8915_WRITE_SEQUENCER_475 0x31DB
-#define WM8915_WRITE_SEQUENCER_476 0x31DC
-#define WM8915_WRITE_SEQUENCER_477 0x31DD
-#define WM8915_WRITE_SEQUENCER_478 0x31DE
-#define WM8915_WRITE_SEQUENCER_479 0x31DF
-#define WM8915_WRITE_SEQUENCER_480 0x31E0
-#define WM8915_WRITE_SEQUENCER_481 0x31E1
-#define WM8915_WRITE_SEQUENCER_482 0x31E2
-#define WM8915_WRITE_SEQUENCER_483 0x31E3
-#define WM8915_WRITE_SEQUENCER_484 0x31E4
-#define WM8915_WRITE_SEQUENCER_485 0x31E5
-#define WM8915_WRITE_SEQUENCER_486 0x31E6
-#define WM8915_WRITE_SEQUENCER_487 0x31E7
-#define WM8915_WRITE_SEQUENCER_488 0x31E8
-#define WM8915_WRITE_SEQUENCER_489 0x31E9
-#define WM8915_WRITE_SEQUENCER_490 0x31EA
-#define WM8915_WRITE_SEQUENCER_491 0x31EB
-#define WM8915_WRITE_SEQUENCER_492 0x31EC
-#define WM8915_WRITE_SEQUENCER_493 0x31ED
-#define WM8915_WRITE_SEQUENCER_494 0x31EE
-#define WM8915_WRITE_SEQUENCER_495 0x31EF
-#define WM8915_WRITE_SEQUENCER_496 0x31F0
-#define WM8915_WRITE_SEQUENCER_497 0x31F1
-#define WM8915_WRITE_SEQUENCER_498 0x31F2
-#define WM8915_WRITE_SEQUENCER_499 0x31F3
-#define WM8915_WRITE_SEQUENCER_500 0x31F4
-#define WM8915_WRITE_SEQUENCER_501 0x31F5
-#define WM8915_WRITE_SEQUENCER_502 0x31F6
-#define WM8915_WRITE_SEQUENCER_503 0x31F7
-#define WM8915_WRITE_SEQUENCER_504 0x31F8
-#define WM8915_WRITE_SEQUENCER_505 0x31F9
-#define WM8915_WRITE_SEQUENCER_506 0x31FA
-#define WM8915_WRITE_SEQUENCER_507 0x31FB
-#define WM8915_WRITE_SEQUENCER_508 0x31FC
-#define WM8915_WRITE_SEQUENCER_509 0x31FD
-#define WM8915_WRITE_SEQUENCER_510 0x31FE
-#define WM8915_WRITE_SEQUENCER_511 0x31FF
-
-#define WM8915_REGISTER_COUNT 706
-#define WM8915_MAX_REGISTER 0x31FF
-
-/*
- * Field Definitions.
- */
-
-/*
- * R0 (0x00) - Software Reset
- */
-#define WM8915_SW_RESET_MASK 0xFFFF /* SW_RESET - [15:0] */
-#define WM8915_SW_RESET_SHIFT 0 /* SW_RESET - [15:0] */
-#define WM8915_SW_RESET_WIDTH 16 /* SW_RESET - [15:0] */
-
-/*
- * R1 (0x01) - Power Management (1)
- */
-#define WM8915_MICB2_ENA 0x0200 /* MICB2_ENA */
-#define WM8915_MICB2_ENA_MASK 0x0200 /* MICB2_ENA */
-#define WM8915_MICB2_ENA_SHIFT 9 /* MICB2_ENA */
-#define WM8915_MICB2_ENA_WIDTH 1 /* MICB2_ENA */
-#define WM8915_MICB1_ENA 0x0100 /* MICB1_ENA */
-#define WM8915_MICB1_ENA_MASK 0x0100 /* MICB1_ENA */
-#define WM8915_MICB1_ENA_SHIFT 8 /* MICB1_ENA */
-#define WM8915_MICB1_ENA_WIDTH 1 /* MICB1_ENA */
-#define WM8915_HPOUT2L_ENA 0x0080 /* HPOUT2L_ENA */
-#define WM8915_HPOUT2L_ENA_MASK 0x0080 /* HPOUT2L_ENA */
-#define WM8915_HPOUT2L_ENA_SHIFT 7 /* HPOUT2L_ENA */
-#define WM8915_HPOUT2L_ENA_WIDTH 1 /* HPOUT2L_ENA */
-#define WM8915_HPOUT2R_ENA 0x0040 /* HPOUT2R_ENA */
-#define WM8915_HPOUT2R_ENA_MASK 0x0040 /* HPOUT2R_ENA */
-#define WM8915_HPOUT2R_ENA_SHIFT 6 /* HPOUT2R_ENA */
-#define WM8915_HPOUT2R_ENA_WIDTH 1 /* HPOUT2R_ENA */
-#define WM8915_HPOUT1L_ENA 0x0020 /* HPOUT1L_ENA */
-#define WM8915_HPOUT1L_ENA_MASK 0x0020 /* HPOUT1L_ENA */
-#define WM8915_HPOUT1L_ENA_SHIFT 5 /* HPOUT1L_ENA */
-#define WM8915_HPOUT1L_ENA_WIDTH 1 /* HPOUT1L_ENA */
-#define WM8915_HPOUT1R_ENA 0x0010 /* HPOUT1R_ENA */
-#define WM8915_HPOUT1R_ENA_MASK 0x0010 /* HPOUT1R_ENA */
-#define WM8915_HPOUT1R_ENA_SHIFT 4 /* HPOUT1R_ENA */
-#define WM8915_HPOUT1R_ENA_WIDTH 1 /* HPOUT1R_ENA */
-#define WM8915_BG_ENA 0x0001 /* BG_ENA */
-#define WM8915_BG_ENA_MASK 0x0001 /* BG_ENA */
-#define WM8915_BG_ENA_SHIFT 0 /* BG_ENA */
-#define WM8915_BG_ENA_WIDTH 1 /* BG_ENA */
-
-/*
- * R2 (0x02) - Power Management (2)
- */
-#define WM8915_OPCLK_ENA 0x0800 /* OPCLK_ENA */
-#define WM8915_OPCLK_ENA_MASK 0x0800 /* OPCLK_ENA */
-#define WM8915_OPCLK_ENA_SHIFT 11 /* OPCLK_ENA */
-#define WM8915_OPCLK_ENA_WIDTH 1 /* OPCLK_ENA */
-#define WM8915_INL_ENA 0x0020 /* INL_ENA */
-#define WM8915_INL_ENA_MASK 0x0020 /* INL_ENA */
-#define WM8915_INL_ENA_SHIFT 5 /* INL_ENA */
-#define WM8915_INL_ENA_WIDTH 1 /* INL_ENA */
-#define WM8915_INR_ENA 0x0010 /* INR_ENA */
-#define WM8915_INR_ENA_MASK 0x0010 /* INR_ENA */
-#define WM8915_INR_ENA_SHIFT 4 /* INR_ENA */
-#define WM8915_INR_ENA_WIDTH 1 /* INR_ENA */
-#define WM8915_LDO2_ENA 0x0002 /* LDO2_ENA */
-#define WM8915_LDO2_ENA_MASK 0x0002 /* LDO2_ENA */
-#define WM8915_LDO2_ENA_SHIFT 1 /* LDO2_ENA */
-#define WM8915_LDO2_ENA_WIDTH 1 /* LDO2_ENA */
-
-/*
- * R3 (0x03) - Power Management (3)
- */
-#define WM8915_DSP2RXL_ENA 0x0800 /* DSP2RXL_ENA */
-#define WM8915_DSP2RXL_ENA_MASK 0x0800 /* DSP2RXL_ENA */
-#define WM8915_DSP2RXL_ENA_SHIFT 11 /* DSP2RXL_ENA */
-#define WM8915_DSP2RXL_ENA_WIDTH 1 /* DSP2RXL_ENA */
-#define WM8915_DSP2RXR_ENA 0x0400 /* DSP2RXR_ENA */
-#define WM8915_DSP2RXR_ENA_MASK 0x0400 /* DSP2RXR_ENA */
-#define WM8915_DSP2RXR_ENA_SHIFT 10 /* DSP2RXR_ENA */
-#define WM8915_DSP2RXR_ENA_WIDTH 1 /* DSP2RXR_ENA */
-#define WM8915_DSP1RXL_ENA 0x0200 /* DSP1RXL_ENA */
-#define WM8915_DSP1RXL_ENA_MASK 0x0200 /* DSP1RXL_ENA */
-#define WM8915_DSP1RXL_ENA_SHIFT 9 /* DSP1RXL_ENA */
-#define WM8915_DSP1RXL_ENA_WIDTH 1 /* DSP1RXL_ENA */
-#define WM8915_DSP1RXR_ENA 0x0100 /* DSP1RXR_ENA */
-#define WM8915_DSP1RXR_ENA_MASK 0x0100 /* DSP1RXR_ENA */
-#define WM8915_DSP1RXR_ENA_SHIFT 8 /* DSP1RXR_ENA */
-#define WM8915_DSP1RXR_ENA_WIDTH 1 /* DSP1RXR_ENA */
-#define WM8915_DMIC2L_ENA 0x0020 /* DMIC2L_ENA */
-#define WM8915_DMIC2L_ENA_MASK 0x0020 /* DMIC2L_ENA */
-#define WM8915_DMIC2L_ENA_SHIFT 5 /* DMIC2L_ENA */
-#define WM8915_DMIC2L_ENA_WIDTH 1 /* DMIC2L_ENA */
-#define WM8915_DMIC2R_ENA 0x0010 /* DMIC2R_ENA */
-#define WM8915_DMIC2R_ENA_MASK 0x0010 /* DMIC2R_ENA */
-#define WM8915_DMIC2R_ENA_SHIFT 4 /* DMIC2R_ENA */
-#define WM8915_DMIC2R_ENA_WIDTH 1 /* DMIC2R_ENA */
-#define WM8915_DMIC1L_ENA 0x0008 /* DMIC1L_ENA */
-#define WM8915_DMIC1L_ENA_MASK 0x0008 /* DMIC1L_ENA */
-#define WM8915_DMIC1L_ENA_SHIFT 3 /* DMIC1L_ENA */
-#define WM8915_DMIC1L_ENA_WIDTH 1 /* DMIC1L_ENA */
-#define WM8915_DMIC1R_ENA 0x0004 /* DMIC1R_ENA */
-#define WM8915_DMIC1R_ENA_MASK 0x0004 /* DMIC1R_ENA */
-#define WM8915_DMIC1R_ENA_SHIFT 2 /* DMIC1R_ENA */
-#define WM8915_DMIC1R_ENA_WIDTH 1 /* DMIC1R_ENA */
-#define WM8915_ADCL_ENA 0x0002 /* ADCL_ENA */
-#define WM8915_ADCL_ENA_MASK 0x0002 /* ADCL_ENA */
-#define WM8915_ADCL_ENA_SHIFT 1 /* ADCL_ENA */
-#define WM8915_ADCL_ENA_WIDTH 1 /* ADCL_ENA */
-#define WM8915_ADCR_ENA 0x0001 /* ADCR_ENA */
-#define WM8915_ADCR_ENA_MASK 0x0001 /* ADCR_ENA */
-#define WM8915_ADCR_ENA_SHIFT 0 /* ADCR_ENA */
-#define WM8915_ADCR_ENA_WIDTH 1 /* ADCR_ENA */
-
-/*
- * R4 (0x04) - Power Management (4)
- */
-#define WM8915_AIF2RX_CHAN1_ENA 0x0200 /* AIF2RX_CHAN1_ENA */
-#define WM8915_AIF2RX_CHAN1_ENA_MASK 0x0200 /* AIF2RX_CHAN1_ENA */
-#define WM8915_AIF2RX_CHAN1_ENA_SHIFT 9 /* AIF2RX_CHAN1_ENA */
-#define WM8915_AIF2RX_CHAN1_ENA_WIDTH 1 /* AIF2RX_CHAN1_ENA */
-#define WM8915_AIF2RX_CHAN0_ENA 0x0100 /* AIF2RX_CHAN0_ENA */
-#define WM8915_AIF2RX_CHAN0_ENA_MASK 0x0100 /* AIF2RX_CHAN0_ENA */
-#define WM8915_AIF2RX_CHAN0_ENA_SHIFT 8 /* AIF2RX_CHAN0_ENA */
-#define WM8915_AIF2RX_CHAN0_ENA_WIDTH 1 /* AIF2RX_CHAN0_ENA */
-#define WM8915_AIF1RX_CHAN5_ENA 0x0020 /* AIF1RX_CHAN5_ENA */
-#define WM8915_AIF1RX_CHAN5_ENA_MASK 0x0020 /* AIF1RX_CHAN5_ENA */
-#define WM8915_AIF1RX_CHAN5_ENA_SHIFT 5 /* AIF1RX_CHAN5_ENA */
-#define WM8915_AIF1RX_CHAN5_ENA_WIDTH 1 /* AIF1RX_CHAN5_ENA */
-#define WM8915_AIF1RX_CHAN4_ENA 0x0010 /* AIF1RX_CHAN4_ENA */
-#define WM8915_AIF1RX_CHAN4_ENA_MASK 0x0010 /* AIF1RX_CHAN4_ENA */
-#define WM8915_AIF1RX_CHAN4_ENA_SHIFT 4 /* AIF1RX_CHAN4_ENA */
-#define WM8915_AIF1RX_CHAN4_ENA_WIDTH 1 /* AIF1RX_CHAN4_ENA */
-#define WM8915_AIF1RX_CHAN3_ENA 0x0008 /* AIF1RX_CHAN3_ENA */
-#define WM8915_AIF1RX_CHAN3_ENA_MASK 0x0008 /* AIF1RX_CHAN3_ENA */
-#define WM8915_AIF1RX_CHAN3_ENA_SHIFT 3 /* AIF1RX_CHAN3_ENA */
-#define WM8915_AIF1RX_CHAN3_ENA_WIDTH 1 /* AIF1RX_CHAN3_ENA */
-#define WM8915_AIF1RX_CHAN2_ENA 0x0004 /* AIF1RX_CHAN2_ENA */
-#define WM8915_AIF1RX_CHAN2_ENA_MASK 0x0004 /* AIF1RX_CHAN2_ENA */
-#define WM8915_AIF1RX_CHAN2_ENA_SHIFT 2 /* AIF1RX_CHAN2_ENA */
-#define WM8915_AIF1RX_CHAN2_ENA_WIDTH 1 /* AIF1RX_CHAN2_ENA */
-#define WM8915_AIF1RX_CHAN1_ENA 0x0002 /* AIF1RX_CHAN1_ENA */
-#define WM8915_AIF1RX_CHAN1_ENA_MASK 0x0002 /* AIF1RX_CHAN1_ENA */
-#define WM8915_AIF1RX_CHAN1_ENA_SHIFT 1 /* AIF1RX_CHAN1_ENA */
-#define WM8915_AIF1RX_CHAN1_ENA_WIDTH 1 /* AIF1RX_CHAN1_ENA */
-#define WM8915_AIF1RX_CHAN0_ENA 0x0001 /* AIF1RX_CHAN0_ENA */
-#define WM8915_AIF1RX_CHAN0_ENA_MASK 0x0001 /* AIF1RX_CHAN0_ENA */
-#define WM8915_AIF1RX_CHAN0_ENA_SHIFT 0 /* AIF1RX_CHAN0_ENA */
-#define WM8915_AIF1RX_CHAN0_ENA_WIDTH 1 /* AIF1RX_CHAN0_ENA */
-
-/*
- * R5 (0x05) - Power Management (5)
- */
-#define WM8915_DSP2TXL_ENA 0x0800 /* DSP2TXL_ENA */
-#define WM8915_DSP2TXL_ENA_MASK 0x0800 /* DSP2TXL_ENA */
-#define WM8915_DSP2TXL_ENA_SHIFT 11 /* DSP2TXL_ENA */
-#define WM8915_DSP2TXL_ENA_WIDTH 1 /* DSP2TXL_ENA */
-#define WM8915_DSP2TXR_ENA 0x0400 /* DSP2TXR_ENA */
-#define WM8915_DSP2TXR_ENA_MASK 0x0400 /* DSP2TXR_ENA */
-#define WM8915_DSP2TXR_ENA_SHIFT 10 /* DSP2TXR_ENA */
-#define WM8915_DSP2TXR_ENA_WIDTH 1 /* DSP2TXR_ENA */
-#define WM8915_DSP1TXL_ENA 0x0200 /* DSP1TXL_ENA */
-#define WM8915_DSP1TXL_ENA_MASK 0x0200 /* DSP1TXL_ENA */
-#define WM8915_DSP1TXL_ENA_SHIFT 9 /* DSP1TXL_ENA */
-#define WM8915_DSP1TXL_ENA_WIDTH 1 /* DSP1TXL_ENA */
-#define WM8915_DSP1TXR_ENA 0x0100 /* DSP1TXR_ENA */
-#define WM8915_DSP1TXR_ENA_MASK 0x0100 /* DSP1TXR_ENA */
-#define WM8915_DSP1TXR_ENA_SHIFT 8 /* DSP1TXR_ENA */
-#define WM8915_DSP1TXR_ENA_WIDTH 1 /* DSP1TXR_ENA */
-#define WM8915_DAC2L_ENA 0x0008 /* DAC2L_ENA */
-#define WM8915_DAC2L_ENA_MASK 0x0008 /* DAC2L_ENA */
-#define WM8915_DAC2L_ENA_SHIFT 3 /* DAC2L_ENA */
-#define WM8915_DAC2L_ENA_WIDTH 1 /* DAC2L_ENA */
-#define WM8915_DAC2R_ENA 0x0004 /* DAC2R_ENA */
-#define WM8915_DAC2R_ENA_MASK 0x0004 /* DAC2R_ENA */
-#define WM8915_DAC2R_ENA_SHIFT 2 /* DAC2R_ENA */
-#define WM8915_DAC2R_ENA_WIDTH 1 /* DAC2R_ENA */
-#define WM8915_DAC1L_ENA 0x0002 /* DAC1L_ENA */
-#define WM8915_DAC1L_ENA_MASK 0x0002 /* DAC1L_ENA */
-#define WM8915_DAC1L_ENA_SHIFT 1 /* DAC1L_ENA */
-#define WM8915_DAC1L_ENA_WIDTH 1 /* DAC1L_ENA */
-#define WM8915_DAC1R_ENA 0x0001 /* DAC1R_ENA */
-#define WM8915_DAC1R_ENA_MASK 0x0001 /* DAC1R_ENA */
-#define WM8915_DAC1R_ENA_SHIFT 0 /* DAC1R_ENA */
-#define WM8915_DAC1R_ENA_WIDTH 1 /* DAC1R_ENA */
-
-/*
- * R6 (0x06) - Power Management (6)
- */
-#define WM8915_AIF2TX_CHAN1_ENA 0x0200 /* AIF2TX_CHAN1_ENA */
-#define WM8915_AIF2TX_CHAN1_ENA_MASK 0x0200 /* AIF2TX_CHAN1_ENA */
-#define WM8915_AIF2TX_CHAN1_ENA_SHIFT 9 /* AIF2TX_CHAN1_ENA */
-#define WM8915_AIF2TX_CHAN1_ENA_WIDTH 1 /* AIF2TX_CHAN1_ENA */
-#define WM8915_AIF2TX_CHAN0_ENA 0x0100 /* AIF2TX_CHAN0_ENA */
-#define WM8915_AIF2TX_CHAN0_ENA_MASK 0x0100 /* AIF2TX_CHAN0_ENA */
-#define WM8915_AIF2TX_CHAN0_ENA_SHIFT 8 /* AIF2TX_CHAN0_ENA */
-#define WM8915_AIF2TX_CHAN0_ENA_WIDTH 1 /* AIF2TX_CHAN0_ENA */
-#define WM8915_AIF1TX_CHAN5_ENA 0x0020 /* AIF1TX_CHAN5_ENA */
-#define WM8915_AIF1TX_CHAN5_ENA_MASK 0x0020 /* AIF1TX_CHAN5_ENA */
-#define WM8915_AIF1TX_CHAN5_ENA_SHIFT 5 /* AIF1TX_CHAN5_ENA */
-#define WM8915_AIF1TX_CHAN5_ENA_WIDTH 1 /* AIF1TX_CHAN5_ENA */
-#define WM8915_AIF1TX_CHAN4_ENA 0x0010 /* AIF1TX_CHAN4_ENA */
-#define WM8915_AIF1TX_CHAN4_ENA_MASK 0x0010 /* AIF1TX_CHAN4_ENA */
-#define WM8915_AIF1TX_CHAN4_ENA_SHIFT 4 /* AIF1TX_CHAN4_ENA */
-#define WM8915_AIF1TX_CHAN4_ENA_WIDTH 1 /* AIF1TX_CHAN4_ENA */
-#define WM8915_AIF1TX_CHAN3_ENA 0x0008 /* AIF1TX_CHAN3_ENA */
-#define WM8915_AIF1TX_CHAN3_ENA_MASK 0x0008 /* AIF1TX_CHAN3_ENA */
-#define WM8915_AIF1TX_CHAN3_ENA_SHIFT 3 /* AIF1TX_CHAN3_ENA */
-#define WM8915_AIF1TX_CHAN3_ENA_WIDTH 1 /* AIF1TX_CHAN3_ENA */
-#define WM8915_AIF1TX_CHAN2_ENA 0x0004 /* AIF1TX_CHAN2_ENA */
-#define WM8915_AIF1TX_CHAN2_ENA_MASK 0x0004 /* AIF1TX_CHAN2_ENA */
-#define WM8915_AIF1TX_CHAN2_ENA_SHIFT 2 /* AIF1TX_CHAN2_ENA */
-#define WM8915_AIF1TX_CHAN2_ENA_WIDTH 1 /* AIF1TX_CHAN2_ENA */
-#define WM8915_AIF1TX_CHAN1_ENA 0x0002 /* AIF1TX_CHAN1_ENA */
-#define WM8915_AIF1TX_CHAN1_ENA_MASK 0x0002 /* AIF1TX_CHAN1_ENA */
-#define WM8915_AIF1TX_CHAN1_ENA_SHIFT 1 /* AIF1TX_CHAN1_ENA */
-#define WM8915_AIF1TX_CHAN1_ENA_WIDTH 1 /* AIF1TX_CHAN1_ENA */
-#define WM8915_AIF1TX_CHAN0_ENA 0x0001 /* AIF1TX_CHAN0_ENA */
-#define WM8915_AIF1TX_CHAN0_ENA_MASK 0x0001 /* AIF1TX_CHAN0_ENA */
-#define WM8915_AIF1TX_CHAN0_ENA_SHIFT 0 /* AIF1TX_CHAN0_ENA */
-#define WM8915_AIF1TX_CHAN0_ENA_WIDTH 1 /* AIF1TX_CHAN0_ENA */
-
-/*
- * R7 (0x07) - Power Management (7)
- */
-#define WM8915_DMIC2_FN 0x0200 /* DMIC2_FN */
-#define WM8915_DMIC2_FN_MASK 0x0200 /* DMIC2_FN */
-#define WM8915_DMIC2_FN_SHIFT 9 /* DMIC2_FN */
-#define WM8915_DMIC2_FN_WIDTH 1 /* DMIC2_FN */
-#define WM8915_DMIC1_FN 0x0100 /* DMIC1_FN */
-#define WM8915_DMIC1_FN_MASK 0x0100 /* DMIC1_FN */
-#define WM8915_DMIC1_FN_SHIFT 8 /* DMIC1_FN */
-#define WM8915_DMIC1_FN_WIDTH 1 /* DMIC1_FN */
-#define WM8915_ADC_DMIC_DSP2R_ENA 0x0080 /* ADC_DMIC_DSP2R_ENA */
-#define WM8915_ADC_DMIC_DSP2R_ENA_MASK 0x0080 /* ADC_DMIC_DSP2R_ENA */
-#define WM8915_ADC_DMIC_DSP2R_ENA_SHIFT 7 /* ADC_DMIC_DSP2R_ENA */
-#define WM8915_ADC_DMIC_DSP2R_ENA_WIDTH 1 /* ADC_DMIC_DSP2R_ENA */
-#define WM8915_ADC_DMIC_DSP2L_ENA 0x0040 /* ADC_DMIC_DSP2L_ENA */
-#define WM8915_ADC_DMIC_DSP2L_ENA_MASK 0x0040 /* ADC_DMIC_DSP2L_ENA */
-#define WM8915_ADC_DMIC_DSP2L_ENA_SHIFT 6 /* ADC_DMIC_DSP2L_ENA */
-#define WM8915_ADC_DMIC_DSP2L_ENA_WIDTH 1 /* ADC_DMIC_DSP2L_ENA */
-#define WM8915_ADC_DMIC_SRC2_MASK 0x0030 /* ADC_DMIC_SRC2 - [5:4] */
-#define WM8915_ADC_DMIC_SRC2_SHIFT 4 /* ADC_DMIC_SRC2 - [5:4] */
-#define WM8915_ADC_DMIC_SRC2_WIDTH 2 /* ADC_DMIC_SRC2 - [5:4] */
-#define WM8915_ADC_DMIC_DSP1R_ENA 0x0008 /* ADC_DMIC_DSP1R_ENA */
-#define WM8915_ADC_DMIC_DSP1R_ENA_MASK 0x0008 /* ADC_DMIC_DSP1R_ENA */
-#define WM8915_ADC_DMIC_DSP1R_ENA_SHIFT 3 /* ADC_DMIC_DSP1R_ENA */
-#define WM8915_ADC_DMIC_DSP1R_ENA_WIDTH 1 /* ADC_DMIC_DSP1R_ENA */
-#define WM8915_ADC_DMIC_DSP1L_ENA 0x0004 /* ADC_DMIC_DSP1L_ENA */
-#define WM8915_ADC_DMIC_DSP1L_ENA_MASK 0x0004 /* ADC_DMIC_DSP1L_ENA */
-#define WM8915_ADC_DMIC_DSP1L_ENA_SHIFT 2 /* ADC_DMIC_DSP1L_ENA */
-#define WM8915_ADC_DMIC_DSP1L_ENA_WIDTH 1 /* ADC_DMIC_DSP1L_ENA */
-#define WM8915_ADC_DMIC_SRC1_MASK 0x0003 /* ADC_DMIC_SRC1 - [1:0] */
-#define WM8915_ADC_DMIC_SRC1_SHIFT 0 /* ADC_DMIC_SRC1 - [1:0] */
-#define WM8915_ADC_DMIC_SRC1_WIDTH 2 /* ADC_DMIC_SRC1 - [1:0] */
-
-/*
- * R8 (0x08) - Power Management (8)
- */
-#define WM8915_AIF2TX_SRC_MASK 0x00C0 /* AIF2TX_SRC - [7:6] */
-#define WM8915_AIF2TX_SRC_SHIFT 6 /* AIF2TX_SRC - [7:6] */
-#define WM8915_AIF2TX_SRC_WIDTH 2 /* AIF2TX_SRC - [7:6] */
-#define WM8915_DSP2RX_SRC 0x0010 /* DSP2RX_SRC */
-#define WM8915_DSP2RX_SRC_MASK 0x0010 /* DSP2RX_SRC */
-#define WM8915_DSP2RX_SRC_SHIFT 4 /* DSP2RX_SRC */
-#define WM8915_DSP2RX_SRC_WIDTH 1 /* DSP2RX_SRC */
-#define WM8915_DSP1RX_SRC 0x0001 /* DSP1RX_SRC */
-#define WM8915_DSP1RX_SRC_MASK 0x0001 /* DSP1RX_SRC */
-#define WM8915_DSP1RX_SRC_SHIFT 0 /* DSP1RX_SRC */
-#define WM8915_DSP1RX_SRC_WIDTH 1 /* DSP1RX_SRC */
-
-/*
- * R16 (0x10) - Left Line Input Volume
- */
-#define WM8915_IN1_VU 0x0080 /* IN1_VU */
-#define WM8915_IN1_VU_MASK 0x0080 /* IN1_VU */
-#define WM8915_IN1_VU_SHIFT 7 /* IN1_VU */
-#define WM8915_IN1_VU_WIDTH 1 /* IN1_VU */
-#define WM8915_IN1L_ZC 0x0020 /* IN1L_ZC */
-#define WM8915_IN1L_ZC_MASK 0x0020 /* IN1L_ZC */
-#define WM8915_IN1L_ZC_SHIFT 5 /* IN1L_ZC */
-#define WM8915_IN1L_ZC_WIDTH 1 /* IN1L_ZC */
-#define WM8915_IN1L_VOL_MASK 0x001F /* IN1L_VOL - [4:0] */
-#define WM8915_IN1L_VOL_SHIFT 0 /* IN1L_VOL - [4:0] */
-#define WM8915_IN1L_VOL_WIDTH 5 /* IN1L_VOL - [4:0] */
-
-/*
- * R17 (0x11) - Right Line Input Volume
- */
-#define WM8915_IN1_VU 0x0080 /* IN1_VU */
-#define WM8915_IN1_VU_MASK 0x0080 /* IN1_VU */
-#define WM8915_IN1_VU_SHIFT 7 /* IN1_VU */
-#define WM8915_IN1_VU_WIDTH 1 /* IN1_VU */
-#define WM8915_IN1R_ZC 0x0020 /* IN1R_ZC */
-#define WM8915_IN1R_ZC_MASK 0x0020 /* IN1R_ZC */
-#define WM8915_IN1R_ZC_SHIFT 5 /* IN1R_ZC */
-#define WM8915_IN1R_ZC_WIDTH 1 /* IN1R_ZC */
-#define WM8915_IN1R_VOL_MASK 0x001F /* IN1R_VOL - [4:0] */
-#define WM8915_IN1R_VOL_SHIFT 0 /* IN1R_VOL - [4:0] */
-#define WM8915_IN1R_VOL_WIDTH 5 /* IN1R_VOL - [4:0] */
-
-/*
- * R18 (0x12) - Line Input Control
- */
-#define WM8915_INL_MODE_MASK 0x000C /* INL_MODE - [3:2] */
-#define WM8915_INL_MODE_SHIFT 2 /* INL_MODE - [3:2] */
-#define WM8915_INL_MODE_WIDTH 2 /* INL_MODE - [3:2] */
-#define WM8915_INR_MODE_MASK 0x0003 /* INR_MODE - [1:0] */
-#define WM8915_INR_MODE_SHIFT 0 /* INR_MODE - [1:0] */
-#define WM8915_INR_MODE_WIDTH 2 /* INR_MODE - [1:0] */
-
-/*
- * R21 (0x15) - DAC1 HPOUT1 Volume
- */
-#define WM8915_DAC1R_HPOUT1R_VOL_MASK 0x00F0 /* DAC1R_HPOUT1R_VOL - [7:4] */
-#define WM8915_DAC1R_HPOUT1R_VOL_SHIFT 4 /* DAC1R_HPOUT1R_VOL - [7:4] */
-#define WM8915_DAC1R_HPOUT1R_VOL_WIDTH 4 /* DAC1R_HPOUT1R_VOL - [7:4] */
-#define WM8915_DAC1L_HPOUT1L_VOL_MASK 0x000F /* DAC1L_HPOUT1L_VOL - [3:0] */
-#define WM8915_DAC1L_HPOUT1L_VOL_SHIFT 0 /* DAC1L_HPOUT1L_VOL - [3:0] */
-#define WM8915_DAC1L_HPOUT1L_VOL_WIDTH 4 /* DAC1L_HPOUT1L_VOL - [3:0] */
-
-/*
- * R22 (0x16) - DAC2 HPOUT2 Volume
- */
-#define WM8915_DAC2R_HPOUT2R_VOL_MASK 0x00F0 /* DAC2R_HPOUT2R_VOL - [7:4] */
-#define WM8915_DAC2R_HPOUT2R_VOL_SHIFT 4 /* DAC2R_HPOUT2R_VOL - [7:4] */
-#define WM8915_DAC2R_HPOUT2R_VOL_WIDTH 4 /* DAC2R_HPOUT2R_VOL - [7:4] */
-#define WM8915_DAC2L_HPOUT2L_VOL_MASK 0x000F /* DAC2L_HPOUT2L_VOL - [3:0] */
-#define WM8915_DAC2L_HPOUT2L_VOL_SHIFT 0 /* DAC2L_HPOUT2L_VOL - [3:0] */
-#define WM8915_DAC2L_HPOUT2L_VOL_WIDTH 4 /* DAC2L_HPOUT2L_VOL - [3:0] */
-
-/*
- * R24 (0x18) - DAC1 Left Volume
- */
-#define WM8915_DAC1L_MUTE 0x0200 /* DAC1L_MUTE */
-#define WM8915_DAC1L_MUTE_MASK 0x0200 /* DAC1L_MUTE */
-#define WM8915_DAC1L_MUTE_SHIFT 9 /* DAC1L_MUTE */
-#define WM8915_DAC1L_MUTE_WIDTH 1 /* DAC1L_MUTE */
-#define WM8915_DAC1_VU 0x0100 /* DAC1_VU */
-#define WM8915_DAC1_VU_MASK 0x0100 /* DAC1_VU */
-#define WM8915_DAC1_VU_SHIFT 8 /* DAC1_VU */
-#define WM8915_DAC1_VU_WIDTH 1 /* DAC1_VU */
-#define WM8915_DAC1L_VOL_MASK 0x00FF /* DAC1L_VOL - [7:0] */
-#define WM8915_DAC1L_VOL_SHIFT 0 /* DAC1L_VOL - [7:0] */
-#define WM8915_DAC1L_VOL_WIDTH 8 /* DAC1L_VOL - [7:0] */
-
-/*
- * R25 (0x19) - DAC1 Right Volume
- */
-#define WM8915_DAC1R_MUTE 0x0200 /* DAC1R_MUTE */
-#define WM8915_DAC1R_MUTE_MASK 0x0200 /* DAC1R_MUTE */
-#define WM8915_DAC1R_MUTE_SHIFT 9 /* DAC1R_MUTE */
-#define WM8915_DAC1R_MUTE_WIDTH 1 /* DAC1R_MUTE */
-#define WM8915_DAC1_VU 0x0100 /* DAC1_VU */
-#define WM8915_DAC1_VU_MASK 0x0100 /* DAC1_VU */
-#define WM8915_DAC1_VU_SHIFT 8 /* DAC1_VU */
-#define WM8915_DAC1_VU_WIDTH 1 /* DAC1_VU */
-#define WM8915_DAC1R_VOL_MASK 0x00FF /* DAC1R_VOL - [7:0] */
-#define WM8915_DAC1R_VOL_SHIFT 0 /* DAC1R_VOL - [7:0] */
-#define WM8915_DAC1R_VOL_WIDTH 8 /* DAC1R_VOL - [7:0] */
-
-/*
- * R26 (0x1A) - DAC2 Left Volume
- */
-#define WM8915_DAC2L_MUTE 0x0200 /* DAC2L_MUTE */
-#define WM8915_DAC2L_MUTE_MASK 0x0200 /* DAC2L_MUTE */
-#define WM8915_DAC2L_MUTE_SHIFT 9 /* DAC2L_MUTE */
-#define WM8915_DAC2L_MUTE_WIDTH 1 /* DAC2L_MUTE */
-#define WM8915_DAC2_VU 0x0100 /* DAC2_VU */
-#define WM8915_DAC2_VU_MASK 0x0100 /* DAC2_VU */
-#define WM8915_DAC2_VU_SHIFT 8 /* DAC2_VU */
-#define WM8915_DAC2_VU_WIDTH 1 /* DAC2_VU */
-#define WM8915_DAC2L_VOL_MASK 0x00FF /* DAC2L_VOL - [7:0] */
-#define WM8915_DAC2L_VOL_SHIFT 0 /* DAC2L_VOL - [7:0] */
-#define WM8915_DAC2L_VOL_WIDTH 8 /* DAC2L_VOL - [7:0] */
-
-/*
- * R27 (0x1B) - DAC2 Right Volume
- */
-#define WM8915_DAC2R_MUTE 0x0200 /* DAC2R_MUTE */
-#define WM8915_DAC2R_MUTE_MASK 0x0200 /* DAC2R_MUTE */
-#define WM8915_DAC2R_MUTE_SHIFT 9 /* DAC2R_MUTE */
-#define WM8915_DAC2R_MUTE_WIDTH 1 /* DAC2R_MUTE */
-#define WM8915_DAC2_VU 0x0100 /* DAC2_VU */
-#define WM8915_DAC2_VU_MASK 0x0100 /* DAC2_VU */
-#define WM8915_DAC2_VU_SHIFT 8 /* DAC2_VU */
-#define WM8915_DAC2_VU_WIDTH 1 /* DAC2_VU */
-#define WM8915_DAC2R_VOL_MASK 0x00FF /* DAC2R_VOL - [7:0] */
-#define WM8915_DAC2R_VOL_SHIFT 0 /* DAC2R_VOL - [7:0] */
-#define WM8915_DAC2R_VOL_WIDTH 8 /* DAC2R_VOL - [7:0] */
-
-/*
- * R28 (0x1C) - Output1 Left Volume
- */
-#define WM8915_DAC1_VU 0x0100 /* DAC1_VU */
-#define WM8915_DAC1_VU_MASK 0x0100 /* DAC1_VU */
-#define WM8915_DAC1_VU_SHIFT 8 /* DAC1_VU */
-#define WM8915_DAC1_VU_WIDTH 1 /* DAC1_VU */
-#define WM8915_HPOUT1L_ZC 0x0080 /* HPOUT1L_ZC */
-#define WM8915_HPOUT1L_ZC_MASK 0x0080 /* HPOUT1L_ZC */
-#define WM8915_HPOUT1L_ZC_SHIFT 7 /* HPOUT1L_ZC */
-#define WM8915_HPOUT1L_ZC_WIDTH 1 /* HPOUT1L_ZC */
-#define WM8915_HPOUT1L_VOL_MASK 0x000F /* HPOUT1L_VOL - [3:0] */
-#define WM8915_HPOUT1L_VOL_SHIFT 0 /* HPOUT1L_VOL - [3:0] */
-#define WM8915_HPOUT1L_VOL_WIDTH 4 /* HPOUT1L_VOL - [3:0] */
-
-/*
- * R29 (0x1D) - Output1 Right Volume
- */
-#define WM8915_DAC1_VU 0x0100 /* DAC1_VU */
-#define WM8915_DAC1_VU_MASK 0x0100 /* DAC1_VU */
-#define WM8915_DAC1_VU_SHIFT 8 /* DAC1_VU */
-#define WM8915_DAC1_VU_WIDTH 1 /* DAC1_VU */
-#define WM8915_HPOUT1R_ZC 0x0080 /* HPOUT1R_ZC */
-#define WM8915_HPOUT1R_ZC_MASK 0x0080 /* HPOUT1R_ZC */
-#define WM8915_HPOUT1R_ZC_SHIFT 7 /* HPOUT1R_ZC */
-#define WM8915_HPOUT1R_ZC_WIDTH 1 /* HPOUT1R_ZC */
-#define WM8915_HPOUT1R_VOL_MASK 0x000F /* HPOUT1R_VOL - [3:0] */
-#define WM8915_HPOUT1R_VOL_SHIFT 0 /* HPOUT1R_VOL - [3:0] */
-#define WM8915_HPOUT1R_VOL_WIDTH 4 /* HPOUT1R_VOL - [3:0] */
-
-/*
- * R30 (0x1E) - Output2 Left Volume
- */
-#define WM8915_DAC2_VU 0x0100 /* DAC2_VU */
-#define WM8915_DAC2_VU_MASK 0x0100 /* DAC2_VU */
-#define WM8915_DAC2_VU_SHIFT 8 /* DAC2_VU */
-#define WM8915_DAC2_VU_WIDTH 1 /* DAC2_VU */
-#define WM8915_HPOUT2L_ZC 0x0080 /* HPOUT2L_ZC */
-#define WM8915_HPOUT2L_ZC_MASK 0x0080 /* HPOUT2L_ZC */
-#define WM8915_HPOUT2L_ZC_SHIFT 7 /* HPOUT2L_ZC */
-#define WM8915_HPOUT2L_ZC_WIDTH 1 /* HPOUT2L_ZC */
-#define WM8915_HPOUT2L_VOL_MASK 0x000F /* HPOUT2L_VOL - [3:0] */
-#define WM8915_HPOUT2L_VOL_SHIFT 0 /* HPOUT2L_VOL - [3:0] */
-#define WM8915_HPOUT2L_VOL_WIDTH 4 /* HPOUT2L_VOL - [3:0] */
-
-/*
- * R31 (0x1F) - Output2 Right Volume
- */
-#define WM8915_DAC2_VU 0x0100 /* DAC2_VU */
-#define WM8915_DAC2_VU_MASK 0x0100 /* DAC2_VU */
-#define WM8915_DAC2_VU_SHIFT 8 /* DAC2_VU */
-#define WM8915_DAC2_VU_WIDTH 1 /* DAC2_VU */
-#define WM8915_HPOUT2R_ZC 0x0080 /* HPOUT2R_ZC */
-#define WM8915_HPOUT2R_ZC_MASK 0x0080 /* HPOUT2R_ZC */
-#define WM8915_HPOUT2R_ZC_SHIFT 7 /* HPOUT2R_ZC */
-#define WM8915_HPOUT2R_ZC_WIDTH 1 /* HPOUT2R_ZC */
-#define WM8915_HPOUT2R_VOL_MASK 0x000F /* HPOUT2R_VOL - [3:0] */
-#define WM8915_HPOUT2R_VOL_SHIFT 0 /* HPOUT2R_VOL - [3:0] */
-#define WM8915_HPOUT2R_VOL_WIDTH 4 /* HPOUT2R_VOL - [3:0] */
-
-/*
- * R32 (0x20) - MICBIAS (1)
- */
-#define WM8915_MICB1_RATE 0x0020 /* MICB1_RATE */
-#define WM8915_MICB1_RATE_MASK 0x0020 /* MICB1_RATE */
-#define WM8915_MICB1_RATE_SHIFT 5 /* MICB1_RATE */
-#define WM8915_MICB1_RATE_WIDTH 1 /* MICB1_RATE */
-#define WM8915_MICB1_MODE 0x0010 /* MICB1_MODE */
-#define WM8915_MICB1_MODE_MASK 0x0010 /* MICB1_MODE */
-#define WM8915_MICB1_MODE_SHIFT 4 /* MICB1_MODE */
-#define WM8915_MICB1_MODE_WIDTH 1 /* MICB1_MODE */
-#define WM8915_MICB1_LVL_MASK 0x000E /* MICB1_LVL - [3:1] */
-#define WM8915_MICB1_LVL_SHIFT 1 /* MICB1_LVL - [3:1] */
-#define WM8915_MICB1_LVL_WIDTH 3 /* MICB1_LVL - [3:1] */
-#define WM8915_MICB1_DISCH 0x0001 /* MICB1_DISCH */
-#define WM8915_MICB1_DISCH_MASK 0x0001 /* MICB1_DISCH */
-#define WM8915_MICB1_DISCH_SHIFT 0 /* MICB1_DISCH */
-#define WM8915_MICB1_DISCH_WIDTH 1 /* MICB1_DISCH */
-
-/*
- * R33 (0x21) - MICBIAS (2)
- */
-#define WM8915_MICB2_RATE 0x0020 /* MICB2_RATE */
-#define WM8915_MICB2_RATE_MASK 0x0020 /* MICB2_RATE */
-#define WM8915_MICB2_RATE_SHIFT 5 /* MICB2_RATE */
-#define WM8915_MICB2_RATE_WIDTH 1 /* MICB2_RATE */
-#define WM8915_MICB2_MODE 0x0010 /* MICB2_MODE */
-#define WM8915_MICB2_MODE_MASK 0x0010 /* MICB2_MODE */
-#define WM8915_MICB2_MODE_SHIFT 4 /* MICB2_MODE */
-#define WM8915_MICB2_MODE_WIDTH 1 /* MICB2_MODE */
-#define WM8915_MICB2_LVL_MASK 0x000E /* MICB2_LVL - [3:1] */
-#define WM8915_MICB2_LVL_SHIFT 1 /* MICB2_LVL - [3:1] */
-#define WM8915_MICB2_LVL_WIDTH 3 /* MICB2_LVL - [3:1] */
-#define WM8915_MICB2_DISCH 0x0001 /* MICB2_DISCH */
-#define WM8915_MICB2_DISCH_MASK 0x0001 /* MICB2_DISCH */
-#define WM8915_MICB2_DISCH_SHIFT 0 /* MICB2_DISCH */
-#define WM8915_MICB2_DISCH_WIDTH 1 /* MICB2_DISCH */
-
-/*
- * R40 (0x28) - LDO 1
- */
-#define WM8915_LDO1_MODE 0x0020 /* LDO1_MODE */
-#define WM8915_LDO1_MODE_MASK 0x0020 /* LDO1_MODE */
-#define WM8915_LDO1_MODE_SHIFT 5 /* LDO1_MODE */
-#define WM8915_LDO1_MODE_WIDTH 1 /* LDO1_MODE */
-#define WM8915_LDO1_VSEL_MASK 0x0006 /* LDO1_VSEL - [2:1] */
-#define WM8915_LDO1_VSEL_SHIFT 1 /* LDO1_VSEL - [2:1] */
-#define WM8915_LDO1_VSEL_WIDTH 2 /* LDO1_VSEL - [2:1] */
-#define WM8915_LDO1_DISCH 0x0001 /* LDO1_DISCH */
-#define WM8915_LDO1_DISCH_MASK 0x0001 /* LDO1_DISCH */
-#define WM8915_LDO1_DISCH_SHIFT 0 /* LDO1_DISCH */
-#define WM8915_LDO1_DISCH_WIDTH 1 /* LDO1_DISCH */
-
-/*
- * R41 (0x29) - LDO 2
- */
-#define WM8915_LDO2_MODE 0x0020 /* LDO2_MODE */
-#define WM8915_LDO2_MODE_MASK 0x0020 /* LDO2_MODE */
-#define WM8915_LDO2_MODE_SHIFT 5 /* LDO2_MODE */
-#define WM8915_LDO2_MODE_WIDTH 1 /* LDO2_MODE */
-#define WM8915_LDO2_VSEL_MASK 0x001E /* LDO2_VSEL - [4:1] */
-#define WM8915_LDO2_VSEL_SHIFT 1 /* LDO2_VSEL - [4:1] */
-#define WM8915_LDO2_VSEL_WIDTH 4 /* LDO2_VSEL - [4:1] */
-#define WM8915_LDO2_DISCH 0x0001 /* LDO2_DISCH */
-#define WM8915_LDO2_DISCH_MASK 0x0001 /* LDO2_DISCH */
-#define WM8915_LDO2_DISCH_SHIFT 0 /* LDO2_DISCH */
-#define WM8915_LDO2_DISCH_WIDTH 1 /* LDO2_DISCH */
-
-/*
- * R48 (0x30) - Accessory Detect Mode 1
- */
-#define WM8915_JD_MODE_MASK 0x0003 /* JD_MODE - [1:0] */
-#define WM8915_JD_MODE_SHIFT 0 /* JD_MODE - [1:0] */
-#define WM8915_JD_MODE_WIDTH 2 /* JD_MODE - [1:0] */
-
-/*
- * R49 (0x31) - Accessory Detect Mode 2
- */
-#define WM8915_HPOUT1FB_SRC 0x0004 /* HPOUT1FB_SRC */
-#define WM8915_HPOUT1FB_SRC_MASK 0x0004 /* HPOUT1FB_SRC */
-#define WM8915_HPOUT1FB_SRC_SHIFT 2 /* HPOUT1FB_SRC */
-#define WM8915_HPOUT1FB_SRC_WIDTH 1 /* HPOUT1FB_SRC */
-#define WM8915_MICD_SRC 0x0002 /* MICD_SRC */
-#define WM8915_MICD_SRC_MASK 0x0002 /* MICD_SRC */
-#define WM8915_MICD_SRC_SHIFT 1 /* MICD_SRC */
-#define WM8915_MICD_SRC_WIDTH 1 /* MICD_SRC */
-#define WM8915_MICD_BIAS_SRC 0x0001 /* MICD_BIAS_SRC */
-#define WM8915_MICD_BIAS_SRC_MASK 0x0001 /* MICD_BIAS_SRC */
-#define WM8915_MICD_BIAS_SRC_SHIFT 0 /* MICD_BIAS_SRC */
-#define WM8915_MICD_BIAS_SRC_WIDTH 1 /* MICD_BIAS_SRC */
-
-/*
- * R52 (0x34) - Headphone Detect 1
- */
-#define WM8915_HP_HOLDTIME_MASK 0x00E0 /* HP_HOLDTIME - [7:5] */
-#define WM8915_HP_HOLDTIME_SHIFT 5 /* HP_HOLDTIME - [7:5] */
-#define WM8915_HP_HOLDTIME_WIDTH 3 /* HP_HOLDTIME - [7:5] */
-#define WM8915_HP_CLK_DIV_MASK 0x0018 /* HP_CLK_DIV - [4:3] */
-#define WM8915_HP_CLK_DIV_SHIFT 3 /* HP_CLK_DIV - [4:3] */
-#define WM8915_HP_CLK_DIV_WIDTH 2 /* HP_CLK_DIV - [4:3] */
-#define WM8915_HP_STEP_SIZE 0x0002 /* HP_STEP_SIZE */
-#define WM8915_HP_STEP_SIZE_MASK 0x0002 /* HP_STEP_SIZE */
-#define WM8915_HP_STEP_SIZE_SHIFT 1 /* HP_STEP_SIZE */
-#define WM8915_HP_STEP_SIZE_WIDTH 1 /* HP_STEP_SIZE */
-#define WM8915_HP_POLL 0x0001 /* HP_POLL */
-#define WM8915_HP_POLL_MASK 0x0001 /* HP_POLL */
-#define WM8915_HP_POLL_SHIFT 0 /* HP_POLL */
-#define WM8915_HP_POLL_WIDTH 1 /* HP_POLL */
-
-/*
- * R53 (0x35) - Headphone Detect 2
- */
-#define WM8915_HP_DONE 0x0080 /* HP_DONE */
-#define WM8915_HP_DONE_MASK 0x0080 /* HP_DONE */
-#define WM8915_HP_DONE_SHIFT 7 /* HP_DONE */
-#define WM8915_HP_DONE_WIDTH 1 /* HP_DONE */
-#define WM8915_HP_LVL_MASK 0x007F /* HP_LVL - [6:0] */
-#define WM8915_HP_LVL_SHIFT 0 /* HP_LVL - [6:0] */
-#define WM8915_HP_LVL_WIDTH 7 /* HP_LVL - [6:0] */
-
-/*
- * R56 (0x38) - Mic Detect 1
- */
-#define WM8915_MICD_BIAS_STARTTIME_MASK 0xF000 /* MICD_BIAS_STARTTIME - [15:12] */
-#define WM8915_MICD_BIAS_STARTTIME_SHIFT 12 /* MICD_BIAS_STARTTIME - [15:12] */
-#define WM8915_MICD_BIAS_STARTTIME_WIDTH 4 /* MICD_BIAS_STARTTIME - [15:12] */
-#define WM8915_MICD_RATE_MASK 0x0F00 /* MICD_RATE - [11:8] */
-#define WM8915_MICD_RATE_SHIFT 8 /* MICD_RATE - [11:8] */
-#define WM8915_MICD_RATE_WIDTH 4 /* MICD_RATE - [11:8] */
-#define WM8915_MICD_DBTIME 0x0002 /* MICD_DBTIME */
-#define WM8915_MICD_DBTIME_MASK 0x0002 /* MICD_DBTIME */
-#define WM8915_MICD_DBTIME_SHIFT 1 /* MICD_DBTIME */
-#define WM8915_MICD_DBTIME_WIDTH 1 /* MICD_DBTIME */
-#define WM8915_MICD_ENA 0x0001 /* MICD_ENA */
-#define WM8915_MICD_ENA_MASK 0x0001 /* MICD_ENA */
-#define WM8915_MICD_ENA_SHIFT 0 /* MICD_ENA */
-#define WM8915_MICD_ENA_WIDTH 1 /* MICD_ENA */
-
-/*
- * R57 (0x39) - Mic Detect 2
- */
-#define WM8915_MICD_LVL_SEL_MASK 0x00FF /* MICD_LVL_SEL - [7:0] */
-#define WM8915_MICD_LVL_SEL_SHIFT 0 /* MICD_LVL_SEL - [7:0] */
-#define WM8915_MICD_LVL_SEL_WIDTH 8 /* MICD_LVL_SEL - [7:0] */
-
-/*
- * R58 (0x3A) - Mic Detect 3
- */
-#define WM8915_MICD_LVL_MASK 0x07FC /* MICD_LVL - [10:2] */
-#define WM8915_MICD_LVL_SHIFT 2 /* MICD_LVL - [10:2] */
-#define WM8915_MICD_LVL_WIDTH 9 /* MICD_LVL - [10:2] */
-#define WM8915_MICD_VALID 0x0002 /* MICD_VALID */
-#define WM8915_MICD_VALID_MASK 0x0002 /* MICD_VALID */
-#define WM8915_MICD_VALID_SHIFT 1 /* MICD_VALID */
-#define WM8915_MICD_VALID_WIDTH 1 /* MICD_VALID */
-#define WM8915_MICD_STS 0x0001 /* MICD_STS */
-#define WM8915_MICD_STS_MASK 0x0001 /* MICD_STS */
-#define WM8915_MICD_STS_SHIFT 0 /* MICD_STS */
-#define WM8915_MICD_STS_WIDTH 1 /* MICD_STS */
-
-/*
- * R64 (0x40) - Charge Pump (1)
- */
-#define WM8915_CP_ENA 0x8000 /* CP_ENA */
-#define WM8915_CP_ENA_MASK 0x8000 /* CP_ENA */
-#define WM8915_CP_ENA_SHIFT 15 /* CP_ENA */
-#define WM8915_CP_ENA_WIDTH 1 /* CP_ENA */
-
-/*
- * R65 (0x41) - Charge Pump (2)
- */
-#define WM8915_CP_DISCH 0x8000 /* CP_DISCH */
-#define WM8915_CP_DISCH_MASK 0x8000 /* CP_DISCH */
-#define WM8915_CP_DISCH_SHIFT 15 /* CP_DISCH */
-#define WM8915_CP_DISCH_WIDTH 1 /* CP_DISCH */
-
-/*
- * R80 (0x50) - DC Servo (1)
- */
-#define WM8915_DCS_ENA_CHAN_3 0x0008 /* DCS_ENA_CHAN_3 */
-#define WM8915_DCS_ENA_CHAN_3_MASK 0x0008 /* DCS_ENA_CHAN_3 */
-#define WM8915_DCS_ENA_CHAN_3_SHIFT 3 /* DCS_ENA_CHAN_3 */
-#define WM8915_DCS_ENA_CHAN_3_WIDTH 1 /* DCS_ENA_CHAN_3 */
-#define WM8915_DCS_ENA_CHAN_2 0x0004 /* DCS_ENA_CHAN_2 */
-#define WM8915_DCS_ENA_CHAN_2_MASK 0x0004 /* DCS_ENA_CHAN_2 */
-#define WM8915_DCS_ENA_CHAN_2_SHIFT 2 /* DCS_ENA_CHAN_2 */
-#define WM8915_DCS_ENA_CHAN_2_WIDTH 1 /* DCS_ENA_CHAN_2 */
-#define WM8915_DCS_ENA_CHAN_1 0x0002 /* DCS_ENA_CHAN_1 */
-#define WM8915_DCS_ENA_CHAN_1_MASK 0x0002 /* DCS_ENA_CHAN_1 */
-#define WM8915_DCS_ENA_CHAN_1_SHIFT 1 /* DCS_ENA_CHAN_1 */
-#define WM8915_DCS_ENA_CHAN_1_WIDTH 1 /* DCS_ENA_CHAN_1 */
-#define WM8915_DCS_ENA_CHAN_0 0x0001 /* DCS_ENA_CHAN_0 */
-#define WM8915_DCS_ENA_CHAN_0_MASK 0x0001 /* DCS_ENA_CHAN_0 */
-#define WM8915_DCS_ENA_CHAN_0_SHIFT 0 /* DCS_ENA_CHAN_0 */
-#define WM8915_DCS_ENA_CHAN_0_WIDTH 1 /* DCS_ENA_CHAN_0 */
-
-/*
- * R81 (0x51) - DC Servo (2)
- */
-#define WM8915_DCS_TRIG_SINGLE_3 0x8000 /* DCS_TRIG_SINGLE_3 */
-#define WM8915_DCS_TRIG_SINGLE_3_MASK 0x8000 /* DCS_TRIG_SINGLE_3 */
-#define WM8915_DCS_TRIG_SINGLE_3_SHIFT 15 /* DCS_TRIG_SINGLE_3 */
-#define WM8915_DCS_TRIG_SINGLE_3_WIDTH 1 /* DCS_TRIG_SINGLE_3 */
-#define WM8915_DCS_TRIG_SINGLE_2 0x4000 /* DCS_TRIG_SINGLE_2 */
-#define WM8915_DCS_TRIG_SINGLE_2_MASK 0x4000 /* DCS_TRIG_SINGLE_2 */
-#define WM8915_DCS_TRIG_SINGLE_2_SHIFT 14 /* DCS_TRIG_SINGLE_2 */
-#define WM8915_DCS_TRIG_SINGLE_2_WIDTH 1 /* DCS_TRIG_SINGLE_2 */
-#define WM8915_DCS_TRIG_SINGLE_1 0x2000 /* DCS_TRIG_SINGLE_1 */
-#define WM8915_DCS_TRIG_SINGLE_1_MASK 0x2000 /* DCS_TRIG_SINGLE_1 */
-#define WM8915_DCS_TRIG_SINGLE_1_SHIFT 13 /* DCS_TRIG_SINGLE_1 */
-#define WM8915_DCS_TRIG_SINGLE_1_WIDTH 1 /* DCS_TRIG_SINGLE_1 */
-#define WM8915_DCS_TRIG_SINGLE_0 0x1000 /* DCS_TRIG_SINGLE_0 */
-#define WM8915_DCS_TRIG_SINGLE_0_MASK 0x1000 /* DCS_TRIG_SINGLE_0 */
-#define WM8915_DCS_TRIG_SINGLE_0_SHIFT 12 /* DCS_TRIG_SINGLE_0 */
-#define WM8915_DCS_TRIG_SINGLE_0_WIDTH 1 /* DCS_TRIG_SINGLE_0 */
-#define WM8915_DCS_TRIG_SERIES_3 0x0800 /* DCS_TRIG_SERIES_3 */
-#define WM8915_DCS_TRIG_SERIES_3_MASK 0x0800 /* DCS_TRIG_SERIES_3 */
-#define WM8915_DCS_TRIG_SERIES_3_SHIFT 11 /* DCS_TRIG_SERIES_3 */
-#define WM8915_DCS_TRIG_SERIES_3_WIDTH 1 /* DCS_TRIG_SERIES_3 */
-#define WM8915_DCS_TRIG_SERIES_2 0x0400 /* DCS_TRIG_SERIES_2 */
-#define WM8915_DCS_TRIG_SERIES_2_MASK 0x0400 /* DCS_TRIG_SERIES_2 */
-#define WM8915_DCS_TRIG_SERIES_2_SHIFT 10 /* DCS_TRIG_SERIES_2 */
-#define WM8915_DCS_TRIG_SERIES_2_WIDTH 1 /* DCS_TRIG_SERIES_2 */
-#define WM8915_DCS_TRIG_SERIES_1 0x0200 /* DCS_TRIG_SERIES_1 */
-#define WM8915_DCS_TRIG_SERIES_1_MASK 0x0200 /* DCS_TRIG_SERIES_1 */
-#define WM8915_DCS_TRIG_SERIES_1_SHIFT 9 /* DCS_TRIG_SERIES_1 */
-#define WM8915_DCS_TRIG_SERIES_1_WIDTH 1 /* DCS_TRIG_SERIES_1 */
-#define WM8915_DCS_TRIG_SERIES_0 0x0100 /* DCS_TRIG_SERIES_0 */
-#define WM8915_DCS_TRIG_SERIES_0_MASK 0x0100 /* DCS_TRIG_SERIES_0 */
-#define WM8915_DCS_TRIG_SERIES_0_SHIFT 8 /* DCS_TRIG_SERIES_0 */
-#define WM8915_DCS_TRIG_SERIES_0_WIDTH 1 /* DCS_TRIG_SERIES_0 */
-#define WM8915_DCS_TRIG_STARTUP_3 0x0080 /* DCS_TRIG_STARTUP_3 */
-#define WM8915_DCS_TRIG_STARTUP_3_MASK 0x0080 /* DCS_TRIG_STARTUP_3 */
-#define WM8915_DCS_TRIG_STARTUP_3_SHIFT 7 /* DCS_TRIG_STARTUP_3 */
-#define WM8915_DCS_TRIG_STARTUP_3_WIDTH 1 /* DCS_TRIG_STARTUP_3 */
-#define WM8915_DCS_TRIG_STARTUP_2 0x0040 /* DCS_TRIG_STARTUP_2 */
-#define WM8915_DCS_TRIG_STARTUP_2_MASK 0x0040 /* DCS_TRIG_STARTUP_2 */
-#define WM8915_DCS_TRIG_STARTUP_2_SHIFT 6 /* DCS_TRIG_STARTUP_2 */
-#define WM8915_DCS_TRIG_STARTUP_2_WIDTH 1 /* DCS_TRIG_STARTUP_2 */
-#define WM8915_DCS_TRIG_STARTUP_1 0x0020 /* DCS_TRIG_STARTUP_1 */
-#define WM8915_DCS_TRIG_STARTUP_1_MASK 0x0020 /* DCS_TRIG_STARTUP_1 */
-#define WM8915_DCS_TRIG_STARTUP_1_SHIFT 5 /* DCS_TRIG_STARTUP_1 */
-#define WM8915_DCS_TRIG_STARTUP_1_WIDTH 1 /* DCS_TRIG_STARTUP_1 */
-#define WM8915_DCS_TRIG_STARTUP_0 0x0010 /* DCS_TRIG_STARTUP_0 */
-#define WM8915_DCS_TRIG_STARTUP_0_MASK 0x0010 /* DCS_TRIG_STARTUP_0 */
-#define WM8915_DCS_TRIG_STARTUP_0_SHIFT 4 /* DCS_TRIG_STARTUP_0 */
-#define WM8915_DCS_TRIG_STARTUP_0_WIDTH 1 /* DCS_TRIG_STARTUP_0 */
-#define WM8915_DCS_TRIG_DAC_WR_3 0x0008 /* DCS_TRIG_DAC_WR_3 */
-#define WM8915_DCS_TRIG_DAC_WR_3_MASK 0x0008 /* DCS_TRIG_DAC_WR_3 */
-#define WM8915_DCS_TRIG_DAC_WR_3_SHIFT 3 /* DCS_TRIG_DAC_WR_3 */
-#define WM8915_DCS_TRIG_DAC_WR_3_WIDTH 1 /* DCS_TRIG_DAC_WR_3 */
-#define WM8915_DCS_TRIG_DAC_WR_2 0x0004 /* DCS_TRIG_DAC_WR_2 */
-#define WM8915_DCS_TRIG_DAC_WR_2_MASK 0x0004 /* DCS_TRIG_DAC_WR_2 */
-#define WM8915_DCS_TRIG_DAC_WR_2_SHIFT 2 /* DCS_TRIG_DAC_WR_2 */
-#define WM8915_DCS_TRIG_DAC_WR_2_WIDTH 1 /* DCS_TRIG_DAC_WR_2 */
-#define WM8915_DCS_TRIG_DAC_WR_1 0x0002 /* DCS_TRIG_DAC_WR_1 */
-#define WM8915_DCS_TRIG_DAC_WR_1_MASK 0x0002 /* DCS_TRIG_DAC_WR_1 */
-#define WM8915_DCS_TRIG_DAC_WR_1_SHIFT 1 /* DCS_TRIG_DAC_WR_1 */
-#define WM8915_DCS_TRIG_DAC_WR_1_WIDTH 1 /* DCS_TRIG_DAC_WR_1 */
-#define WM8915_DCS_TRIG_DAC_WR_0 0x0001 /* DCS_TRIG_DAC_WR_0 */
-#define WM8915_DCS_TRIG_DAC_WR_0_MASK 0x0001 /* DCS_TRIG_DAC_WR_0 */
-#define WM8915_DCS_TRIG_DAC_WR_0_SHIFT 0 /* DCS_TRIG_DAC_WR_0 */
-#define WM8915_DCS_TRIG_DAC_WR_0_WIDTH 1 /* DCS_TRIG_DAC_WR_0 */
-
-/*
- * R82 (0x52) - DC Servo (3)
- */
-#define WM8915_DCS_TIMER_PERIOD_23_MASK 0x0F00 /* DCS_TIMER_PERIOD_23 - [11:8] */
-#define WM8915_DCS_TIMER_PERIOD_23_SHIFT 8 /* DCS_TIMER_PERIOD_23 - [11:8] */
-#define WM8915_DCS_TIMER_PERIOD_23_WIDTH 4 /* DCS_TIMER_PERIOD_23 - [11:8] */
-#define WM8915_DCS_TIMER_PERIOD_01_MASK 0x000F /* DCS_TIMER_PERIOD_01 - [3:0] */
-#define WM8915_DCS_TIMER_PERIOD_01_SHIFT 0 /* DCS_TIMER_PERIOD_01 - [3:0] */
-#define WM8915_DCS_TIMER_PERIOD_01_WIDTH 4 /* DCS_TIMER_PERIOD_01 - [3:0] */
-
-/*
- * R84 (0x54) - DC Servo (5)
- */
-#define WM8915_DCS_SERIES_NO_23_MASK 0x7F00 /* DCS_SERIES_NO_23 - [14:8] */
-#define WM8915_DCS_SERIES_NO_23_SHIFT 8 /* DCS_SERIES_NO_23 - [14:8] */
-#define WM8915_DCS_SERIES_NO_23_WIDTH 7 /* DCS_SERIES_NO_23 - [14:8] */
-#define WM8915_DCS_SERIES_NO_01_MASK 0x007F /* DCS_SERIES_NO_01 - [6:0] */
-#define WM8915_DCS_SERIES_NO_01_SHIFT 0 /* DCS_SERIES_NO_01 - [6:0] */
-#define WM8915_DCS_SERIES_NO_01_WIDTH 7 /* DCS_SERIES_NO_01 - [6:0] */
-
-/*
- * R85 (0x55) - DC Servo (6)
- */
-#define WM8915_DCS_DAC_WR_VAL_3_MASK 0xFF00 /* DCS_DAC_WR_VAL_3 - [15:8] */
-#define WM8915_DCS_DAC_WR_VAL_3_SHIFT 8 /* DCS_DAC_WR_VAL_3 - [15:8] */
-#define WM8915_DCS_DAC_WR_VAL_3_WIDTH 8 /* DCS_DAC_WR_VAL_3 - [15:8] */
-#define WM8915_DCS_DAC_WR_VAL_2_MASK 0x00FF /* DCS_DAC_WR_VAL_2 - [7:0] */
-#define WM8915_DCS_DAC_WR_VAL_2_SHIFT 0 /* DCS_DAC_WR_VAL_2 - [7:0] */
-#define WM8915_DCS_DAC_WR_VAL_2_WIDTH 8 /* DCS_DAC_WR_VAL_2 - [7:0] */
-
-/*
- * R86 (0x56) - DC Servo (7)
- */
-#define WM8915_DCS_DAC_WR_VAL_1_MASK 0xFF00 /* DCS_DAC_WR_VAL_1 - [15:8] */
-#define WM8915_DCS_DAC_WR_VAL_1_SHIFT 8 /* DCS_DAC_WR_VAL_1 - [15:8] */
-#define WM8915_DCS_DAC_WR_VAL_1_WIDTH 8 /* DCS_DAC_WR_VAL_1 - [15:8] */
-#define WM8915_DCS_DAC_WR_VAL_0_MASK 0x00FF /* DCS_DAC_WR_VAL_0 - [7:0] */
-#define WM8915_DCS_DAC_WR_VAL_0_SHIFT 0 /* DCS_DAC_WR_VAL_0 - [7:0] */
-#define WM8915_DCS_DAC_WR_VAL_0_WIDTH 8 /* DCS_DAC_WR_VAL_0 - [7:0] */
-
-/*
- * R87 (0x57) - DC Servo Readback 0
- */
-#define WM8915_DCS_CAL_COMPLETE_MASK 0x0F00 /* DCS_CAL_COMPLETE - [11:8] */
-#define WM8915_DCS_CAL_COMPLETE_SHIFT 8 /* DCS_CAL_COMPLETE - [11:8] */
-#define WM8915_DCS_CAL_COMPLETE_WIDTH 4 /* DCS_CAL_COMPLETE - [11:8] */
-#define WM8915_DCS_DAC_WR_COMPLETE_MASK 0x00F0 /* DCS_DAC_WR_COMPLETE - [7:4] */
-#define WM8915_DCS_DAC_WR_COMPLETE_SHIFT 4 /* DCS_DAC_WR_COMPLETE - [7:4] */
-#define WM8915_DCS_DAC_WR_COMPLETE_WIDTH 4 /* DCS_DAC_WR_COMPLETE - [7:4] */
-#define WM8915_DCS_STARTUP_COMPLETE_MASK 0x000F /* DCS_STARTUP_COMPLETE - [3:0] */
-#define WM8915_DCS_STARTUP_COMPLETE_SHIFT 0 /* DCS_STARTUP_COMPLETE - [3:0] */
-#define WM8915_DCS_STARTUP_COMPLETE_WIDTH 4 /* DCS_STARTUP_COMPLETE - [3:0] */
-
-/*
- * R96 (0x60) - Analogue HP (1)
- */
-#define WM8915_HPOUT1L_RMV_SHORT 0x0080 /* HPOUT1L_RMV_SHORT */
-#define WM8915_HPOUT1L_RMV_SHORT_MASK 0x0080 /* HPOUT1L_RMV_SHORT */
-#define WM8915_HPOUT1L_RMV_SHORT_SHIFT 7 /* HPOUT1L_RMV_SHORT */
-#define WM8915_HPOUT1L_RMV_SHORT_WIDTH 1 /* HPOUT1L_RMV_SHORT */
-#define WM8915_HPOUT1L_OUTP 0x0040 /* HPOUT1L_OUTP */
-#define WM8915_HPOUT1L_OUTP_MASK 0x0040 /* HPOUT1L_OUTP */
-#define WM8915_HPOUT1L_OUTP_SHIFT 6 /* HPOUT1L_OUTP */
-#define WM8915_HPOUT1L_OUTP_WIDTH 1 /* HPOUT1L_OUTP */
-#define WM8915_HPOUT1L_DLY 0x0020 /* HPOUT1L_DLY */
-#define WM8915_HPOUT1L_DLY_MASK 0x0020 /* HPOUT1L_DLY */
-#define WM8915_HPOUT1L_DLY_SHIFT 5 /* HPOUT1L_DLY */
-#define WM8915_HPOUT1L_DLY_WIDTH 1 /* HPOUT1L_DLY */
-#define WM8915_HPOUT1R_RMV_SHORT 0x0008 /* HPOUT1R_RMV_SHORT */
-#define WM8915_HPOUT1R_RMV_SHORT_MASK 0x0008 /* HPOUT1R_RMV_SHORT */
-#define WM8915_HPOUT1R_RMV_SHORT_SHIFT 3 /* HPOUT1R_RMV_SHORT */
-#define WM8915_HPOUT1R_RMV_SHORT_WIDTH 1 /* HPOUT1R_RMV_SHORT */
-#define WM8915_HPOUT1R_OUTP 0x0004 /* HPOUT1R_OUTP */
-#define WM8915_HPOUT1R_OUTP_MASK 0x0004 /* HPOUT1R_OUTP */
-#define WM8915_HPOUT1R_OUTP_SHIFT 2 /* HPOUT1R_OUTP */
-#define WM8915_HPOUT1R_OUTP_WIDTH 1 /* HPOUT1R_OUTP */
-#define WM8915_HPOUT1R_DLY 0x0002 /* HPOUT1R_DLY */
-#define WM8915_HPOUT1R_DLY_MASK 0x0002 /* HPOUT1R_DLY */
-#define WM8915_HPOUT1R_DLY_SHIFT 1 /* HPOUT1R_DLY */
-#define WM8915_HPOUT1R_DLY_WIDTH 1 /* HPOUT1R_DLY */
-
-/*
- * R97 (0x61) - Analogue HP (2)
- */
-#define WM8915_HPOUT2L_RMV_SHORT 0x0080 /* HPOUT2L_RMV_SHORT */
-#define WM8915_HPOUT2L_RMV_SHORT_MASK 0x0080 /* HPOUT2L_RMV_SHORT */
-#define WM8915_HPOUT2L_RMV_SHORT_SHIFT 7 /* HPOUT2L_RMV_SHORT */
-#define WM8915_HPOUT2L_RMV_SHORT_WIDTH 1 /* HPOUT2L_RMV_SHORT */
-#define WM8915_HPOUT2L_OUTP 0x0040 /* HPOUT2L_OUTP */
-#define WM8915_HPOUT2L_OUTP_MASK 0x0040 /* HPOUT2L_OUTP */
-#define WM8915_HPOUT2L_OUTP_SHIFT 6 /* HPOUT2L_OUTP */
-#define WM8915_HPOUT2L_OUTP_WIDTH 1 /* HPOUT2L_OUTP */
-#define WM8915_HPOUT2L_DLY 0x0020 /* HPOUT2L_DLY */
-#define WM8915_HPOUT2L_DLY_MASK 0x0020 /* HPOUT2L_DLY */
-#define WM8915_HPOUT2L_DLY_SHIFT 5 /* HPOUT2L_DLY */
-#define WM8915_HPOUT2L_DLY_WIDTH 1 /* HPOUT2L_DLY */
-#define WM8915_HPOUT2R_RMV_SHORT 0x0008 /* HPOUT2R_RMV_SHORT */
-#define WM8915_HPOUT2R_RMV_SHORT_MASK 0x0008 /* HPOUT2R_RMV_SHORT */
-#define WM8915_HPOUT2R_RMV_SHORT_SHIFT 3 /* HPOUT2R_RMV_SHORT */
-#define WM8915_HPOUT2R_RMV_SHORT_WIDTH 1 /* HPOUT2R_RMV_SHORT */
-#define WM8915_HPOUT2R_OUTP 0x0004 /* HPOUT2R_OUTP */
-#define WM8915_HPOUT2R_OUTP_MASK 0x0004 /* HPOUT2R_OUTP */
-#define WM8915_HPOUT2R_OUTP_SHIFT 2 /* HPOUT2R_OUTP */
-#define WM8915_HPOUT2R_OUTP_WIDTH 1 /* HPOUT2R_OUTP */
-#define WM8915_HPOUT2R_DLY 0x0002 /* HPOUT2R_DLY */
-#define WM8915_HPOUT2R_DLY_MASK 0x0002 /* HPOUT2R_DLY */
-#define WM8915_HPOUT2R_DLY_SHIFT 1 /* HPOUT2R_DLY */
-#define WM8915_HPOUT2R_DLY_WIDTH 1 /* HPOUT2R_DLY */
-
-/*
- * R256 (0x100) - Chip Revision
- */
-#define WM8915_CHIP_REV_MASK 0x000F /* CHIP_REV - [3:0] */
-#define WM8915_CHIP_REV_SHIFT 0 /* CHIP_REV - [3:0] */
-#define WM8915_CHIP_REV_WIDTH 4 /* CHIP_REV - [3:0] */
-
-/*
- * R257 (0x101) - Control Interface (1)
- */
-#define WM8915_AUTO_INC 0x0004 /* AUTO_INC */
-#define WM8915_AUTO_INC_MASK 0x0004 /* AUTO_INC */
-#define WM8915_AUTO_INC_SHIFT 2 /* AUTO_INC */
-#define WM8915_AUTO_INC_WIDTH 1 /* AUTO_INC */
-
-/*
- * R272 (0x110) - Write Sequencer Ctrl (1)
- */
-#define WM8915_WSEQ_ENA 0x8000 /* WSEQ_ENA */
-#define WM8915_WSEQ_ENA_MASK 0x8000 /* WSEQ_ENA */
-#define WM8915_WSEQ_ENA_SHIFT 15 /* WSEQ_ENA */
-#define WM8915_WSEQ_ENA_WIDTH 1 /* WSEQ_ENA */
-#define WM8915_WSEQ_ABORT 0x0200 /* WSEQ_ABORT */
-#define WM8915_WSEQ_ABORT_MASK 0x0200 /* WSEQ_ABORT */
-#define WM8915_WSEQ_ABORT_SHIFT 9 /* WSEQ_ABORT */
-#define WM8915_WSEQ_ABORT_WIDTH 1 /* WSEQ_ABORT */
-#define WM8915_WSEQ_START 0x0100 /* WSEQ_START */
-#define WM8915_WSEQ_START_MASK 0x0100 /* WSEQ_START */
-#define WM8915_WSEQ_START_SHIFT 8 /* WSEQ_START */
-#define WM8915_WSEQ_START_WIDTH 1 /* WSEQ_START */
-#define WM8915_WSEQ_START_INDEX_MASK 0x007F /* WSEQ_START_INDEX - [6:0] */
-#define WM8915_WSEQ_START_INDEX_SHIFT 0 /* WSEQ_START_INDEX - [6:0] */
-#define WM8915_WSEQ_START_INDEX_WIDTH 7 /* WSEQ_START_INDEX - [6:0] */
-
-/*
- * R273 (0x111) - Write Sequencer Ctrl (2)
- */
-#define WM8915_WSEQ_BUSY 0x0100 /* WSEQ_BUSY */
-#define WM8915_WSEQ_BUSY_MASK 0x0100 /* WSEQ_BUSY */
-#define WM8915_WSEQ_BUSY_SHIFT 8 /* WSEQ_BUSY */
-#define WM8915_WSEQ_BUSY_WIDTH 1 /* WSEQ_BUSY */
-#define WM8915_WSEQ_CURRENT_INDEX_MASK 0x007F /* WSEQ_CURRENT_INDEX - [6:0] */
-#define WM8915_WSEQ_CURRENT_INDEX_SHIFT 0 /* WSEQ_CURRENT_INDEX - [6:0] */
-#define WM8915_WSEQ_CURRENT_INDEX_WIDTH 7 /* WSEQ_CURRENT_INDEX - [6:0] */
-
-/*
- * R512 (0x200) - AIF Clocking (1)
- */
-#define WM8915_SYSCLK_SRC_MASK 0x0018 /* SYSCLK_SRC - [4:3] */
-#define WM8915_SYSCLK_SRC_SHIFT 3 /* SYSCLK_SRC - [4:3] */
-#define WM8915_SYSCLK_SRC_WIDTH 2 /* SYSCLK_SRC - [4:3] */
-#define WM8915_SYSCLK_INV 0x0004 /* SYSCLK_INV */
-#define WM8915_SYSCLK_INV_MASK 0x0004 /* SYSCLK_INV */
-#define WM8915_SYSCLK_INV_SHIFT 2 /* SYSCLK_INV */
-#define WM8915_SYSCLK_INV_WIDTH 1 /* SYSCLK_INV */
-#define WM8915_SYSCLK_DIV 0x0002 /* SYSCLK_DIV */
-#define WM8915_SYSCLK_DIV_MASK 0x0002 /* SYSCLK_DIV */
-#define WM8915_SYSCLK_DIV_SHIFT 1 /* SYSCLK_DIV */
-#define WM8915_SYSCLK_DIV_WIDTH 1 /* SYSCLK_DIV */
-#define WM8915_SYSCLK_ENA 0x0001 /* SYSCLK_ENA */
-#define WM8915_SYSCLK_ENA_MASK 0x0001 /* SYSCLK_ENA */
-#define WM8915_SYSCLK_ENA_SHIFT 0 /* SYSCLK_ENA */
-#define WM8915_SYSCLK_ENA_WIDTH 1 /* SYSCLK_ENA */
-
-/*
- * R513 (0x201) - AIF Clocking (2)
- */
-#define WM8915_DSP2_DIV_MASK 0x0018 /* DSP2_DIV - [4:3] */
-#define WM8915_DSP2_DIV_SHIFT 3 /* DSP2_DIV - [4:3] */
-#define WM8915_DSP2_DIV_WIDTH 2 /* DSP2_DIV - [4:3] */
-#define WM8915_DSP1_DIV_MASK 0x0003 /* DSP1_DIV - [1:0] */
-#define WM8915_DSP1_DIV_SHIFT 0 /* DSP1_DIV - [1:0] */
-#define WM8915_DSP1_DIV_WIDTH 2 /* DSP1_DIV - [1:0] */
-
-/*
- * R520 (0x208) - Clocking (1)
- */
-#define WM8915_LFCLK_ENA 0x0020 /* LFCLK_ENA */
-#define WM8915_LFCLK_ENA_MASK 0x0020 /* LFCLK_ENA */
-#define WM8915_LFCLK_ENA_SHIFT 5 /* LFCLK_ENA */
-#define WM8915_LFCLK_ENA_WIDTH 1 /* LFCLK_ENA */
-#define WM8915_TOCLK_ENA 0x0010 /* TOCLK_ENA */
-#define WM8915_TOCLK_ENA_MASK 0x0010 /* TOCLK_ENA */
-#define WM8915_TOCLK_ENA_SHIFT 4 /* TOCLK_ENA */
-#define WM8915_TOCLK_ENA_WIDTH 1 /* TOCLK_ENA */
-#define WM8915_AIFCLK_ENA 0x0004 /* AIFCLK_ENA */
-#define WM8915_AIFCLK_ENA_MASK 0x0004 /* AIFCLK_ENA */
-#define WM8915_AIFCLK_ENA_SHIFT 2 /* AIFCLK_ENA */
-#define WM8915_AIFCLK_ENA_WIDTH 1 /* AIFCLK_ENA */
-#define WM8915_SYSDSPCLK_ENA 0x0002 /* SYSDSPCLK_ENA */
-#define WM8915_SYSDSPCLK_ENA_MASK 0x0002 /* SYSDSPCLK_ENA */
-#define WM8915_SYSDSPCLK_ENA_SHIFT 1 /* SYSDSPCLK_ENA */
-#define WM8915_SYSDSPCLK_ENA_WIDTH 1 /* SYSDSPCLK_ENA */
-
-/*
- * R521 (0x209) - Clocking (2)
- */
-#define WM8915_TOCLK_DIV_MASK 0x0700 /* TOCLK_DIV - [10:8] */
-#define WM8915_TOCLK_DIV_SHIFT 8 /* TOCLK_DIV - [10:8] */
-#define WM8915_TOCLK_DIV_WIDTH 3 /* TOCLK_DIV - [10:8] */
-#define WM8915_DBCLK_DIV_MASK 0x00F0 /* DBCLK_DIV - [7:4] */
-#define WM8915_DBCLK_DIV_SHIFT 4 /* DBCLK_DIV - [7:4] */
-#define WM8915_DBCLK_DIV_WIDTH 4 /* DBCLK_DIV - [7:4] */
-#define WM8915_OPCLK_DIV_MASK 0x0007 /* OPCLK_DIV - [2:0] */
-#define WM8915_OPCLK_DIV_SHIFT 0 /* OPCLK_DIV - [2:0] */
-#define WM8915_OPCLK_DIV_WIDTH 3 /* OPCLK_DIV - [2:0] */
-
-/*
- * R528 (0x210) - AIF Rate
- */
-#define WM8915_SYSCLK_RATE 0x0001 /* SYSCLK_RATE */
-#define WM8915_SYSCLK_RATE_MASK 0x0001 /* SYSCLK_RATE */
-#define WM8915_SYSCLK_RATE_SHIFT 0 /* SYSCLK_RATE */
-#define WM8915_SYSCLK_RATE_WIDTH 1 /* SYSCLK_RATE */
-
-/*
- * R544 (0x220) - FLL Control (1)
- */
-#define WM8915_FLL_OSC_ENA 0x0002 /* FLL_OSC_ENA */
-#define WM8915_FLL_OSC_ENA_MASK 0x0002 /* FLL_OSC_ENA */
-#define WM8915_FLL_OSC_ENA_SHIFT 1 /* FLL_OSC_ENA */
-#define WM8915_FLL_OSC_ENA_WIDTH 1 /* FLL_OSC_ENA */
-#define WM8915_FLL_ENA 0x0001 /* FLL_ENA */
-#define WM8915_FLL_ENA_MASK 0x0001 /* FLL_ENA */
-#define WM8915_FLL_ENA_SHIFT 0 /* FLL_ENA */
-#define WM8915_FLL_ENA_WIDTH 1 /* FLL_ENA */
-
-/*
- * R545 (0x221) - FLL Control (2)
- */
-#define WM8915_FLL_OUTDIV_MASK 0x3F00 /* FLL_OUTDIV - [13:8] */
-#define WM8915_FLL_OUTDIV_SHIFT 8 /* FLL_OUTDIV - [13:8] */
-#define WM8915_FLL_OUTDIV_WIDTH 6 /* FLL_OUTDIV - [13:8] */
-#define WM8915_FLL_FRATIO_MASK 0x0007 /* FLL_FRATIO - [2:0] */
-#define WM8915_FLL_FRATIO_SHIFT 0 /* FLL_FRATIO - [2:0] */
-#define WM8915_FLL_FRATIO_WIDTH 3 /* FLL_FRATIO - [2:0] */
-
-/*
- * R546 (0x222) - FLL Control (3)
- */
-#define WM8915_FLL_THETA_MASK 0xFFFF /* FLL_THETA - [15:0] */
-#define WM8915_FLL_THETA_SHIFT 0 /* FLL_THETA - [15:0] */
-#define WM8915_FLL_THETA_WIDTH 16 /* FLL_THETA - [15:0] */
-
-/*
- * R547 (0x223) - FLL Control (4)
- */
-#define WM8915_FLL_N_MASK 0x7FE0 /* FLL_N - [14:5] */
-#define WM8915_FLL_N_SHIFT 5 /* FLL_N - [14:5] */
-#define WM8915_FLL_N_WIDTH 10 /* FLL_N - [14:5] */
-#define WM8915_FLL_LOOP_GAIN_MASK 0x000F /* FLL_LOOP_GAIN - [3:0] */
-#define WM8915_FLL_LOOP_GAIN_SHIFT 0 /* FLL_LOOP_GAIN - [3:0] */
-#define WM8915_FLL_LOOP_GAIN_WIDTH 4 /* FLL_LOOP_GAIN - [3:0] */
-
-/*
- * R548 (0x224) - FLL Control (5)
- */
-#define WM8915_FLL_FRC_NCO_VAL_MASK 0x1F80 /* FLL_FRC_NCO_VAL - [12:7] */
-#define WM8915_FLL_FRC_NCO_VAL_SHIFT 7 /* FLL_FRC_NCO_VAL - [12:7] */
-#define WM8915_FLL_FRC_NCO_VAL_WIDTH 6 /* FLL_FRC_NCO_VAL - [12:7] */
-#define WM8915_FLL_FRC_NCO 0x0040 /* FLL_FRC_NCO */
-#define WM8915_FLL_FRC_NCO_MASK 0x0040 /* FLL_FRC_NCO */
-#define WM8915_FLL_FRC_NCO_SHIFT 6 /* FLL_FRC_NCO */
-#define WM8915_FLL_FRC_NCO_WIDTH 1 /* FLL_FRC_NCO */
-#define WM8915_FLL_REFCLK_DIV_MASK 0x0018 /* FLL_REFCLK_DIV - [4:3] */
-#define WM8915_FLL_REFCLK_DIV_SHIFT 3 /* FLL_REFCLK_DIV - [4:3] */
-#define WM8915_FLL_REFCLK_DIV_WIDTH 2 /* FLL_REFCLK_DIV - [4:3] */
-#define WM8915_FLL_REF_FREQ 0x0004 /* FLL_REF_FREQ */
-#define WM8915_FLL_REF_FREQ_MASK 0x0004 /* FLL_REF_FREQ */
-#define WM8915_FLL_REF_FREQ_SHIFT 2 /* FLL_REF_FREQ */
-#define WM8915_FLL_REF_FREQ_WIDTH 1 /* FLL_REF_FREQ */
-#define WM8915_FLL_REFCLK_SRC_MASK 0x0003 /* FLL_REFCLK_SRC - [1:0] */
-#define WM8915_FLL_REFCLK_SRC_SHIFT 0 /* FLL_REFCLK_SRC - [1:0] */
-#define WM8915_FLL_REFCLK_SRC_WIDTH 2 /* FLL_REFCLK_SRC - [1:0] */
-
-/*
- * R549 (0x225) - FLL Control (6)
- */
-#define WM8915_FLL_REFCLK_SRC_STS_MASK 0x000C /* FLL_REFCLK_SRC_STS - [3:2] */
-#define WM8915_FLL_REFCLK_SRC_STS_SHIFT 2 /* FLL_REFCLK_SRC_STS - [3:2] */
-#define WM8915_FLL_REFCLK_SRC_STS_WIDTH 2 /* FLL_REFCLK_SRC_STS - [3:2] */
-#define WM8915_FLL_SWITCH_CLK 0x0001 /* FLL_SWITCH_CLK */
-#define WM8915_FLL_SWITCH_CLK_MASK 0x0001 /* FLL_SWITCH_CLK */
-#define WM8915_FLL_SWITCH_CLK_SHIFT 0 /* FLL_SWITCH_CLK */
-#define WM8915_FLL_SWITCH_CLK_WIDTH 1 /* FLL_SWITCH_CLK */
-
-/*
- * R550 (0x226) - FLL EFS 1
- */
-#define WM8915_FLL_LAMBDA_MASK 0xFFFF /* FLL_LAMBDA - [15:0] */
-#define WM8915_FLL_LAMBDA_SHIFT 0 /* FLL_LAMBDA - [15:0] */
-#define WM8915_FLL_LAMBDA_WIDTH 16 /* FLL_LAMBDA - [15:0] */
-
-/*
- * R551 (0x227) - FLL EFS 2
- */
-#define WM8915_FLL_LFSR_SEL_MASK 0x0006 /* FLL_LFSR_SEL - [2:1] */
-#define WM8915_FLL_LFSR_SEL_SHIFT 1 /* FLL_LFSR_SEL - [2:1] */
-#define WM8915_FLL_LFSR_SEL_WIDTH 2 /* FLL_LFSR_SEL - [2:1] */
-#define WM8915_FLL_EFS_ENA 0x0001 /* FLL_EFS_ENA */
-#define WM8915_FLL_EFS_ENA_MASK 0x0001 /* FLL_EFS_ENA */
-#define WM8915_FLL_EFS_ENA_SHIFT 0 /* FLL_EFS_ENA */
-#define WM8915_FLL_EFS_ENA_WIDTH 1 /* FLL_EFS_ENA */
-
-/*
- * R768 (0x300) - AIF1 Control
- */
-#define WM8915_AIF1_TRI 0x0004 /* AIF1_TRI */
-#define WM8915_AIF1_TRI_MASK 0x0004 /* AIF1_TRI */
-#define WM8915_AIF1_TRI_SHIFT 2 /* AIF1_TRI */
-#define WM8915_AIF1_TRI_WIDTH 1 /* AIF1_TRI */
-#define WM8915_AIF1_FMT_MASK 0x0003 /* AIF1_FMT - [1:0] */
-#define WM8915_AIF1_FMT_SHIFT 0 /* AIF1_FMT - [1:0] */
-#define WM8915_AIF1_FMT_WIDTH 2 /* AIF1_FMT - [1:0] */
-
-/*
- * R769 (0x301) - AIF1 BCLK
- */
-#define WM8915_AIF1_BCLK_INV 0x0400 /* AIF1_BCLK_INV */
-#define WM8915_AIF1_BCLK_INV_MASK 0x0400 /* AIF1_BCLK_INV */
-#define WM8915_AIF1_BCLK_INV_SHIFT 10 /* AIF1_BCLK_INV */
-#define WM8915_AIF1_BCLK_INV_WIDTH 1 /* AIF1_BCLK_INV */
-#define WM8915_AIF1_BCLK_FRC 0x0200 /* AIF1_BCLK_FRC */
-#define WM8915_AIF1_BCLK_FRC_MASK 0x0200 /* AIF1_BCLK_FRC */
-#define WM8915_AIF1_BCLK_FRC_SHIFT 9 /* AIF1_BCLK_FRC */
-#define WM8915_AIF1_BCLK_FRC_WIDTH 1 /* AIF1_BCLK_FRC */
-#define WM8915_AIF1_BCLK_MSTR 0x0100 /* AIF1_BCLK_MSTR */
-#define WM8915_AIF1_BCLK_MSTR_MASK 0x0100 /* AIF1_BCLK_MSTR */
-#define WM8915_AIF1_BCLK_MSTR_SHIFT 8 /* AIF1_BCLK_MSTR */
-#define WM8915_AIF1_BCLK_MSTR_WIDTH 1 /* AIF1_BCLK_MSTR */
-#define WM8915_AIF1_BCLK_DIV_MASK 0x000F /* AIF1_BCLK_DIV - [3:0] */
-#define WM8915_AIF1_BCLK_DIV_SHIFT 0 /* AIF1_BCLK_DIV - [3:0] */
-#define WM8915_AIF1_BCLK_DIV_WIDTH 4 /* AIF1_BCLK_DIV - [3:0] */
-
-/*
- * R770 (0x302) - AIF1 TX LRCLK(1)
- */
-#define WM8915_AIF1TX_RATE_MASK 0x07FF /* AIF1TX_RATE - [10:0] */
-#define WM8915_AIF1TX_RATE_SHIFT 0 /* AIF1TX_RATE - [10:0] */
-#define WM8915_AIF1TX_RATE_WIDTH 11 /* AIF1TX_RATE - [10:0] */
-
-/*
- * R771 (0x303) - AIF1 TX LRCLK(2)
- */
-#define WM8915_AIF1TX_LRCLK_MODE 0x0008 /* AIF1TX_LRCLK_MODE */
-#define WM8915_AIF1TX_LRCLK_MODE_MASK 0x0008 /* AIF1TX_LRCLK_MODE */
-#define WM8915_AIF1TX_LRCLK_MODE_SHIFT 3 /* AIF1TX_LRCLK_MODE */
-#define WM8915_AIF1TX_LRCLK_MODE_WIDTH 1 /* AIF1TX_LRCLK_MODE */
-#define WM8915_AIF1TX_LRCLK_INV 0x0004 /* AIF1TX_LRCLK_INV */
-#define WM8915_AIF1TX_LRCLK_INV_MASK 0x0004 /* AIF1TX_LRCLK_INV */
-#define WM8915_AIF1TX_LRCLK_INV_SHIFT 2 /* AIF1TX_LRCLK_INV */
-#define WM8915_AIF1TX_LRCLK_INV_WIDTH 1 /* AIF1TX_LRCLK_INV */
-#define WM8915_AIF1TX_LRCLK_FRC 0x0002 /* AIF1TX_LRCLK_FRC */
-#define WM8915_AIF1TX_LRCLK_FRC_MASK 0x0002 /* AIF1TX_LRCLK_FRC */
-#define WM8915_AIF1TX_LRCLK_FRC_SHIFT 1 /* AIF1TX_LRCLK_FRC */
-#define WM8915_AIF1TX_LRCLK_FRC_WIDTH 1 /* AIF1TX_LRCLK_FRC */
-#define WM8915_AIF1TX_LRCLK_MSTR 0x0001 /* AIF1TX_LRCLK_MSTR */
-#define WM8915_AIF1TX_LRCLK_MSTR_MASK 0x0001 /* AIF1TX_LRCLK_MSTR */
-#define WM8915_AIF1TX_LRCLK_MSTR_SHIFT 0 /* AIF1TX_LRCLK_MSTR */
-#define WM8915_AIF1TX_LRCLK_MSTR_WIDTH 1 /* AIF1TX_LRCLK_MSTR */
-
-/*
- * R772 (0x304) - AIF1 RX LRCLK(1)
- */
-#define WM8915_AIF1RX_RATE_MASK 0x07FF /* AIF1RX_RATE - [10:0] */
-#define WM8915_AIF1RX_RATE_SHIFT 0 /* AIF1RX_RATE - [10:0] */
-#define WM8915_AIF1RX_RATE_WIDTH 11 /* AIF1RX_RATE - [10:0] */
-
-/*
- * R773 (0x305) - AIF1 RX LRCLK(2)
- */
-#define WM8915_AIF1RX_LRCLK_INV 0x0004 /* AIF1RX_LRCLK_INV */
-#define WM8915_AIF1RX_LRCLK_INV_MASK 0x0004 /* AIF1RX_LRCLK_INV */
-#define WM8915_AIF1RX_LRCLK_INV_SHIFT 2 /* AIF1RX_LRCLK_INV */
-#define WM8915_AIF1RX_LRCLK_INV_WIDTH 1 /* AIF1RX_LRCLK_INV */
-#define WM8915_AIF1RX_LRCLK_FRC 0x0002 /* AIF1RX_LRCLK_FRC */
-#define WM8915_AIF1RX_LRCLK_FRC_MASK 0x0002 /* AIF1RX_LRCLK_FRC */
-#define WM8915_AIF1RX_LRCLK_FRC_SHIFT 1 /* AIF1RX_LRCLK_FRC */
-#define WM8915_AIF1RX_LRCLK_FRC_WIDTH 1 /* AIF1RX_LRCLK_FRC */
-#define WM8915_AIF1RX_LRCLK_MSTR 0x0001 /* AIF1RX_LRCLK_MSTR */
-#define WM8915_AIF1RX_LRCLK_MSTR_MASK 0x0001 /* AIF1RX_LRCLK_MSTR */
-#define WM8915_AIF1RX_LRCLK_MSTR_SHIFT 0 /* AIF1RX_LRCLK_MSTR */
-#define WM8915_AIF1RX_LRCLK_MSTR_WIDTH 1 /* AIF1RX_LRCLK_MSTR */
-
-/*
- * R774 (0x306) - AIF1TX Data Configuration (1)
- */
-#define WM8915_AIF1TX_WL_MASK 0xFF00 /* AIF1TX_WL - [15:8] */
-#define WM8915_AIF1TX_WL_SHIFT 8 /* AIF1TX_WL - [15:8] */
-#define WM8915_AIF1TX_WL_WIDTH 8 /* AIF1TX_WL - [15:8] */
-#define WM8915_AIF1TX_SLOT_LEN_MASK 0x00FF /* AIF1TX_SLOT_LEN - [7:0] */
-#define WM8915_AIF1TX_SLOT_LEN_SHIFT 0 /* AIF1TX_SLOT_LEN - [7:0] */
-#define WM8915_AIF1TX_SLOT_LEN_WIDTH 8 /* AIF1TX_SLOT_LEN - [7:0] */
-
-/*
- * R775 (0x307) - AIF1TX Data Configuration (2)
- */
-#define WM8915_AIF1TX_DAT_TRI 0x0001 /* AIF1TX_DAT_TRI */
-#define WM8915_AIF1TX_DAT_TRI_MASK 0x0001 /* AIF1TX_DAT_TRI */
-#define WM8915_AIF1TX_DAT_TRI_SHIFT 0 /* AIF1TX_DAT_TRI */
-#define WM8915_AIF1TX_DAT_TRI_WIDTH 1 /* AIF1TX_DAT_TRI */
-
-/*
- * R776 (0x308) - AIF1RX Data Configuration
- */
-#define WM8915_AIF1RX_WL_MASK 0xFF00 /* AIF1RX_WL - [15:8] */
-#define WM8915_AIF1RX_WL_SHIFT 8 /* AIF1RX_WL - [15:8] */
-#define WM8915_AIF1RX_WL_WIDTH 8 /* AIF1RX_WL - [15:8] */
-#define WM8915_AIF1RX_SLOT_LEN_MASK 0x00FF /* AIF1RX_SLOT_LEN - [7:0] */
-#define WM8915_AIF1RX_SLOT_LEN_SHIFT 0 /* AIF1RX_SLOT_LEN - [7:0] */
-#define WM8915_AIF1RX_SLOT_LEN_WIDTH 8 /* AIF1RX_SLOT_LEN - [7:0] */
-
-/*
- * R777 (0x309) - AIF1TX Channel 0 Configuration
- */
-#define WM8915_AIF1TX_CHAN0_DAT_INV 0x8000 /* AIF1TX_CHAN0_DAT_INV */
-#define WM8915_AIF1TX_CHAN0_DAT_INV_MASK 0x8000 /* AIF1TX_CHAN0_DAT_INV */
-#define WM8915_AIF1TX_CHAN0_DAT_INV_SHIFT 15 /* AIF1TX_CHAN0_DAT_INV */
-#define WM8915_AIF1TX_CHAN0_DAT_INV_WIDTH 1 /* AIF1TX_CHAN0_DAT_INV */
-#define WM8915_AIF1TX_CHAN0_SPACING_MASK 0x7E00 /* AIF1TX_CHAN0_SPACING - [14:9] */
-#define WM8915_AIF1TX_CHAN0_SPACING_SHIFT 9 /* AIF1TX_CHAN0_SPACING - [14:9] */
-#define WM8915_AIF1TX_CHAN0_SPACING_WIDTH 6 /* AIF1TX_CHAN0_SPACING - [14:9] */
-#define WM8915_AIF1TX_CHAN0_SLOTS_MASK 0x01C0 /* AIF1TX_CHAN0_SLOTS - [8:6] */
-#define WM8915_AIF1TX_CHAN0_SLOTS_SHIFT 6 /* AIF1TX_CHAN0_SLOTS - [8:6] */
-#define WM8915_AIF1TX_CHAN0_SLOTS_WIDTH 3 /* AIF1TX_CHAN0_SLOTS - [8:6] */
-#define WM8915_AIF1TX_CHAN0_START_SLOT_MASK 0x003F /* AIF1TX_CHAN0_START_SLOT - [5:0] */
-#define WM8915_AIF1TX_CHAN0_START_SLOT_SHIFT 0 /* AIF1TX_CHAN0_START_SLOT - [5:0] */
-#define WM8915_AIF1TX_CHAN0_START_SLOT_WIDTH 6 /* AIF1TX_CHAN0_START_SLOT - [5:0] */
-
-/*
- * R778 (0x30A) - AIF1TX Channel 1 Configuration
- */
-#define WM8915_AIF1TX_CHAN1_DAT_INV 0x8000 /* AIF1TX_CHAN1_DAT_INV */
-#define WM8915_AIF1TX_CHAN1_DAT_INV_MASK 0x8000 /* AIF1TX_CHAN1_DAT_INV */
-#define WM8915_AIF1TX_CHAN1_DAT_INV_SHIFT 15 /* AIF1TX_CHAN1_DAT_INV */
-#define WM8915_AIF1TX_CHAN1_DAT_INV_WIDTH 1 /* AIF1TX_CHAN1_DAT_INV */
-#define WM8915_AIF1TX_CHAN1_SPACING_MASK 0x7E00 /* AIF1TX_CHAN1_SPACING - [14:9] */
-#define WM8915_AIF1TX_CHAN1_SPACING_SHIFT 9 /* AIF1TX_CHAN1_SPACING - [14:9] */
-#define WM8915_AIF1TX_CHAN1_SPACING_WIDTH 6 /* AIF1TX_CHAN1_SPACING - [14:9] */
-#define WM8915_AIF1TX_CHAN1_SLOTS_MASK 0x01C0 /* AIF1TX_CHAN1_SLOTS - [8:6] */
-#define WM8915_AIF1TX_CHAN1_SLOTS_SHIFT 6 /* AIF1TX_CHAN1_SLOTS - [8:6] */
-#define WM8915_AIF1TX_CHAN1_SLOTS_WIDTH 3 /* AIF1TX_CHAN1_SLOTS - [8:6] */
-#define WM8915_AIF1TX_CHAN1_START_SLOT_MASK 0x003F /* AIF1TX_CHAN1_START_SLOT - [5:0] */
-#define WM8915_AIF1TX_CHAN1_START_SLOT_SHIFT 0 /* AIF1TX_CHAN1_START_SLOT - [5:0] */
-#define WM8915_AIF1TX_CHAN1_START_SLOT_WIDTH 6 /* AIF1TX_CHAN1_START_SLOT - [5:0] */
-
-/*
- * R779 (0x30B) - AIF1TX Channel 2 Configuration
- */
-#define WM8915_AIF1TX_CHAN2_DAT_INV 0x8000 /* AIF1TX_CHAN2_DAT_INV */
-#define WM8915_AIF1TX_CHAN2_DAT_INV_MASK 0x8000 /* AIF1TX_CHAN2_DAT_INV */
-#define WM8915_AIF1TX_CHAN2_DAT_INV_SHIFT 15 /* AIF1TX_CHAN2_DAT_INV */
-#define WM8915_AIF1TX_CHAN2_DAT_INV_WIDTH 1 /* AIF1TX_CHAN2_DAT_INV */
-#define WM8915_AIF1TX_CHAN2_SPACING_MASK 0x7E00 /* AIF1TX_CHAN2_SPACING - [14:9] */
-#define WM8915_AIF1TX_CHAN2_SPACING_SHIFT 9 /* AIF1TX_CHAN2_SPACING - [14:9] */
-#define WM8915_AIF1TX_CHAN2_SPACING_WIDTH 6 /* AIF1TX_CHAN2_SPACING - [14:9] */
-#define WM8915_AIF1TX_CHAN2_SLOTS_MASK 0x01C0 /* AIF1TX_CHAN2_SLOTS - [8:6] */
-#define WM8915_AIF1TX_CHAN2_SLOTS_SHIFT 6 /* AIF1TX_CHAN2_SLOTS - [8:6] */
-#define WM8915_AIF1TX_CHAN2_SLOTS_WIDTH 3 /* AIF1TX_CHAN2_SLOTS - [8:6] */
-#define WM8915_AIF1TX_CHAN2_START_SLOT_MASK 0x003F /* AIF1TX_CHAN2_START_SLOT - [5:0] */
-#define WM8915_AIF1TX_CHAN2_START_SLOT_SHIFT 0 /* AIF1TX_CHAN2_START_SLOT - [5:0] */
-#define WM8915_AIF1TX_CHAN2_START_SLOT_WIDTH 6 /* AIF1TX_CHAN2_START_SLOT - [5:0] */
-
-/*
- * R780 (0x30C) - AIF1TX Channel 3 Configuration
- */
-#define WM8915_AIF1TX_CHAN3_DAT_INV 0x8000 /* AIF1TX_CHAN3_DAT_INV */
-#define WM8915_AIF1TX_CHAN3_DAT_INV_MASK 0x8000 /* AIF1TX_CHAN3_DAT_INV */
-#define WM8915_AIF1TX_CHAN3_DAT_INV_SHIFT 15 /* AIF1TX_CHAN3_DAT_INV */
-#define WM8915_AIF1TX_CHAN3_DAT_INV_WIDTH 1 /* AIF1TX_CHAN3_DAT_INV */
-#define WM8915_AIF1TX_CHAN3_SPACING_MASK 0x7E00 /* AIF1TX_CHAN3_SPACING - [14:9] */
-#define WM8915_AIF1TX_CHAN3_SPACING_SHIFT 9 /* AIF1TX_CHAN3_SPACING - [14:9] */
-#define WM8915_AIF1TX_CHAN3_SPACING_WIDTH 6 /* AIF1TX_CHAN3_SPACING - [14:9] */
-#define WM8915_AIF1TX_CHAN3_SLOTS_MASK 0x01C0 /* AIF1TX_CHAN3_SLOTS - [8:6] */
-#define WM8915_AIF1TX_CHAN3_SLOTS_SHIFT 6 /* AIF1TX_CHAN3_SLOTS - [8:6] */
-#define WM8915_AIF1TX_CHAN3_SLOTS_WIDTH 3 /* AIF1TX_CHAN3_SLOTS - [8:6] */
-#define WM8915_AIF1TX_CHAN3_START_SLOT_MASK 0x003F /* AIF1TX_CHAN3_START_SLOT - [5:0] */
-#define WM8915_AIF1TX_CHAN3_START_SLOT_SHIFT 0 /* AIF1TX_CHAN3_START_SLOT - [5:0] */
-#define WM8915_AIF1TX_CHAN3_START_SLOT_WIDTH 6 /* AIF1TX_CHAN3_START_SLOT - [5:0] */
-
-/*
- * R781 (0x30D) - AIF1TX Channel 4 Configuration
- */
-#define WM8915_AIF1TX_CHAN4_DAT_INV 0x8000 /* AIF1TX_CHAN4_DAT_INV */
-#define WM8915_AIF1TX_CHAN4_DAT_INV_MASK 0x8000 /* AIF1TX_CHAN4_DAT_INV */
-#define WM8915_AIF1TX_CHAN4_DAT_INV_SHIFT 15 /* AIF1TX_CHAN4_DAT_INV */
-#define WM8915_AIF1TX_CHAN4_DAT_INV_WIDTH 1 /* AIF1TX_CHAN4_DAT_INV */
-#define WM8915_AIF1TX_CHAN4_SPACING_MASK 0x7E00 /* AIF1TX_CHAN4_SPACING - [14:9] */
-#define WM8915_AIF1TX_CHAN4_SPACING_SHIFT 9 /* AIF1TX_CHAN4_SPACING - [14:9] */
-#define WM8915_AIF1TX_CHAN4_SPACING_WIDTH 6 /* AIF1TX_CHAN4_SPACING - [14:9] */
-#define WM8915_AIF1TX_CHAN4_SLOTS_MASK 0x01C0 /* AIF1TX_CHAN4_SLOTS - [8:6] */
-#define WM8915_AIF1TX_CHAN4_SLOTS_SHIFT 6 /* AIF1TX_CHAN4_SLOTS - [8:6] */
-#define WM8915_AIF1TX_CHAN4_SLOTS_WIDTH 3 /* AIF1TX_CHAN4_SLOTS - [8:6] */
-#define WM8915_AIF1TX_CHAN4_START_SLOT_MASK 0x003F /* AIF1TX_CHAN4_START_SLOT - [5:0] */
-#define WM8915_AIF1TX_CHAN4_START_SLOT_SHIFT 0 /* AIF1TX_CHAN4_START_SLOT - [5:0] */
-#define WM8915_AIF1TX_CHAN4_START_SLOT_WIDTH 6 /* AIF1TX_CHAN4_START_SLOT - [5:0] */
-
-/*
- * R782 (0x30E) - AIF1TX Channel 5 Configuration
- */
-#define WM8915_AIF1TX_CHAN5_DAT_INV 0x8000 /* AIF1TX_CHAN5_DAT_INV */
-#define WM8915_AIF1TX_CHAN5_DAT_INV_MASK 0x8000 /* AIF1TX_CHAN5_DAT_INV */
-#define WM8915_AIF1TX_CHAN5_DAT_INV_SHIFT 15 /* AIF1TX_CHAN5_DAT_INV */
-#define WM8915_AIF1TX_CHAN5_DAT_INV_WIDTH 1 /* AIF1TX_CHAN5_DAT_INV */
-#define WM8915_AIF1TX_CHAN5_SPACING_MASK 0x7E00 /* AIF1TX_CHAN5_SPACING - [14:9] */
-#define WM8915_AIF1TX_CHAN5_SPACING_SHIFT 9 /* AIF1TX_CHAN5_SPACING - [14:9] */
-#define WM8915_AIF1TX_CHAN5_SPACING_WIDTH 6 /* AIF1TX_CHAN5_SPACING - [14:9] */
-#define WM8915_AIF1TX_CHAN5_SLOTS_MASK 0x01C0 /* AIF1TX_CHAN5_SLOTS - [8:6] */
-#define WM8915_AIF1TX_CHAN5_SLOTS_SHIFT 6 /* AIF1TX_CHAN5_SLOTS - [8:6] */
-#define WM8915_AIF1TX_CHAN5_SLOTS_WIDTH 3 /* AIF1TX_CHAN5_SLOTS - [8:6] */
-#define WM8915_AIF1TX_CHAN5_START_SLOT_MASK 0x003F /* AIF1TX_CHAN5_START_SLOT - [5:0] */
-#define WM8915_AIF1TX_CHAN5_START_SLOT_SHIFT 0 /* AIF1TX_CHAN5_START_SLOT - [5:0] */
-#define WM8915_AIF1TX_CHAN5_START_SLOT_WIDTH 6 /* AIF1TX_CHAN5_START_SLOT - [5:0] */
-
-/*
- * R783 (0x30F) - AIF1RX Channel 0 Configuration
- */
-#define WM8915_AIF1RX_CHAN0_DAT_INV 0x8000 /* AIF1RX_CHAN0_DAT_INV */
-#define WM8915_AIF1RX_CHAN0_DAT_INV_MASK 0x8000 /* AIF1RX_CHAN0_DAT_INV */
-#define WM8915_AIF1RX_CHAN0_DAT_INV_SHIFT 15 /* AIF1RX_CHAN0_DAT_INV */
-#define WM8915_AIF1RX_CHAN0_DAT_INV_WIDTH 1 /* AIF1RX_CHAN0_DAT_INV */
-#define WM8915_AIF1RX_CHAN0_SPACING_MASK 0x7E00 /* AIF1RX_CHAN0_SPACING - [14:9] */
-#define WM8915_AIF1RX_CHAN0_SPACING_SHIFT 9 /* AIF1RX_CHAN0_SPACING - [14:9] */
-#define WM8915_AIF1RX_CHAN0_SPACING_WIDTH 6 /* AIF1RX_CHAN0_SPACING - [14:9] */
-#define WM8915_AIF1RX_CHAN0_SLOTS_MASK 0x01C0 /* AIF1RX_CHAN0_SLOTS - [8:6] */
-#define WM8915_AIF1RX_CHAN0_SLOTS_SHIFT 6 /* AIF1RX_CHAN0_SLOTS - [8:6] */
-#define WM8915_AIF1RX_CHAN0_SLOTS_WIDTH 3 /* AIF1RX_CHAN0_SLOTS - [8:6] */
-#define WM8915_AIF1RX_CHAN0_START_SLOT_MASK 0x003F /* AIF1RX_CHAN0_START_SLOT - [5:0] */
-#define WM8915_AIF1RX_CHAN0_START_SLOT_SHIFT 0 /* AIF1RX_CHAN0_START_SLOT - [5:0] */
-#define WM8915_AIF1RX_CHAN0_START_SLOT_WIDTH 6 /* AIF1RX_CHAN0_START_SLOT - [5:0] */
-
-/*
- * R784 (0x310) - AIF1RX Channel 1 Configuration
- */
-#define WM8915_AIF1RX_CHAN1_DAT_INV 0x8000 /* AIF1RX_CHAN1_DAT_INV */
-#define WM8915_AIF1RX_CHAN1_DAT_INV_MASK 0x8000 /* AIF1RX_CHAN1_DAT_INV */
-#define WM8915_AIF1RX_CHAN1_DAT_INV_SHIFT 15 /* AIF1RX_CHAN1_DAT_INV */
-#define WM8915_AIF1RX_CHAN1_DAT_INV_WIDTH 1 /* AIF1RX_CHAN1_DAT_INV */
-#define WM8915_AIF1RX_CHAN1_SPACING_MASK 0x7E00 /* AIF1RX_CHAN1_SPACING - [14:9] */
-#define WM8915_AIF1RX_CHAN1_SPACING_SHIFT 9 /* AIF1RX_CHAN1_SPACING - [14:9] */
-#define WM8915_AIF1RX_CHAN1_SPACING_WIDTH 6 /* AIF1RX_CHAN1_SPACING - [14:9] */
-#define WM8915_AIF1RX_CHAN1_SLOTS_MASK 0x01C0 /* AIF1RX_CHAN1_SLOTS - [8:6] */
-#define WM8915_AIF1RX_CHAN1_SLOTS_SHIFT 6 /* AIF1RX_CHAN1_SLOTS - [8:6] */
-#define WM8915_AIF1RX_CHAN1_SLOTS_WIDTH 3 /* AIF1RX_CHAN1_SLOTS - [8:6] */
-#define WM8915_AIF1RX_CHAN1_START_SLOT_MASK 0x003F /* AIF1RX_CHAN1_START_SLOT - [5:0] */
-#define WM8915_AIF1RX_CHAN1_START_SLOT_SHIFT 0 /* AIF1RX_CHAN1_START_SLOT - [5:0] */
-#define WM8915_AIF1RX_CHAN1_START_SLOT_WIDTH 6 /* AIF1RX_CHAN1_START_SLOT - [5:0] */
-
-/*
- * R785 (0x311) - AIF1RX Channel 2 Configuration
- */
-#define WM8915_AIF1RX_CHAN2_DAT_INV 0x8000 /* AIF1RX_CHAN2_DAT_INV */
-#define WM8915_AIF1RX_CHAN2_DAT_INV_MASK 0x8000 /* AIF1RX_CHAN2_DAT_INV */
-#define WM8915_AIF1RX_CHAN2_DAT_INV_SHIFT 15 /* AIF1RX_CHAN2_DAT_INV */
-#define WM8915_AIF1RX_CHAN2_DAT_INV_WIDTH 1 /* AIF1RX_CHAN2_DAT_INV */
-#define WM8915_AIF1RX_CHAN2_SPACING_MASK 0x7E00 /* AIF1RX_CHAN2_SPACING - [14:9] */
-#define WM8915_AIF1RX_CHAN2_SPACING_SHIFT 9 /* AIF1RX_CHAN2_SPACING - [14:9] */
-#define WM8915_AIF1RX_CHAN2_SPACING_WIDTH 6 /* AIF1RX_CHAN2_SPACING - [14:9] */
-#define WM8915_AIF1RX_CHAN2_SLOTS_MASK 0x01C0 /* AIF1RX_CHAN2_SLOTS - [8:6] */
-#define WM8915_AIF1RX_CHAN2_SLOTS_SHIFT 6 /* AIF1RX_CHAN2_SLOTS - [8:6] */
-#define WM8915_AIF1RX_CHAN2_SLOTS_WIDTH 3 /* AIF1RX_CHAN2_SLOTS - [8:6] */
-#define WM8915_AIF1RX_CHAN2_START_SLOT_MASK 0x003F /* AIF1RX_CHAN2_START_SLOT - [5:0] */
-#define WM8915_AIF1RX_CHAN2_START_SLOT_SHIFT 0 /* AIF1RX_CHAN2_START_SLOT - [5:0] */
-#define WM8915_AIF1RX_CHAN2_START_SLOT_WIDTH 6 /* AIF1RX_CHAN2_START_SLOT - [5:0] */
-
-/*
- * R786 (0x312) - AIF1RX Channel 3 Configuration
- */
-#define WM8915_AIF1RX_CHAN3_DAT_INV 0x8000 /* AIF1RX_CHAN3_DAT_INV */
-#define WM8915_AIF1RX_CHAN3_DAT_INV_MASK 0x8000 /* AIF1RX_CHAN3_DAT_INV */
-#define WM8915_AIF1RX_CHAN3_DAT_INV_SHIFT 15 /* AIF1RX_CHAN3_DAT_INV */
-#define WM8915_AIF1RX_CHAN3_DAT_INV_WIDTH 1 /* AIF1RX_CHAN3_DAT_INV */
-#define WM8915_AIF1RX_CHAN3_SPACING_MASK 0x7E00 /* AIF1RX_CHAN3_SPACING - [14:9] */
-#define WM8915_AIF1RX_CHAN3_SPACING_SHIFT 9 /* AIF1RX_CHAN3_SPACING - [14:9] */
-#define WM8915_AIF1RX_CHAN3_SPACING_WIDTH 6 /* AIF1RX_CHAN3_SPACING - [14:9] */
-#define WM8915_AIF1RX_CHAN3_SLOTS_MASK 0x01C0 /* AIF1RX_CHAN3_SLOTS - [8:6] */
-#define WM8915_AIF1RX_CHAN3_SLOTS_SHIFT 6 /* AIF1RX_CHAN3_SLOTS - [8:6] */
-#define WM8915_AIF1RX_CHAN3_SLOTS_WIDTH 3 /* AIF1RX_CHAN3_SLOTS - [8:6] */
-#define WM8915_AIF1RX_CHAN3_START_SLOT_MASK 0x003F /* AIF1RX_CHAN3_START_SLOT - [5:0] */
-#define WM8915_AIF1RX_CHAN3_START_SLOT_SHIFT 0 /* AIF1RX_CHAN3_START_SLOT - [5:0] */
-#define WM8915_AIF1RX_CHAN3_START_SLOT_WIDTH 6 /* AIF1RX_CHAN3_START_SLOT - [5:0] */
-
-/*
- * R787 (0x313) - AIF1RX Channel 4 Configuration
- */
-#define WM8915_AIF1RX_CHAN4_DAT_INV 0x8000 /* AIF1RX_CHAN4_DAT_INV */
-#define WM8915_AIF1RX_CHAN4_DAT_INV_MASK 0x8000 /* AIF1RX_CHAN4_DAT_INV */
-#define WM8915_AIF1RX_CHAN4_DAT_INV_SHIFT 15 /* AIF1RX_CHAN4_DAT_INV */
-#define WM8915_AIF1RX_CHAN4_DAT_INV_WIDTH 1 /* AIF1RX_CHAN4_DAT_INV */
-#define WM8915_AIF1RX_CHAN4_SPACING_MASK 0x7E00 /* AIF1RX_CHAN4_SPACING - [14:9] */
-#define WM8915_AIF1RX_CHAN4_SPACING_SHIFT 9 /* AIF1RX_CHAN4_SPACING - [14:9] */
-#define WM8915_AIF1RX_CHAN4_SPACING_WIDTH 6 /* AIF1RX_CHAN4_SPACING - [14:9] */
-#define WM8915_AIF1RX_CHAN4_SLOTS_MASK 0x01C0 /* AIF1RX_CHAN4_SLOTS - [8:6] */
-#define WM8915_AIF1RX_CHAN4_SLOTS_SHIFT 6 /* AIF1RX_CHAN4_SLOTS - [8:6] */
-#define WM8915_AIF1RX_CHAN4_SLOTS_WIDTH 3 /* AIF1RX_CHAN4_SLOTS - [8:6] */
-#define WM8915_AIF1RX_CHAN4_START_SLOT_MASK 0x003F /* AIF1RX_CHAN4_START_SLOT - [5:0] */
-#define WM8915_AIF1RX_CHAN4_START_SLOT_SHIFT 0 /* AIF1RX_CHAN4_START_SLOT - [5:0] */
-#define WM8915_AIF1RX_CHAN4_START_SLOT_WIDTH 6 /* AIF1RX_CHAN4_START_SLOT - [5:0] */
-
-/*
- * R788 (0x314) - AIF1RX Channel 5 Configuration
- */
-#define WM8915_AIF1RX_CHAN5_DAT_INV 0x8000 /* AIF1RX_CHAN5_DAT_INV */
-#define WM8915_AIF1RX_CHAN5_DAT_INV_MASK 0x8000 /* AIF1RX_CHAN5_DAT_INV */
-#define WM8915_AIF1RX_CHAN5_DAT_INV_SHIFT 15 /* AIF1RX_CHAN5_DAT_INV */
-#define WM8915_AIF1RX_CHAN5_DAT_INV_WIDTH 1 /* AIF1RX_CHAN5_DAT_INV */
-#define WM8915_AIF1RX_CHAN5_SPACING_MASK 0x7E00 /* AIF1RX_CHAN5_SPACING - [14:9] */
-#define WM8915_AIF1RX_CHAN5_SPACING_SHIFT 9 /* AIF1RX_CHAN5_SPACING - [14:9] */
-#define WM8915_AIF1RX_CHAN5_SPACING_WIDTH 6 /* AIF1RX_CHAN5_SPACING - [14:9] */
-#define WM8915_AIF1RX_CHAN5_SLOTS_MASK 0x01C0 /* AIF1RX_CHAN5_SLOTS - [8:6] */
-#define WM8915_AIF1RX_CHAN5_SLOTS_SHIFT 6 /* AIF1RX_CHAN5_SLOTS - [8:6] */
-#define WM8915_AIF1RX_CHAN5_SLOTS_WIDTH 3 /* AIF1RX_CHAN5_SLOTS - [8:6] */
-#define WM8915_AIF1RX_CHAN5_START_SLOT_MASK 0x003F /* AIF1RX_CHAN5_START_SLOT - [5:0] */
-#define WM8915_AIF1RX_CHAN5_START_SLOT_SHIFT 0 /* AIF1RX_CHAN5_START_SLOT - [5:0] */
-#define WM8915_AIF1RX_CHAN5_START_SLOT_WIDTH 6 /* AIF1RX_CHAN5_START_SLOT - [5:0] */
-
-/*
- * R789 (0x315) - AIF1RX Mono Configuration
- */
-#define WM8915_AIF1RX_CHAN4_MONO_MODE 0x0004 /* AIF1RX_CHAN4_MONO_MODE */
-#define WM8915_AIF1RX_CHAN4_MONO_MODE_MASK 0x0004 /* AIF1RX_CHAN4_MONO_MODE */
-#define WM8915_AIF1RX_CHAN4_MONO_MODE_SHIFT 2 /* AIF1RX_CHAN4_MONO_MODE */
-#define WM8915_AIF1RX_CHAN4_MONO_MODE_WIDTH 1 /* AIF1RX_CHAN4_MONO_MODE */
-#define WM8915_AIF1RX_CHAN2_MONO_MODE 0x0002 /* AIF1RX_CHAN2_MONO_MODE */
-#define WM8915_AIF1RX_CHAN2_MONO_MODE_MASK 0x0002 /* AIF1RX_CHAN2_MONO_MODE */
-#define WM8915_AIF1RX_CHAN2_MONO_MODE_SHIFT 1 /* AIF1RX_CHAN2_MONO_MODE */
-#define WM8915_AIF1RX_CHAN2_MONO_MODE_WIDTH 1 /* AIF1RX_CHAN2_MONO_MODE */
-#define WM8915_AIF1RX_CHAN0_MONO_MODE 0x0001 /* AIF1RX_CHAN0_MONO_MODE */
-#define WM8915_AIF1RX_CHAN0_MONO_MODE_MASK 0x0001 /* AIF1RX_CHAN0_MONO_MODE */
-#define WM8915_AIF1RX_CHAN0_MONO_MODE_SHIFT 0 /* AIF1RX_CHAN0_MONO_MODE */
-#define WM8915_AIF1RX_CHAN0_MONO_MODE_WIDTH 1 /* AIF1RX_CHAN0_MONO_MODE */
-
-/*
- * R794 (0x31A) - AIF1TX Test
- */
-#define WM8915_AIF1TX45_DITHER_ENA 0x0004 /* AIF1TX45_DITHER_ENA */
-#define WM8915_AIF1TX45_DITHER_ENA_MASK 0x0004 /* AIF1TX45_DITHER_ENA */
-#define WM8915_AIF1TX45_DITHER_ENA_SHIFT 2 /* AIF1TX45_DITHER_ENA */
-#define WM8915_AIF1TX45_DITHER_ENA_WIDTH 1 /* AIF1TX45_DITHER_ENA */
-#define WM8915_AIF1TX23_DITHER_ENA 0x0002 /* AIF1TX23_DITHER_ENA */
-#define WM8915_AIF1TX23_DITHER_ENA_MASK 0x0002 /* AIF1TX23_DITHER_ENA */
-#define WM8915_AIF1TX23_DITHER_ENA_SHIFT 1 /* AIF1TX23_DITHER_ENA */
-#define WM8915_AIF1TX23_DITHER_ENA_WIDTH 1 /* AIF1TX23_DITHER_ENA */
-#define WM8915_AIF1TX01_DITHER_ENA 0x0001 /* AIF1TX01_DITHER_ENA */
-#define WM8915_AIF1TX01_DITHER_ENA_MASK 0x0001 /* AIF1TX01_DITHER_ENA */
-#define WM8915_AIF1TX01_DITHER_ENA_SHIFT 0 /* AIF1TX01_DITHER_ENA */
-#define WM8915_AIF1TX01_DITHER_ENA_WIDTH 1 /* AIF1TX01_DITHER_ENA */
-
-/*
- * R800 (0x320) - AIF2 Control
- */
-#define WM8915_AIF2_TRI 0x0004 /* AIF2_TRI */
-#define WM8915_AIF2_TRI_MASK 0x0004 /* AIF2_TRI */
-#define WM8915_AIF2_TRI_SHIFT 2 /* AIF2_TRI */
-#define WM8915_AIF2_TRI_WIDTH 1 /* AIF2_TRI */
-#define WM8915_AIF2_FMT_MASK 0x0003 /* AIF2_FMT - [1:0] */
-#define WM8915_AIF2_FMT_SHIFT 0 /* AIF2_FMT - [1:0] */
-#define WM8915_AIF2_FMT_WIDTH 2 /* AIF2_FMT - [1:0] */
-
-/*
- * R801 (0x321) - AIF2 BCLK
- */
-#define WM8915_AIF2_BCLK_INV 0x0400 /* AIF2_BCLK_INV */
-#define WM8915_AIF2_BCLK_INV_MASK 0x0400 /* AIF2_BCLK_INV */
-#define WM8915_AIF2_BCLK_INV_SHIFT 10 /* AIF2_BCLK_INV */
-#define WM8915_AIF2_BCLK_INV_WIDTH 1 /* AIF2_BCLK_INV */
-#define WM8915_AIF2_BCLK_FRC 0x0200 /* AIF2_BCLK_FRC */
-#define WM8915_AIF2_BCLK_FRC_MASK 0x0200 /* AIF2_BCLK_FRC */
-#define WM8915_AIF2_BCLK_FRC_SHIFT 9 /* AIF2_BCLK_FRC */
-#define WM8915_AIF2_BCLK_FRC_WIDTH 1 /* AIF2_BCLK_FRC */
-#define WM8915_AIF2_BCLK_MSTR 0x0100 /* AIF2_BCLK_MSTR */
-#define WM8915_AIF2_BCLK_MSTR_MASK 0x0100 /* AIF2_BCLK_MSTR */
-#define WM8915_AIF2_BCLK_MSTR_SHIFT 8 /* AIF2_BCLK_MSTR */
-#define WM8915_AIF2_BCLK_MSTR_WIDTH 1 /* AIF2_BCLK_MSTR */
-#define WM8915_AIF2_BCLK_DIV_MASK 0x000F /* AIF2_BCLK_DIV - [3:0] */
-#define WM8915_AIF2_BCLK_DIV_SHIFT 0 /* AIF2_BCLK_DIV - [3:0] */
-#define WM8915_AIF2_BCLK_DIV_WIDTH 4 /* AIF2_BCLK_DIV - [3:0] */
-
-/*
- * R802 (0x322) - AIF2 TX LRCLK(1)
- */
-#define WM8915_AIF2TX_RATE_MASK 0x07FF /* AIF2TX_RATE - [10:0] */
-#define WM8915_AIF2TX_RATE_SHIFT 0 /* AIF2TX_RATE - [10:0] */
-#define WM8915_AIF2TX_RATE_WIDTH 11 /* AIF2TX_RATE - [10:0] */
-
-/*
- * R803 (0x323) - AIF2 TX LRCLK(2)
- */
-#define WM8915_AIF2TX_LRCLK_MODE 0x0008 /* AIF2TX_LRCLK_MODE */
-#define WM8915_AIF2TX_LRCLK_MODE_MASK 0x0008 /* AIF2TX_LRCLK_MODE */
-#define WM8915_AIF2TX_LRCLK_MODE_SHIFT 3 /* AIF2TX_LRCLK_MODE */
-#define WM8915_AIF2TX_LRCLK_MODE_WIDTH 1 /* AIF2TX_LRCLK_MODE */
-#define WM8915_AIF2TX_LRCLK_INV 0x0004 /* AIF2TX_LRCLK_INV */
-#define WM8915_AIF2TX_LRCLK_INV_MASK 0x0004 /* AIF2TX_LRCLK_INV */
-#define WM8915_AIF2TX_LRCLK_INV_SHIFT 2 /* AIF2TX_LRCLK_INV */
-#define WM8915_AIF2TX_LRCLK_INV_WIDTH 1 /* AIF2TX_LRCLK_INV */
-#define WM8915_AIF2TX_LRCLK_FRC 0x0002 /* AIF2TX_LRCLK_FRC */
-#define WM8915_AIF2TX_LRCLK_FRC_MASK 0x0002 /* AIF2TX_LRCLK_FRC */
-#define WM8915_AIF2TX_LRCLK_FRC_SHIFT 1 /* AIF2TX_LRCLK_FRC */
-#define WM8915_AIF2TX_LRCLK_FRC_WIDTH 1 /* AIF2TX_LRCLK_FRC */
-#define WM8915_AIF2TX_LRCLK_MSTR 0x0001 /* AIF2TX_LRCLK_MSTR */
-#define WM8915_AIF2TX_LRCLK_MSTR_MASK 0x0001 /* AIF2TX_LRCLK_MSTR */
-#define WM8915_AIF2TX_LRCLK_MSTR_SHIFT 0 /* AIF2TX_LRCLK_MSTR */
-#define WM8915_AIF2TX_LRCLK_MSTR_WIDTH 1 /* AIF2TX_LRCLK_MSTR */
-
-/*
- * R804 (0x324) - AIF2 RX LRCLK(1)
- */
-#define WM8915_AIF2RX_RATE_MASK 0x07FF /* AIF2RX_RATE - [10:0] */
-#define WM8915_AIF2RX_RATE_SHIFT 0 /* AIF2RX_RATE - [10:0] */
-#define WM8915_AIF2RX_RATE_WIDTH 11 /* AIF2RX_RATE - [10:0] */
-
-/*
- * R805 (0x325) - AIF2 RX LRCLK(2)
- */
-#define WM8915_AIF2RX_LRCLK_INV 0x0004 /* AIF2RX_LRCLK_INV */
-#define WM8915_AIF2RX_LRCLK_INV_MASK 0x0004 /* AIF2RX_LRCLK_INV */
-#define WM8915_AIF2RX_LRCLK_INV_SHIFT 2 /* AIF2RX_LRCLK_INV */
-#define WM8915_AIF2RX_LRCLK_INV_WIDTH 1 /* AIF2RX_LRCLK_INV */
-#define WM8915_AIF2RX_LRCLK_FRC 0x0002 /* AIF2RX_LRCLK_FRC */
-#define WM8915_AIF2RX_LRCLK_FRC_MASK 0x0002 /* AIF2RX_LRCLK_FRC */
-#define WM8915_AIF2RX_LRCLK_FRC_SHIFT 1 /* AIF2RX_LRCLK_FRC */
-#define WM8915_AIF2RX_LRCLK_FRC_WIDTH 1 /* AIF2RX_LRCLK_FRC */
-#define WM8915_AIF2RX_LRCLK_MSTR 0x0001 /* AIF2RX_LRCLK_MSTR */
-#define WM8915_AIF2RX_LRCLK_MSTR_MASK 0x0001 /* AIF2RX_LRCLK_MSTR */
-#define WM8915_AIF2RX_LRCLK_MSTR_SHIFT 0 /* AIF2RX_LRCLK_MSTR */
-#define WM8915_AIF2RX_LRCLK_MSTR_WIDTH 1 /* AIF2RX_LRCLK_MSTR */
-
-/*
- * R806 (0x326) - AIF2TX Data Configuration (1)
- */
-#define WM8915_AIF2TX_WL_MASK 0xFF00 /* AIF2TX_WL - [15:8] */
-#define WM8915_AIF2TX_WL_SHIFT 8 /* AIF2TX_WL - [15:8] */
-#define WM8915_AIF2TX_WL_WIDTH 8 /* AIF2TX_WL - [15:8] */
-#define WM8915_AIF2TX_SLOT_LEN_MASK 0x00FF /* AIF2TX_SLOT_LEN - [7:0] */
-#define WM8915_AIF2TX_SLOT_LEN_SHIFT 0 /* AIF2TX_SLOT_LEN - [7:0] */
-#define WM8915_AIF2TX_SLOT_LEN_WIDTH 8 /* AIF2TX_SLOT_LEN - [7:0] */
-
-/*
- * R807 (0x327) - AIF2TX Data Configuration (2)
- */
-#define WM8915_AIF2TX_DAT_TRI 0x0001 /* AIF2TX_DAT_TRI */
-#define WM8915_AIF2TX_DAT_TRI_MASK 0x0001 /* AIF2TX_DAT_TRI */
-#define WM8915_AIF2TX_DAT_TRI_SHIFT 0 /* AIF2TX_DAT_TRI */
-#define WM8915_AIF2TX_DAT_TRI_WIDTH 1 /* AIF2TX_DAT_TRI */
-
-/*
- * R808 (0x328) - AIF2RX Data Configuration
- */
-#define WM8915_AIF2RX_WL_MASK 0xFF00 /* AIF2RX_WL - [15:8] */
-#define WM8915_AIF2RX_WL_SHIFT 8 /* AIF2RX_WL - [15:8] */
-#define WM8915_AIF2RX_WL_WIDTH 8 /* AIF2RX_WL - [15:8] */
-#define WM8915_AIF2RX_SLOT_LEN_MASK 0x00FF /* AIF2RX_SLOT_LEN - [7:0] */
-#define WM8915_AIF2RX_SLOT_LEN_SHIFT 0 /* AIF2RX_SLOT_LEN - [7:0] */
-#define WM8915_AIF2RX_SLOT_LEN_WIDTH 8 /* AIF2RX_SLOT_LEN - [7:0] */
-
-/*
- * R809 (0x329) - AIF2TX Channel 0 Configuration
- */
-#define WM8915_AIF2TX_CHAN0_DAT_INV 0x8000 /* AIF2TX_CHAN0_DAT_INV */
-#define WM8915_AIF2TX_CHAN0_DAT_INV_MASK 0x8000 /* AIF2TX_CHAN0_DAT_INV */
-#define WM8915_AIF2TX_CHAN0_DAT_INV_SHIFT 15 /* AIF2TX_CHAN0_DAT_INV */
-#define WM8915_AIF2TX_CHAN0_DAT_INV_WIDTH 1 /* AIF2TX_CHAN0_DAT_INV */
-#define WM8915_AIF2TX_CHAN0_SPACING_MASK 0x7E00 /* AIF2TX_CHAN0_SPACING - [14:9] */
-#define WM8915_AIF2TX_CHAN0_SPACING_SHIFT 9 /* AIF2TX_CHAN0_SPACING - [14:9] */
-#define WM8915_AIF2TX_CHAN0_SPACING_WIDTH 6 /* AIF2TX_CHAN0_SPACING - [14:9] */
-#define WM8915_AIF2TX_CHAN0_SLOTS_MASK 0x01C0 /* AIF2TX_CHAN0_SLOTS - [8:6] */
-#define WM8915_AIF2TX_CHAN0_SLOTS_SHIFT 6 /* AIF2TX_CHAN0_SLOTS - [8:6] */
-#define WM8915_AIF2TX_CHAN0_SLOTS_WIDTH 3 /* AIF2TX_CHAN0_SLOTS - [8:6] */
-#define WM8915_AIF2TX_CHAN0_START_SLOT_MASK 0x003F /* AIF2TX_CHAN0_START_SLOT - [5:0] */
-#define WM8915_AIF2TX_CHAN0_START_SLOT_SHIFT 0 /* AIF2TX_CHAN0_START_SLOT - [5:0] */
-#define WM8915_AIF2TX_CHAN0_START_SLOT_WIDTH 6 /* AIF2TX_CHAN0_START_SLOT - [5:0] */
-
-/*
- * R810 (0x32A) - AIF2TX Channel 1 Configuration
- */
-#define WM8915_AIF2TX_CHAN1_DAT_INV 0x8000 /* AIF2TX_CHAN1_DAT_INV */
-#define WM8915_AIF2TX_CHAN1_DAT_INV_MASK 0x8000 /* AIF2TX_CHAN1_DAT_INV */
-#define WM8915_AIF2TX_CHAN1_DAT_INV_SHIFT 15 /* AIF2TX_CHAN1_DAT_INV */
-#define WM8915_AIF2TX_CHAN1_DAT_INV_WIDTH 1 /* AIF2TX_CHAN1_DAT_INV */
-#define WM8915_AIF2TX_CHAN1_SPACING_MASK 0x7E00 /* AIF2TX_CHAN1_SPACING - [14:9] */
-#define WM8915_AIF2TX_CHAN1_SPACING_SHIFT 9 /* AIF2TX_CHAN1_SPACING - [14:9] */
-#define WM8915_AIF2TX_CHAN1_SPACING_WIDTH 6 /* AIF2TX_CHAN1_SPACING - [14:9] */
-#define WM8915_AIF2TX_CHAN1_SLOTS_MASK 0x01C0 /* AIF2TX_CHAN1_SLOTS - [8:6] */
-#define WM8915_AIF2TX_CHAN1_SLOTS_SHIFT 6 /* AIF2TX_CHAN1_SLOTS - [8:6] */
-#define WM8915_AIF2TX_CHAN1_SLOTS_WIDTH 3 /* AIF2TX_CHAN1_SLOTS - [8:6] */
-#define WM8915_AIF2TX_CHAN1_START_SLOT_MASK 0x003F /* AIF2TX_CHAN1_START_SLOT - [5:0] */
-#define WM8915_AIF2TX_CHAN1_START_SLOT_SHIFT 0 /* AIF2TX_CHAN1_START_SLOT - [5:0] */
-#define WM8915_AIF2TX_CHAN1_START_SLOT_WIDTH 6 /* AIF2TX_CHAN1_START_SLOT - [5:0] */
-
-/*
- * R811 (0x32B) - AIF2RX Channel 0 Configuration
- */
-#define WM8915_AIF2RX_CHAN0_DAT_INV 0x8000 /* AIF2RX_CHAN0_DAT_INV */
-#define WM8915_AIF2RX_CHAN0_DAT_INV_MASK 0x8000 /* AIF2RX_CHAN0_DAT_INV */
-#define WM8915_AIF2RX_CHAN0_DAT_INV_SHIFT 15 /* AIF2RX_CHAN0_DAT_INV */
-#define WM8915_AIF2RX_CHAN0_DAT_INV_WIDTH 1 /* AIF2RX_CHAN0_DAT_INV */
-#define WM8915_AIF2RX_CHAN0_SPACING_MASK 0x7E00 /* AIF2RX_CHAN0_SPACING - [14:9] */
-#define WM8915_AIF2RX_CHAN0_SPACING_SHIFT 9 /* AIF2RX_CHAN0_SPACING - [14:9] */
-#define WM8915_AIF2RX_CHAN0_SPACING_WIDTH 6 /* AIF2RX_CHAN0_SPACING - [14:9] */
-#define WM8915_AIF2RX_CHAN0_SLOTS_MASK 0x01C0 /* AIF2RX_CHAN0_SLOTS - [8:6] */
-#define WM8915_AIF2RX_CHAN0_SLOTS_SHIFT 6 /* AIF2RX_CHAN0_SLOTS - [8:6] */
-#define WM8915_AIF2RX_CHAN0_SLOTS_WIDTH 3 /* AIF2RX_CHAN0_SLOTS - [8:6] */
-#define WM8915_AIF2RX_CHAN0_START_SLOT_MASK 0x003F /* AIF2RX_CHAN0_START_SLOT - [5:0] */
-#define WM8915_AIF2RX_CHAN0_START_SLOT_SHIFT 0 /* AIF2RX_CHAN0_START_SLOT - [5:0] */
-#define WM8915_AIF2RX_CHAN0_START_SLOT_WIDTH 6 /* AIF2RX_CHAN0_START_SLOT - [5:0] */
-
-/*
- * R812 (0x32C) - AIF2RX Channel 1 Configuration
- */
-#define WM8915_AIF2RX_CHAN1_DAT_INV 0x8000 /* AIF2RX_CHAN1_DAT_INV */
-#define WM8915_AIF2RX_CHAN1_DAT_INV_MASK 0x8000 /* AIF2RX_CHAN1_DAT_INV */
-#define WM8915_AIF2RX_CHAN1_DAT_INV_SHIFT 15 /* AIF2RX_CHAN1_DAT_INV */
-#define WM8915_AIF2RX_CHAN1_DAT_INV_WIDTH 1 /* AIF2RX_CHAN1_DAT_INV */
-#define WM8915_AIF2RX_CHAN1_SPACING_MASK 0x7E00 /* AIF2RX_CHAN1_SPACING - [14:9] */
-#define WM8915_AIF2RX_CHAN1_SPACING_SHIFT 9 /* AIF2RX_CHAN1_SPACING - [14:9] */
-#define WM8915_AIF2RX_CHAN1_SPACING_WIDTH 6 /* AIF2RX_CHAN1_SPACING - [14:9] */
-#define WM8915_AIF2RX_CHAN1_SLOTS_MASK 0x01C0 /* AIF2RX_CHAN1_SLOTS - [8:6] */
-#define WM8915_AIF2RX_CHAN1_SLOTS_SHIFT 6 /* AIF2RX_CHAN1_SLOTS - [8:6] */
-#define WM8915_AIF2RX_CHAN1_SLOTS_WIDTH 3 /* AIF2RX_CHAN1_SLOTS - [8:6] */
-#define WM8915_AIF2RX_CHAN1_START_SLOT_MASK 0x003F /* AIF2RX_CHAN1_START_SLOT - [5:0] */
-#define WM8915_AIF2RX_CHAN1_START_SLOT_SHIFT 0 /* AIF2RX_CHAN1_START_SLOT - [5:0] */
-#define WM8915_AIF2RX_CHAN1_START_SLOT_WIDTH 6 /* AIF2RX_CHAN1_START_SLOT - [5:0] */
-
-/*
- * R813 (0x32D) - AIF2RX Mono Configuration
- */
-#define WM8915_AIF2RX_CHAN0_MONO_MODE 0x0001 /* AIF2RX_CHAN0_MONO_MODE */
-#define WM8915_AIF2RX_CHAN0_MONO_MODE_MASK 0x0001 /* AIF2RX_CHAN0_MONO_MODE */
-#define WM8915_AIF2RX_CHAN0_MONO_MODE_SHIFT 0 /* AIF2RX_CHAN0_MONO_MODE */
-#define WM8915_AIF2RX_CHAN0_MONO_MODE_WIDTH 1 /* AIF2RX_CHAN0_MONO_MODE */
-
-/*
- * R815 (0x32F) - AIF2TX Test
- */
-#define WM8915_AIF2TX_DITHER_ENA 0x0001 /* AIF2TX_DITHER_ENA */
-#define WM8915_AIF2TX_DITHER_ENA_MASK 0x0001 /* AIF2TX_DITHER_ENA */
-#define WM8915_AIF2TX_DITHER_ENA_SHIFT 0 /* AIF2TX_DITHER_ENA */
-#define WM8915_AIF2TX_DITHER_ENA_WIDTH 1 /* AIF2TX_DITHER_ENA */
-
-/*
- * R1024 (0x400) - DSP1 TX Left Volume
- */
-#define WM8915_DSP1TX_VU 0x0100 /* DSP1TX_VU */
-#define WM8915_DSP1TX_VU_MASK 0x0100 /* DSP1TX_VU */
-#define WM8915_DSP1TX_VU_SHIFT 8 /* DSP1TX_VU */
-#define WM8915_DSP1TX_VU_WIDTH 1 /* DSP1TX_VU */
-#define WM8915_DSP1TXL_VOL_MASK 0x00FF /* DSP1TXL_VOL - [7:0] */
-#define WM8915_DSP1TXL_VOL_SHIFT 0 /* DSP1TXL_VOL - [7:0] */
-#define WM8915_DSP1TXL_VOL_WIDTH 8 /* DSP1TXL_VOL - [7:0] */
-
-/*
- * R1025 (0x401) - DSP1 TX Right Volume
- */
-#define WM8915_DSP1TX_VU 0x0100 /* DSP1TX_VU */
-#define WM8915_DSP1TX_VU_MASK 0x0100 /* DSP1TX_VU */
-#define WM8915_DSP1TX_VU_SHIFT 8 /* DSP1TX_VU */
-#define WM8915_DSP1TX_VU_WIDTH 1 /* DSP1TX_VU */
-#define WM8915_DSP1TXR_VOL_MASK 0x00FF /* DSP1TXR_VOL - [7:0] */
-#define WM8915_DSP1TXR_VOL_SHIFT 0 /* DSP1TXR_VOL - [7:0] */
-#define WM8915_DSP1TXR_VOL_WIDTH 8 /* DSP1TXR_VOL - [7:0] */
-
-/*
- * R1026 (0x402) - DSP1 RX Left Volume
- */
-#define WM8915_DSP1RX_VU 0x0100 /* DSP1RX_VU */
-#define WM8915_DSP1RX_VU_MASK 0x0100 /* DSP1RX_VU */
-#define WM8915_DSP1RX_VU_SHIFT 8 /* DSP1RX_VU */
-#define WM8915_DSP1RX_VU_WIDTH 1 /* DSP1RX_VU */
-#define WM8915_DSP1RXL_VOL_MASK 0x00FF /* DSP1RXL_VOL - [7:0] */
-#define WM8915_DSP1RXL_VOL_SHIFT 0 /* DSP1RXL_VOL - [7:0] */
-#define WM8915_DSP1RXL_VOL_WIDTH 8 /* DSP1RXL_VOL - [7:0] */
-
-/*
- * R1027 (0x403) - DSP1 RX Right Volume
- */
-#define WM8915_DSP1RX_VU 0x0100 /* DSP1RX_VU */
-#define WM8915_DSP1RX_VU_MASK 0x0100 /* DSP1RX_VU */
-#define WM8915_DSP1RX_VU_SHIFT 8 /* DSP1RX_VU */
-#define WM8915_DSP1RX_VU_WIDTH 1 /* DSP1RX_VU */
-#define WM8915_DSP1RXR_VOL_MASK 0x00FF /* DSP1RXR_VOL - [7:0] */
-#define WM8915_DSP1RXR_VOL_SHIFT 0 /* DSP1RXR_VOL - [7:0] */
-#define WM8915_DSP1RXR_VOL_WIDTH 8 /* DSP1RXR_VOL - [7:0] */
-
-/*
- * R1040 (0x410) - DSP1 TX Filters
- */
-#define WM8915_DSP1TX_NF 0x2000 /* DSP1TX_NF */
-#define WM8915_DSP1TX_NF_MASK 0x2000 /* DSP1TX_NF */
-#define WM8915_DSP1TX_NF_SHIFT 13 /* DSP1TX_NF */
-#define WM8915_DSP1TX_NF_WIDTH 1 /* DSP1TX_NF */
-#define WM8915_DSP1TXL_HPF 0x1000 /* DSP1TXL_HPF */
-#define WM8915_DSP1TXL_HPF_MASK 0x1000 /* DSP1TXL_HPF */
-#define WM8915_DSP1TXL_HPF_SHIFT 12 /* DSP1TXL_HPF */
-#define WM8915_DSP1TXL_HPF_WIDTH 1 /* DSP1TXL_HPF */
-#define WM8915_DSP1TXR_HPF 0x0800 /* DSP1TXR_HPF */
-#define WM8915_DSP1TXR_HPF_MASK 0x0800 /* DSP1TXR_HPF */
-#define WM8915_DSP1TXR_HPF_SHIFT 11 /* DSP1TXR_HPF */
-#define WM8915_DSP1TXR_HPF_WIDTH 1 /* DSP1TXR_HPF */
-#define WM8915_DSP1TX_HPF_MODE_MASK 0x0018 /* DSP1TX_HPF_MODE - [4:3] */
-#define WM8915_DSP1TX_HPF_MODE_SHIFT 3 /* DSP1TX_HPF_MODE - [4:3] */
-#define WM8915_DSP1TX_HPF_MODE_WIDTH 2 /* DSP1TX_HPF_MODE - [4:3] */
-#define WM8915_DSP1TX_HPF_CUT_MASK 0x0007 /* DSP1TX_HPF_CUT - [2:0] */
-#define WM8915_DSP1TX_HPF_CUT_SHIFT 0 /* DSP1TX_HPF_CUT - [2:0] */
-#define WM8915_DSP1TX_HPF_CUT_WIDTH 3 /* DSP1TX_HPF_CUT - [2:0] */
-
-/*
- * R1056 (0x420) - DSP1 RX Filters (1)
- */
-#define WM8915_DSP1RX_MUTE 0x0200 /* DSP1RX_MUTE */
-#define WM8915_DSP1RX_MUTE_MASK 0x0200 /* DSP1RX_MUTE */
-#define WM8915_DSP1RX_MUTE_SHIFT 9 /* DSP1RX_MUTE */
-#define WM8915_DSP1RX_MUTE_WIDTH 1 /* DSP1RX_MUTE */
-#define WM8915_DSP1RX_MONO 0x0080 /* DSP1RX_MONO */
-#define WM8915_DSP1RX_MONO_MASK 0x0080 /* DSP1RX_MONO */
-#define WM8915_DSP1RX_MONO_SHIFT 7 /* DSP1RX_MONO */
-#define WM8915_DSP1RX_MONO_WIDTH 1 /* DSP1RX_MONO */
-#define WM8915_DSP1RX_MUTERATE 0x0020 /* DSP1RX_MUTERATE */
-#define WM8915_DSP1RX_MUTERATE_MASK 0x0020 /* DSP1RX_MUTERATE */
-#define WM8915_DSP1RX_MUTERATE_SHIFT 5 /* DSP1RX_MUTERATE */
-#define WM8915_DSP1RX_MUTERATE_WIDTH 1 /* DSP1RX_MUTERATE */
-#define WM8915_DSP1RX_UNMUTE_RAMP 0x0010 /* DSP1RX_UNMUTE_RAMP */
-#define WM8915_DSP1RX_UNMUTE_RAMP_MASK 0x0010 /* DSP1RX_UNMUTE_RAMP */
-#define WM8915_DSP1RX_UNMUTE_RAMP_SHIFT 4 /* DSP1RX_UNMUTE_RAMP */
-#define WM8915_DSP1RX_UNMUTE_RAMP_WIDTH 1 /* DSP1RX_UNMUTE_RAMP */
-
-/*
- * R1057 (0x421) - DSP1 RX Filters (2)
- */
-#define WM8915_DSP1RX_3D_GAIN_MASK 0x3E00 /* DSP1RX_3D_GAIN - [13:9] */
-#define WM8915_DSP1RX_3D_GAIN_SHIFT 9 /* DSP1RX_3D_GAIN - [13:9] */
-#define WM8915_DSP1RX_3D_GAIN_WIDTH 5 /* DSP1RX_3D_GAIN - [13:9] */
-#define WM8915_DSP1RX_3D_ENA 0x0100 /* DSP1RX_3D_ENA */
-#define WM8915_DSP1RX_3D_ENA_MASK 0x0100 /* DSP1RX_3D_ENA */
-#define WM8915_DSP1RX_3D_ENA_SHIFT 8 /* DSP1RX_3D_ENA */
-#define WM8915_DSP1RX_3D_ENA_WIDTH 1 /* DSP1RX_3D_ENA */
-
-/*
- * R1088 (0x440) - DSP1 DRC (1)
- */
-#define WM8915_DSP1DRC_SIG_DET_RMS_MASK 0xF800 /* DSP1DRC_SIG_DET_RMS - [15:11] */
-#define WM8915_DSP1DRC_SIG_DET_RMS_SHIFT 11 /* DSP1DRC_SIG_DET_RMS - [15:11] */
-#define WM8915_DSP1DRC_SIG_DET_RMS_WIDTH 5 /* DSP1DRC_SIG_DET_RMS - [15:11] */
-#define WM8915_DSP1DRC_SIG_DET_PK_MASK 0x0600 /* DSP1DRC_SIG_DET_PK - [10:9] */
-#define WM8915_DSP1DRC_SIG_DET_PK_SHIFT 9 /* DSP1DRC_SIG_DET_PK - [10:9] */
-#define WM8915_DSP1DRC_SIG_DET_PK_WIDTH 2 /* DSP1DRC_SIG_DET_PK - [10:9] */
-#define WM8915_DSP1DRC_NG_ENA 0x0100 /* DSP1DRC_NG_ENA */
-#define WM8915_DSP1DRC_NG_ENA_MASK 0x0100 /* DSP1DRC_NG_ENA */
-#define WM8915_DSP1DRC_NG_ENA_SHIFT 8 /* DSP1DRC_NG_ENA */
-#define WM8915_DSP1DRC_NG_ENA_WIDTH 1 /* DSP1DRC_NG_ENA */
-#define WM8915_DSP1DRC_SIG_DET_MODE 0x0080 /* DSP1DRC_SIG_DET_MODE */
-#define WM8915_DSP1DRC_SIG_DET_MODE_MASK 0x0080 /* DSP1DRC_SIG_DET_MODE */
-#define WM8915_DSP1DRC_SIG_DET_MODE_SHIFT 7 /* DSP1DRC_SIG_DET_MODE */
-#define WM8915_DSP1DRC_SIG_DET_MODE_WIDTH 1 /* DSP1DRC_SIG_DET_MODE */
-#define WM8915_DSP1DRC_SIG_DET 0x0040 /* DSP1DRC_SIG_DET */
-#define WM8915_DSP1DRC_SIG_DET_MASK 0x0040 /* DSP1DRC_SIG_DET */
-#define WM8915_DSP1DRC_SIG_DET_SHIFT 6 /* DSP1DRC_SIG_DET */
-#define WM8915_DSP1DRC_SIG_DET_WIDTH 1 /* DSP1DRC_SIG_DET */
-#define WM8915_DSP1DRC_KNEE2_OP_ENA 0x0020 /* DSP1DRC_KNEE2_OP_ENA */
-#define WM8915_DSP1DRC_KNEE2_OP_ENA_MASK 0x0020 /* DSP1DRC_KNEE2_OP_ENA */
-#define WM8915_DSP1DRC_KNEE2_OP_ENA_SHIFT 5 /* DSP1DRC_KNEE2_OP_ENA */
-#define WM8915_DSP1DRC_KNEE2_OP_ENA_WIDTH 1 /* DSP1DRC_KNEE2_OP_ENA */
-#define WM8915_DSP1DRC_QR 0x0010 /* DSP1DRC_QR */
-#define WM8915_DSP1DRC_QR_MASK 0x0010 /* DSP1DRC_QR */
-#define WM8915_DSP1DRC_QR_SHIFT 4 /* DSP1DRC_QR */
-#define WM8915_DSP1DRC_QR_WIDTH 1 /* DSP1DRC_QR */
-#define WM8915_DSP1DRC_ANTICLIP 0x0008 /* DSP1DRC_ANTICLIP */
-#define WM8915_DSP1DRC_ANTICLIP_MASK 0x0008 /* DSP1DRC_ANTICLIP */
-#define WM8915_DSP1DRC_ANTICLIP_SHIFT 3 /* DSP1DRC_ANTICLIP */
-#define WM8915_DSP1DRC_ANTICLIP_WIDTH 1 /* DSP1DRC_ANTICLIP */
-#define WM8915_DSP1RX_DRC_ENA 0x0004 /* DSP1RX_DRC_ENA */
-#define WM8915_DSP1RX_DRC_ENA_MASK 0x0004 /* DSP1RX_DRC_ENA */
-#define WM8915_DSP1RX_DRC_ENA_SHIFT 2 /* DSP1RX_DRC_ENA */
-#define WM8915_DSP1RX_DRC_ENA_WIDTH 1 /* DSP1RX_DRC_ENA */
-#define WM8915_DSP1TXL_DRC_ENA 0x0002 /* DSP1TXL_DRC_ENA */
-#define WM8915_DSP1TXL_DRC_ENA_MASK 0x0002 /* DSP1TXL_DRC_ENA */
-#define WM8915_DSP1TXL_DRC_ENA_SHIFT 1 /* DSP1TXL_DRC_ENA */
-#define WM8915_DSP1TXL_DRC_ENA_WIDTH 1 /* DSP1TXL_DRC_ENA */
-#define WM8915_DSP1TXR_DRC_ENA 0x0001 /* DSP1TXR_DRC_ENA */
-#define WM8915_DSP1TXR_DRC_ENA_MASK 0x0001 /* DSP1TXR_DRC_ENA */
-#define WM8915_DSP1TXR_DRC_ENA_SHIFT 0 /* DSP1TXR_DRC_ENA */
-#define WM8915_DSP1TXR_DRC_ENA_WIDTH 1 /* DSP1TXR_DRC_ENA */
-
-/*
- * R1089 (0x441) - DSP1 DRC (2)
- */
-#define WM8915_DSP1DRC_ATK_MASK 0x1E00 /* DSP1DRC_ATK - [12:9] */
-#define WM8915_DSP1DRC_ATK_SHIFT 9 /* DSP1DRC_ATK - [12:9] */
-#define WM8915_DSP1DRC_ATK_WIDTH 4 /* DSP1DRC_ATK - [12:9] */
-#define WM8915_DSP1DRC_DCY_MASK 0x01E0 /* DSP1DRC_DCY - [8:5] */
-#define WM8915_DSP1DRC_DCY_SHIFT 5 /* DSP1DRC_DCY - [8:5] */
-#define WM8915_DSP1DRC_DCY_WIDTH 4 /* DSP1DRC_DCY - [8:5] */
-#define WM8915_DSP1DRC_MINGAIN_MASK 0x001C /* DSP1DRC_MINGAIN - [4:2] */
-#define WM8915_DSP1DRC_MINGAIN_SHIFT 2 /* DSP1DRC_MINGAIN - [4:2] */
-#define WM8915_DSP1DRC_MINGAIN_WIDTH 3 /* DSP1DRC_MINGAIN - [4:2] */
-#define WM8915_DSP1DRC_MAXGAIN_MASK 0x0003 /* DSP1DRC_MAXGAIN - [1:0] */
-#define WM8915_DSP1DRC_MAXGAIN_SHIFT 0 /* DSP1DRC_MAXGAIN - [1:0] */
-#define WM8915_DSP1DRC_MAXGAIN_WIDTH 2 /* DSP1DRC_MAXGAIN - [1:0] */
-
-/*
- * R1090 (0x442) - DSP1 DRC (3)
- */
-#define WM8915_DSP1DRC_NG_MINGAIN_MASK 0xF000 /* DSP1DRC_NG_MINGAIN - [15:12] */
-#define WM8915_DSP1DRC_NG_MINGAIN_SHIFT 12 /* DSP1DRC_NG_MINGAIN - [15:12] */
-#define WM8915_DSP1DRC_NG_MINGAIN_WIDTH 4 /* DSP1DRC_NG_MINGAIN - [15:12] */
-#define WM8915_DSP1DRC_NG_EXP_MASK 0x0C00 /* DSP1DRC_NG_EXP - [11:10] */
-#define WM8915_DSP1DRC_NG_EXP_SHIFT 10 /* DSP1DRC_NG_EXP - [11:10] */
-#define WM8915_DSP1DRC_NG_EXP_WIDTH 2 /* DSP1DRC_NG_EXP - [11:10] */
-#define WM8915_DSP1DRC_QR_THR_MASK 0x0300 /* DSP1DRC_QR_THR - [9:8] */
-#define WM8915_DSP1DRC_QR_THR_SHIFT 8 /* DSP1DRC_QR_THR - [9:8] */
-#define WM8915_DSP1DRC_QR_THR_WIDTH 2 /* DSP1DRC_QR_THR - [9:8] */
-#define WM8915_DSP1DRC_QR_DCY_MASK 0x00C0 /* DSP1DRC_QR_DCY - [7:6] */
-#define WM8915_DSP1DRC_QR_DCY_SHIFT 6 /* DSP1DRC_QR_DCY - [7:6] */
-#define WM8915_DSP1DRC_QR_DCY_WIDTH 2 /* DSP1DRC_QR_DCY - [7:6] */
-#define WM8915_DSP1DRC_HI_COMP_MASK 0x0038 /* DSP1DRC_HI_COMP - [5:3] */
-#define WM8915_DSP1DRC_HI_COMP_SHIFT 3 /* DSP1DRC_HI_COMP - [5:3] */
-#define WM8915_DSP1DRC_HI_COMP_WIDTH 3 /* DSP1DRC_HI_COMP - [5:3] */
-#define WM8915_DSP1DRC_LO_COMP_MASK 0x0007 /* DSP1DRC_LO_COMP - [2:0] */
-#define WM8915_DSP1DRC_LO_COMP_SHIFT 0 /* DSP1DRC_LO_COMP - [2:0] */
-#define WM8915_DSP1DRC_LO_COMP_WIDTH 3 /* DSP1DRC_LO_COMP - [2:0] */
-
-/*
- * R1091 (0x443) - DSP1 DRC (4)
- */
-#define WM8915_DSP1DRC_KNEE_IP_MASK 0x07E0 /* DSP1DRC_KNEE_IP - [10:5] */
-#define WM8915_DSP1DRC_KNEE_IP_SHIFT 5 /* DSP1DRC_KNEE_IP - [10:5] */
-#define WM8915_DSP1DRC_KNEE_IP_WIDTH 6 /* DSP1DRC_KNEE_IP - [10:5] */
-#define WM8915_DSP1DRC_KNEE_OP_MASK 0x001F /* DSP1DRC_KNEE_OP - [4:0] */
-#define WM8915_DSP1DRC_KNEE_OP_SHIFT 0 /* DSP1DRC_KNEE_OP - [4:0] */
-#define WM8915_DSP1DRC_KNEE_OP_WIDTH 5 /* DSP1DRC_KNEE_OP - [4:0] */
-
-/*
- * R1092 (0x444) - DSP1 DRC (5)
- */
-#define WM8915_DSP1DRC_KNEE2_IP_MASK 0x03E0 /* DSP1DRC_KNEE2_IP - [9:5] */
-#define WM8915_DSP1DRC_KNEE2_IP_SHIFT 5 /* DSP1DRC_KNEE2_IP - [9:5] */
-#define WM8915_DSP1DRC_KNEE2_IP_WIDTH 5 /* DSP1DRC_KNEE2_IP - [9:5] */
-#define WM8915_DSP1DRC_KNEE2_OP_MASK 0x001F /* DSP1DRC_KNEE2_OP - [4:0] */
-#define WM8915_DSP1DRC_KNEE2_OP_SHIFT 0 /* DSP1DRC_KNEE2_OP - [4:0] */
-#define WM8915_DSP1DRC_KNEE2_OP_WIDTH 5 /* DSP1DRC_KNEE2_OP - [4:0] */
-
-/*
- * R1152 (0x480) - DSP1 RX EQ Gains (1)
- */
-#define WM8915_DSP1RX_EQ_B1_GAIN_MASK 0xF800 /* DSP1RX_EQ_B1_GAIN - [15:11] */
-#define WM8915_DSP1RX_EQ_B1_GAIN_SHIFT 11 /* DSP1RX_EQ_B1_GAIN - [15:11] */
-#define WM8915_DSP1RX_EQ_B1_GAIN_WIDTH 5 /* DSP1RX_EQ_B1_GAIN - [15:11] */
-#define WM8915_DSP1RX_EQ_B2_GAIN_MASK 0x07C0 /* DSP1RX_EQ_B2_GAIN - [10:6] */
-#define WM8915_DSP1RX_EQ_B2_GAIN_SHIFT 6 /* DSP1RX_EQ_B2_GAIN - [10:6] */
-#define WM8915_DSP1RX_EQ_B2_GAIN_WIDTH 5 /* DSP1RX_EQ_B2_GAIN - [10:6] */
-#define WM8915_DSP1RX_EQ_B3_GAIN_MASK 0x003E /* DSP1RX_EQ_B3_GAIN - [5:1] */
-#define WM8915_DSP1RX_EQ_B3_GAIN_SHIFT 1 /* DSP1RX_EQ_B3_GAIN - [5:1] */
-#define WM8915_DSP1RX_EQ_B3_GAIN_WIDTH 5 /* DSP1RX_EQ_B3_GAIN - [5:1] */
-#define WM8915_DSP1RX_EQ_ENA 0x0001 /* DSP1RX_EQ_ENA */
-#define WM8915_DSP1RX_EQ_ENA_MASK 0x0001 /* DSP1RX_EQ_ENA */
-#define WM8915_DSP1RX_EQ_ENA_SHIFT 0 /* DSP1RX_EQ_ENA */
-#define WM8915_DSP1RX_EQ_ENA_WIDTH 1 /* DSP1RX_EQ_ENA */
-
-/*
- * R1153 (0x481) - DSP1 RX EQ Gains (2)
- */
-#define WM8915_DSP1RX_EQ_B4_GAIN_MASK 0xF800 /* DSP1RX_EQ_B4_GAIN - [15:11] */
-#define WM8915_DSP1RX_EQ_B4_GAIN_SHIFT 11 /* DSP1RX_EQ_B4_GAIN - [15:11] */
-#define WM8915_DSP1RX_EQ_B4_GAIN_WIDTH 5 /* DSP1RX_EQ_B4_GAIN - [15:11] */
-#define WM8915_DSP1RX_EQ_B5_GAIN_MASK 0x07C0 /* DSP1RX_EQ_B5_GAIN - [10:6] */
-#define WM8915_DSP1RX_EQ_B5_GAIN_SHIFT 6 /* DSP1RX_EQ_B5_GAIN - [10:6] */
-#define WM8915_DSP1RX_EQ_B5_GAIN_WIDTH 5 /* DSP1RX_EQ_B5_GAIN - [10:6] */
-
-/*
- * R1154 (0x482) - DSP1 RX EQ Band 1 A
- */
-#define WM8915_DSP1RX_EQ_B1_A_MASK 0xFFFF /* DSP1RX_EQ_B1_A - [15:0] */
-#define WM8915_DSP1RX_EQ_B1_A_SHIFT 0 /* DSP1RX_EQ_B1_A - [15:0] */
-#define WM8915_DSP1RX_EQ_B1_A_WIDTH 16 /* DSP1RX_EQ_B1_A - [15:0] */
-
-/*
- * R1155 (0x483) - DSP1 RX EQ Band 1 B
- */
-#define WM8915_DSP1RX_EQ_B1_B_MASK 0xFFFF /* DSP1RX_EQ_B1_B - [15:0] */
-#define WM8915_DSP1RX_EQ_B1_B_SHIFT 0 /* DSP1RX_EQ_B1_B - [15:0] */
-#define WM8915_DSP1RX_EQ_B1_B_WIDTH 16 /* DSP1RX_EQ_B1_B - [15:0] */
-
-/*
- * R1156 (0x484) - DSP1 RX EQ Band 1 PG
- */
-#define WM8915_DSP1RX_EQ_B1_PG_MASK 0xFFFF /* DSP1RX_EQ_B1_PG - [15:0] */
-#define WM8915_DSP1RX_EQ_B1_PG_SHIFT 0 /* DSP1RX_EQ_B1_PG - [15:0] */
-#define WM8915_DSP1RX_EQ_B1_PG_WIDTH 16 /* DSP1RX_EQ_B1_PG - [15:0] */
-
-/*
- * R1157 (0x485) - DSP1 RX EQ Band 2 A
- */
-#define WM8915_DSP1RX_EQ_B2_A_MASK 0xFFFF /* DSP1RX_EQ_B2_A - [15:0] */
-#define WM8915_DSP1RX_EQ_B2_A_SHIFT 0 /* DSP1RX_EQ_B2_A - [15:0] */
-#define WM8915_DSP1RX_EQ_B2_A_WIDTH 16 /* DSP1RX_EQ_B2_A - [15:0] */
-
-/*
- * R1158 (0x486) - DSP1 RX EQ Band 2 B
- */
-#define WM8915_DSP1RX_EQ_B2_B_MASK 0xFFFF /* DSP1RX_EQ_B2_B - [15:0] */
-#define WM8915_DSP1RX_EQ_B2_B_SHIFT 0 /* DSP1RX_EQ_B2_B - [15:0] */
-#define WM8915_DSP1RX_EQ_B2_B_WIDTH 16 /* DSP1RX_EQ_B2_B - [15:0] */
-
-/*
- * R1159 (0x487) - DSP1 RX EQ Band 2 C
- */
-#define WM8915_DSP1RX_EQ_B2_C_MASK 0xFFFF /* DSP1RX_EQ_B2_C - [15:0] */
-#define WM8915_DSP1RX_EQ_B2_C_SHIFT 0 /* DSP1RX_EQ_B2_C - [15:0] */
-#define WM8915_DSP1RX_EQ_B2_C_WIDTH 16 /* DSP1RX_EQ_B2_C - [15:0] */
-
-/*
- * R1160 (0x488) - DSP1 RX EQ Band 2 PG
- */
-#define WM8915_DSP1RX_EQ_B2_PG_MASK 0xFFFF /* DSP1RX_EQ_B2_PG - [15:0] */
-#define WM8915_DSP1RX_EQ_B2_PG_SHIFT 0 /* DSP1RX_EQ_B2_PG - [15:0] */
-#define WM8915_DSP1RX_EQ_B2_PG_WIDTH 16 /* DSP1RX_EQ_B2_PG - [15:0] */
-
-/*
- * R1161 (0x489) - DSP1 RX EQ Band 3 A
- */
-#define WM8915_DSP1RX_EQ_B3_A_MASK 0xFFFF /* DSP1RX_EQ_B3_A - [15:0] */
-#define WM8915_DSP1RX_EQ_B3_A_SHIFT 0 /* DSP1RX_EQ_B3_A - [15:0] */
-#define WM8915_DSP1RX_EQ_B3_A_WIDTH 16 /* DSP1RX_EQ_B3_A - [15:0] */
-
-/*
- * R1162 (0x48A) - DSP1 RX EQ Band 3 B
- */
-#define WM8915_DSP1RX_EQ_B3_B_MASK 0xFFFF /* DSP1RX_EQ_B3_B - [15:0] */
-#define WM8915_DSP1RX_EQ_B3_B_SHIFT 0 /* DSP1RX_EQ_B3_B - [15:0] */
-#define WM8915_DSP1RX_EQ_B3_B_WIDTH 16 /* DSP1RX_EQ_B3_B - [15:0] */
-
-/*
- * R1163 (0x48B) - DSP1 RX EQ Band 3 C
- */
-#define WM8915_DSP1RX_EQ_B3_C_MASK 0xFFFF /* DSP1RX_EQ_B3_C - [15:0] */
-#define WM8915_DSP1RX_EQ_B3_C_SHIFT 0 /* DSP1RX_EQ_B3_C - [15:0] */
-#define WM8915_DSP1RX_EQ_B3_C_WIDTH 16 /* DSP1RX_EQ_B3_C - [15:0] */
-
-/*
- * R1164 (0x48C) - DSP1 RX EQ Band 3 PG
- */
-#define WM8915_DSP1RX_EQ_B3_PG_MASK 0xFFFF /* DSP1RX_EQ_B3_PG - [15:0] */
-#define WM8915_DSP1RX_EQ_B3_PG_SHIFT 0 /* DSP1RX_EQ_B3_PG - [15:0] */
-#define WM8915_DSP1RX_EQ_B3_PG_WIDTH 16 /* DSP1RX_EQ_B3_PG - [15:0] */
-
-/*
- * R1165 (0x48D) - DSP1 RX EQ Band 4 A
- */
-#define WM8915_DSP1RX_EQ_B4_A_MASK 0xFFFF /* DSP1RX_EQ_B4_A - [15:0] */
-#define WM8915_DSP1RX_EQ_B4_A_SHIFT 0 /* DSP1RX_EQ_B4_A - [15:0] */
-#define WM8915_DSP1RX_EQ_B4_A_WIDTH 16 /* DSP1RX_EQ_B4_A - [15:0] */
-
-/*
- * R1166 (0x48E) - DSP1 RX EQ Band 4 B
- */
-#define WM8915_DSP1RX_EQ_B4_B_MASK 0xFFFF /* DSP1RX_EQ_B4_B - [15:0] */
-#define WM8915_DSP1RX_EQ_B4_B_SHIFT 0 /* DSP1RX_EQ_B4_B - [15:0] */
-#define WM8915_DSP1RX_EQ_B4_B_WIDTH 16 /* DSP1RX_EQ_B4_B - [15:0] */
-
-/*
- * R1167 (0x48F) - DSP1 RX EQ Band 4 C
- */
-#define WM8915_DSP1RX_EQ_B4_C_MASK 0xFFFF /* DSP1RX_EQ_B4_C - [15:0] */
-#define WM8915_DSP1RX_EQ_B4_C_SHIFT 0 /* DSP1RX_EQ_B4_C - [15:0] */
-#define WM8915_DSP1RX_EQ_B4_C_WIDTH 16 /* DSP1RX_EQ_B4_C - [15:0] */
-
-/*
- * R1168 (0x490) - DSP1 RX EQ Band 4 PG
- */
-#define WM8915_DSP1RX_EQ_B4_PG_MASK 0xFFFF /* DSP1RX_EQ_B4_PG - [15:0] */
-#define WM8915_DSP1RX_EQ_B4_PG_SHIFT 0 /* DSP1RX_EQ_B4_PG - [15:0] */
-#define WM8915_DSP1RX_EQ_B4_PG_WIDTH 16 /* DSP1RX_EQ_B4_PG - [15:0] */
-
-/*
- * R1169 (0x491) - DSP1 RX EQ Band 5 A
- */
-#define WM8915_DSP1RX_EQ_B5_A_MASK 0xFFFF /* DSP1RX_EQ_B5_A - [15:0] */
-#define WM8915_DSP1RX_EQ_B5_A_SHIFT 0 /* DSP1RX_EQ_B5_A - [15:0] */
-#define WM8915_DSP1RX_EQ_B5_A_WIDTH 16 /* DSP1RX_EQ_B5_A - [15:0] */
-
-/*
- * R1170 (0x492) - DSP1 RX EQ Band 5 B
- */
-#define WM8915_DSP1RX_EQ_B5_B_MASK 0xFFFF /* DSP1RX_EQ_B5_B - [15:0] */
-#define WM8915_DSP1RX_EQ_B5_B_SHIFT 0 /* DSP1RX_EQ_B5_B - [15:0] */
-#define WM8915_DSP1RX_EQ_B5_B_WIDTH 16 /* DSP1RX_EQ_B5_B - [15:0] */
-
-/*
- * R1171 (0x493) - DSP1 RX EQ Band 5 PG
- */
-#define WM8915_DSP1RX_EQ_B5_PG_MASK 0xFFFF /* DSP1RX_EQ_B5_PG - [15:0] */
-#define WM8915_DSP1RX_EQ_B5_PG_SHIFT 0 /* DSP1RX_EQ_B5_PG - [15:0] */
-#define WM8915_DSP1RX_EQ_B5_PG_WIDTH 16 /* DSP1RX_EQ_B5_PG - [15:0] */
-
-/*
- * R1280 (0x500) - DSP2 TX Left Volume
- */
-#define WM8915_DSP2TX_VU 0x0100 /* DSP2TX_VU */
-#define WM8915_DSP2TX_VU_MASK 0x0100 /* DSP2TX_VU */
-#define WM8915_DSP2TX_VU_SHIFT 8 /* DSP2TX_VU */
-#define WM8915_DSP2TX_VU_WIDTH 1 /* DSP2TX_VU */
-#define WM8915_DSP2TXL_VOL_MASK 0x00FF /* DSP2TXL_VOL - [7:0] */
-#define WM8915_DSP2TXL_VOL_SHIFT 0 /* DSP2TXL_VOL - [7:0] */
-#define WM8915_DSP2TXL_VOL_WIDTH 8 /* DSP2TXL_VOL - [7:0] */
-
-/*
- * R1281 (0x501) - DSP2 TX Right Volume
- */
-#define WM8915_DSP2TX_VU 0x0100 /* DSP2TX_VU */
-#define WM8915_DSP2TX_VU_MASK 0x0100 /* DSP2TX_VU */
-#define WM8915_DSP2TX_VU_SHIFT 8 /* DSP2TX_VU */
-#define WM8915_DSP2TX_VU_WIDTH 1 /* DSP2TX_VU */
-#define WM8915_DSP2TXR_VOL_MASK 0x00FF /* DSP2TXR_VOL - [7:0] */
-#define WM8915_DSP2TXR_VOL_SHIFT 0 /* DSP2TXR_VOL - [7:0] */
-#define WM8915_DSP2TXR_VOL_WIDTH 8 /* DSP2TXR_VOL - [7:0] */
-
-/*
- * R1282 (0x502) - DSP2 RX Left Volume
- */
-#define WM8915_DSP2RX_VU 0x0100 /* DSP2RX_VU */
-#define WM8915_DSP2RX_VU_MASK 0x0100 /* DSP2RX_VU */
-#define WM8915_DSP2RX_VU_SHIFT 8 /* DSP2RX_VU */
-#define WM8915_DSP2RX_VU_WIDTH 1 /* DSP2RX_VU */
-#define WM8915_DSP2RXL_VOL_MASK 0x00FF /* DSP2RXL_VOL - [7:0] */
-#define WM8915_DSP2RXL_VOL_SHIFT 0 /* DSP2RXL_VOL - [7:0] */
-#define WM8915_DSP2RXL_VOL_WIDTH 8 /* DSP2RXL_VOL - [7:0] */
-
-/*
- * R1283 (0x503) - DSP2 RX Right Volume
- */
-#define WM8915_DSP2RX_VU 0x0100 /* DSP2RX_VU */
-#define WM8915_DSP2RX_VU_MASK 0x0100 /* DSP2RX_VU */
-#define WM8915_DSP2RX_VU_SHIFT 8 /* DSP2RX_VU */
-#define WM8915_DSP2RX_VU_WIDTH 1 /* DSP2RX_VU */
-#define WM8915_DSP2RXR_VOL_MASK 0x00FF /* DSP2RXR_VOL - [7:0] */
-#define WM8915_DSP2RXR_VOL_SHIFT 0 /* DSP2RXR_VOL - [7:0] */
-#define WM8915_DSP2RXR_VOL_WIDTH 8 /* DSP2RXR_VOL - [7:0] */
-
-/*
- * R1296 (0x510) - DSP2 TX Filters
- */
-#define WM8915_DSP2TX_NF 0x2000 /* DSP2TX_NF */
-#define WM8915_DSP2TX_NF_MASK 0x2000 /* DSP2TX_NF */
-#define WM8915_DSP2TX_NF_SHIFT 13 /* DSP2TX_NF */
-#define WM8915_DSP2TX_NF_WIDTH 1 /* DSP2TX_NF */
-#define WM8915_DSP2TXL_HPF 0x1000 /* DSP2TXL_HPF */
-#define WM8915_DSP2TXL_HPF_MASK 0x1000 /* DSP2TXL_HPF */
-#define WM8915_DSP2TXL_HPF_SHIFT 12 /* DSP2TXL_HPF */
-#define WM8915_DSP2TXL_HPF_WIDTH 1 /* DSP2TXL_HPF */
-#define WM8915_DSP2TXR_HPF 0x0800 /* DSP2TXR_HPF */
-#define WM8915_DSP2TXR_HPF_MASK 0x0800 /* DSP2TXR_HPF */
-#define WM8915_DSP2TXR_HPF_SHIFT 11 /* DSP2TXR_HPF */
-#define WM8915_DSP2TXR_HPF_WIDTH 1 /* DSP2TXR_HPF */
-#define WM8915_DSP2TX_HPF_MODE_MASK 0x0018 /* DSP2TX_HPF_MODE - [4:3] */
-#define WM8915_DSP2TX_HPF_MODE_SHIFT 3 /* DSP2TX_HPF_MODE - [4:3] */
-#define WM8915_DSP2TX_HPF_MODE_WIDTH 2 /* DSP2TX_HPF_MODE - [4:3] */
-#define WM8915_DSP2TX_HPF_CUT_MASK 0x0007 /* DSP2TX_HPF_CUT - [2:0] */
-#define WM8915_DSP2TX_HPF_CUT_SHIFT 0 /* DSP2TX_HPF_CUT - [2:0] */
-#define WM8915_DSP2TX_HPF_CUT_WIDTH 3 /* DSP2TX_HPF_CUT - [2:0] */
-
-/*
- * R1312 (0x520) - DSP2 RX Filters (1)
- */
-#define WM8915_DSP2RX_MUTE 0x0200 /* DSP2RX_MUTE */
-#define WM8915_DSP2RX_MUTE_MASK 0x0200 /* DSP2RX_MUTE */
-#define WM8915_DSP2RX_MUTE_SHIFT 9 /* DSP2RX_MUTE */
-#define WM8915_DSP2RX_MUTE_WIDTH 1 /* DSP2RX_MUTE */
-#define WM8915_DSP2RX_MONO 0x0080 /* DSP2RX_MONO */
-#define WM8915_DSP2RX_MONO_MASK 0x0080 /* DSP2RX_MONO */
-#define WM8915_DSP2RX_MONO_SHIFT 7 /* DSP2RX_MONO */
-#define WM8915_DSP2RX_MONO_WIDTH 1 /* DSP2RX_MONO */
-#define WM8915_DSP2RX_MUTERATE 0x0020 /* DSP2RX_MUTERATE */
-#define WM8915_DSP2RX_MUTERATE_MASK 0x0020 /* DSP2RX_MUTERATE */
-#define WM8915_DSP2RX_MUTERATE_SHIFT 5 /* DSP2RX_MUTERATE */
-#define WM8915_DSP2RX_MUTERATE_WIDTH 1 /* DSP2RX_MUTERATE */
-#define WM8915_DSP2RX_UNMUTE_RAMP 0x0010 /* DSP2RX_UNMUTE_RAMP */
-#define WM8915_DSP2RX_UNMUTE_RAMP_MASK 0x0010 /* DSP2RX_UNMUTE_RAMP */
-#define WM8915_DSP2RX_UNMUTE_RAMP_SHIFT 4 /* DSP2RX_UNMUTE_RAMP */
-#define WM8915_DSP2RX_UNMUTE_RAMP_WIDTH 1 /* DSP2RX_UNMUTE_RAMP */
-
-/*
- * R1313 (0x521) - DSP2 RX Filters (2)
- */
-#define WM8915_DSP2RX_3D_GAIN_MASK 0x3E00 /* DSP2RX_3D_GAIN - [13:9] */
-#define WM8915_DSP2RX_3D_GAIN_SHIFT 9 /* DSP2RX_3D_GAIN - [13:9] */
-#define WM8915_DSP2RX_3D_GAIN_WIDTH 5 /* DSP2RX_3D_GAIN - [13:9] */
-#define WM8915_DSP2RX_3D_ENA 0x0100 /* DSP2RX_3D_ENA */
-#define WM8915_DSP2RX_3D_ENA_MASK 0x0100 /* DSP2RX_3D_ENA */
-#define WM8915_DSP2RX_3D_ENA_SHIFT 8 /* DSP2RX_3D_ENA */
-#define WM8915_DSP2RX_3D_ENA_WIDTH 1 /* DSP2RX_3D_ENA */
-
-/*
- * R1344 (0x540) - DSP2 DRC (1)
- */
-#define WM8915_DSP2DRC_SIG_DET_RMS_MASK 0xF800 /* DSP2DRC_SIG_DET_RMS - [15:11] */
-#define WM8915_DSP2DRC_SIG_DET_RMS_SHIFT 11 /* DSP2DRC_SIG_DET_RMS - [15:11] */
-#define WM8915_DSP2DRC_SIG_DET_RMS_WIDTH 5 /* DSP2DRC_SIG_DET_RMS - [15:11] */
-#define WM8915_DSP2DRC_SIG_DET_PK_MASK 0x0600 /* DSP2DRC_SIG_DET_PK - [10:9] */
-#define WM8915_DSP2DRC_SIG_DET_PK_SHIFT 9 /* DSP2DRC_SIG_DET_PK - [10:9] */
-#define WM8915_DSP2DRC_SIG_DET_PK_WIDTH 2 /* DSP2DRC_SIG_DET_PK - [10:9] */
-#define WM8915_DSP2DRC_NG_ENA 0x0100 /* DSP2DRC_NG_ENA */
-#define WM8915_DSP2DRC_NG_ENA_MASK 0x0100 /* DSP2DRC_NG_ENA */
-#define WM8915_DSP2DRC_NG_ENA_SHIFT 8 /* DSP2DRC_NG_ENA */
-#define WM8915_DSP2DRC_NG_ENA_WIDTH 1 /* DSP2DRC_NG_ENA */
-#define WM8915_DSP2DRC_SIG_DET_MODE 0x0080 /* DSP2DRC_SIG_DET_MODE */
-#define WM8915_DSP2DRC_SIG_DET_MODE_MASK 0x0080 /* DSP2DRC_SIG_DET_MODE */
-#define WM8915_DSP2DRC_SIG_DET_MODE_SHIFT 7 /* DSP2DRC_SIG_DET_MODE */
-#define WM8915_DSP2DRC_SIG_DET_MODE_WIDTH 1 /* DSP2DRC_SIG_DET_MODE */
-#define WM8915_DSP2DRC_SIG_DET 0x0040 /* DSP2DRC_SIG_DET */
-#define WM8915_DSP2DRC_SIG_DET_MASK 0x0040 /* DSP2DRC_SIG_DET */
-#define WM8915_DSP2DRC_SIG_DET_SHIFT 6 /* DSP2DRC_SIG_DET */
-#define WM8915_DSP2DRC_SIG_DET_WIDTH 1 /* DSP2DRC_SIG_DET */
-#define WM8915_DSP2DRC_KNEE2_OP_ENA 0x0020 /* DSP2DRC_KNEE2_OP_ENA */
-#define WM8915_DSP2DRC_KNEE2_OP_ENA_MASK 0x0020 /* DSP2DRC_KNEE2_OP_ENA */
-#define WM8915_DSP2DRC_KNEE2_OP_ENA_SHIFT 5 /* DSP2DRC_KNEE2_OP_ENA */
-#define WM8915_DSP2DRC_KNEE2_OP_ENA_WIDTH 1 /* DSP2DRC_KNEE2_OP_ENA */
-#define WM8915_DSP2DRC_QR 0x0010 /* DSP2DRC_QR */
-#define WM8915_DSP2DRC_QR_MASK 0x0010 /* DSP2DRC_QR */
-#define WM8915_DSP2DRC_QR_SHIFT 4 /* DSP2DRC_QR */
-#define WM8915_DSP2DRC_QR_WIDTH 1 /* DSP2DRC_QR */
-#define WM8915_DSP2DRC_ANTICLIP 0x0008 /* DSP2DRC_ANTICLIP */
-#define WM8915_DSP2DRC_ANTICLIP_MASK 0x0008 /* DSP2DRC_ANTICLIP */
-#define WM8915_DSP2DRC_ANTICLIP_SHIFT 3 /* DSP2DRC_ANTICLIP */
-#define WM8915_DSP2DRC_ANTICLIP_WIDTH 1 /* DSP2DRC_ANTICLIP */
-#define WM8915_DSP2RX_DRC_ENA 0x0004 /* DSP2RX_DRC_ENA */
-#define WM8915_DSP2RX_DRC_ENA_MASK 0x0004 /* DSP2RX_DRC_ENA */
-#define WM8915_DSP2RX_DRC_ENA_SHIFT 2 /* DSP2RX_DRC_ENA */
-#define WM8915_DSP2RX_DRC_ENA_WIDTH 1 /* DSP2RX_DRC_ENA */
-#define WM8915_DSP2TXL_DRC_ENA 0x0002 /* DSP2TXL_DRC_ENA */
-#define WM8915_DSP2TXL_DRC_ENA_MASK 0x0002 /* DSP2TXL_DRC_ENA */
-#define WM8915_DSP2TXL_DRC_ENA_SHIFT 1 /* DSP2TXL_DRC_ENA */
-#define WM8915_DSP2TXL_DRC_ENA_WIDTH 1 /* DSP2TXL_DRC_ENA */
-#define WM8915_DSP2TXR_DRC_ENA 0x0001 /* DSP2TXR_DRC_ENA */
-#define WM8915_DSP2TXR_DRC_ENA_MASK 0x0001 /* DSP2TXR_DRC_ENA */
-#define WM8915_DSP2TXR_DRC_ENA_SHIFT 0 /* DSP2TXR_DRC_ENA */
-#define WM8915_DSP2TXR_DRC_ENA_WIDTH 1 /* DSP2TXR_DRC_ENA */
-
-/*
- * R1345 (0x541) - DSP2 DRC (2)
- */
-#define WM8915_DSP2DRC_ATK_MASK 0x1E00 /* DSP2DRC_ATK - [12:9] */
-#define WM8915_DSP2DRC_ATK_SHIFT 9 /* DSP2DRC_ATK - [12:9] */
-#define WM8915_DSP2DRC_ATK_WIDTH 4 /* DSP2DRC_ATK - [12:9] */
-#define WM8915_DSP2DRC_DCY_MASK 0x01E0 /* DSP2DRC_DCY - [8:5] */
-#define WM8915_DSP2DRC_DCY_SHIFT 5 /* DSP2DRC_DCY - [8:5] */
-#define WM8915_DSP2DRC_DCY_WIDTH 4 /* DSP2DRC_DCY - [8:5] */
-#define WM8915_DSP2DRC_MINGAIN_MASK 0x001C /* DSP2DRC_MINGAIN - [4:2] */
-#define WM8915_DSP2DRC_MINGAIN_SHIFT 2 /* DSP2DRC_MINGAIN - [4:2] */
-#define WM8915_DSP2DRC_MINGAIN_WIDTH 3 /* DSP2DRC_MINGAIN - [4:2] */
-#define WM8915_DSP2DRC_MAXGAIN_MASK 0x0003 /* DSP2DRC_MAXGAIN - [1:0] */
-#define WM8915_DSP2DRC_MAXGAIN_SHIFT 0 /* DSP2DRC_MAXGAIN - [1:0] */
-#define WM8915_DSP2DRC_MAXGAIN_WIDTH 2 /* DSP2DRC_MAXGAIN - [1:0] */
-
-/*
- * R1346 (0x542) - DSP2 DRC (3)
- */
-#define WM8915_DSP2DRC_NG_MINGAIN_MASK 0xF000 /* DSP2DRC_NG_MINGAIN - [15:12] */
-#define WM8915_DSP2DRC_NG_MINGAIN_SHIFT 12 /* DSP2DRC_NG_MINGAIN - [15:12] */
-#define WM8915_DSP2DRC_NG_MINGAIN_WIDTH 4 /* DSP2DRC_NG_MINGAIN - [15:12] */
-#define WM8915_DSP2DRC_NG_EXP_MASK 0x0C00 /* DSP2DRC_NG_EXP - [11:10] */
-#define WM8915_DSP2DRC_NG_EXP_SHIFT 10 /* DSP2DRC_NG_EXP - [11:10] */
-#define WM8915_DSP2DRC_NG_EXP_WIDTH 2 /* DSP2DRC_NG_EXP - [11:10] */
-#define WM8915_DSP2DRC_QR_THR_MASK 0x0300 /* DSP2DRC_QR_THR - [9:8] */
-#define WM8915_DSP2DRC_QR_THR_SHIFT 8 /* DSP2DRC_QR_THR - [9:8] */
-#define WM8915_DSP2DRC_QR_THR_WIDTH 2 /* DSP2DRC_QR_THR - [9:8] */
-#define WM8915_DSP2DRC_QR_DCY_MASK 0x00C0 /* DSP2DRC_QR_DCY - [7:6] */
-#define WM8915_DSP2DRC_QR_DCY_SHIFT 6 /* DSP2DRC_QR_DCY - [7:6] */
-#define WM8915_DSP2DRC_QR_DCY_WIDTH 2 /* DSP2DRC_QR_DCY - [7:6] */
-#define WM8915_DSP2DRC_HI_COMP_MASK 0x0038 /* DSP2DRC_HI_COMP - [5:3] */
-#define WM8915_DSP2DRC_HI_COMP_SHIFT 3 /* DSP2DRC_HI_COMP - [5:3] */
-#define WM8915_DSP2DRC_HI_COMP_WIDTH 3 /* DSP2DRC_HI_COMP - [5:3] */
-#define WM8915_DSP2DRC_LO_COMP_MASK 0x0007 /* DSP2DRC_LO_COMP - [2:0] */
-#define WM8915_DSP2DRC_LO_COMP_SHIFT 0 /* DSP2DRC_LO_COMP - [2:0] */
-#define WM8915_DSP2DRC_LO_COMP_WIDTH 3 /* DSP2DRC_LO_COMP - [2:0] */
-
-/*
- * R1347 (0x543) - DSP2 DRC (4)
- */
-#define WM8915_DSP2DRC_KNEE_IP_MASK 0x07E0 /* DSP2DRC_KNEE_IP - [10:5] */
-#define WM8915_DSP2DRC_KNEE_IP_SHIFT 5 /* DSP2DRC_KNEE_IP - [10:5] */
-#define WM8915_DSP2DRC_KNEE_IP_WIDTH 6 /* DSP2DRC_KNEE_IP - [10:5] */
-#define WM8915_DSP2DRC_KNEE_OP_MASK 0x001F /* DSP2DRC_KNEE_OP - [4:0] */
-#define WM8915_DSP2DRC_KNEE_OP_SHIFT 0 /* DSP2DRC_KNEE_OP - [4:0] */
-#define WM8915_DSP2DRC_KNEE_OP_WIDTH 5 /* DSP2DRC_KNEE_OP - [4:0] */
-
-/*
- * R1348 (0x544) - DSP2 DRC (5)
- */
-#define WM8915_DSP2DRC_KNEE2_IP_MASK 0x03E0 /* DSP2DRC_KNEE2_IP - [9:5] */
-#define WM8915_DSP2DRC_KNEE2_IP_SHIFT 5 /* DSP2DRC_KNEE2_IP - [9:5] */
-#define WM8915_DSP2DRC_KNEE2_IP_WIDTH 5 /* DSP2DRC_KNEE2_IP - [9:5] */
-#define WM8915_DSP2DRC_KNEE2_OP_MASK 0x001F /* DSP2DRC_KNEE2_OP - [4:0] */
-#define WM8915_DSP2DRC_KNEE2_OP_SHIFT 0 /* DSP2DRC_KNEE2_OP - [4:0] */
-#define WM8915_DSP2DRC_KNEE2_OP_WIDTH 5 /* DSP2DRC_KNEE2_OP - [4:0] */
-
-/*
- * R1408 (0x580) - DSP2 RX EQ Gains (1)
- */
-#define WM8915_DSP2RX_EQ_B1_GAIN_MASK 0xF800 /* DSP2RX_EQ_B1_GAIN - [15:11] */
-#define WM8915_DSP2RX_EQ_B1_GAIN_SHIFT 11 /* DSP2RX_EQ_B1_GAIN - [15:11] */
-#define WM8915_DSP2RX_EQ_B1_GAIN_WIDTH 5 /* DSP2RX_EQ_B1_GAIN - [15:11] */
-#define WM8915_DSP2RX_EQ_B2_GAIN_MASK 0x07C0 /* DSP2RX_EQ_B2_GAIN - [10:6] */
-#define WM8915_DSP2RX_EQ_B2_GAIN_SHIFT 6 /* DSP2RX_EQ_B2_GAIN - [10:6] */
-#define WM8915_DSP2RX_EQ_B2_GAIN_WIDTH 5 /* DSP2RX_EQ_B2_GAIN - [10:6] */
-#define WM8915_DSP2RX_EQ_B3_GAIN_MASK 0x003E /* DSP2RX_EQ_B3_GAIN - [5:1] */
-#define WM8915_DSP2RX_EQ_B3_GAIN_SHIFT 1 /* DSP2RX_EQ_B3_GAIN - [5:1] */
-#define WM8915_DSP2RX_EQ_B3_GAIN_WIDTH 5 /* DSP2RX_EQ_B3_GAIN - [5:1] */
-#define WM8915_DSP2RX_EQ_ENA 0x0001 /* DSP2RX_EQ_ENA */
-#define WM8915_DSP2RX_EQ_ENA_MASK 0x0001 /* DSP2RX_EQ_ENA */
-#define WM8915_DSP2RX_EQ_ENA_SHIFT 0 /* DSP2RX_EQ_ENA */
-#define WM8915_DSP2RX_EQ_ENA_WIDTH 1 /* DSP2RX_EQ_ENA */
-
-/*
- * R1409 (0x581) - DSP2 RX EQ Gains (2)
- */
-#define WM8915_DSP2RX_EQ_B4_GAIN_MASK 0xF800 /* DSP2RX_EQ_B4_GAIN - [15:11] */
-#define WM8915_DSP2RX_EQ_B4_GAIN_SHIFT 11 /* DSP2RX_EQ_B4_GAIN - [15:11] */
-#define WM8915_DSP2RX_EQ_B4_GAIN_WIDTH 5 /* DSP2RX_EQ_B4_GAIN - [15:11] */
-#define WM8915_DSP2RX_EQ_B5_GAIN_MASK 0x07C0 /* DSP2RX_EQ_B5_GAIN - [10:6] */
-#define WM8915_DSP2RX_EQ_B5_GAIN_SHIFT 6 /* DSP2RX_EQ_B5_GAIN - [10:6] */
-#define WM8915_DSP2RX_EQ_B5_GAIN_WIDTH 5 /* DSP2RX_EQ_B5_GAIN - [10:6] */
-
-/*
- * R1410 (0x582) - DSP2 RX EQ Band 1 A
- */
-#define WM8915_DSP2RX_EQ_B1_A_MASK 0xFFFF /* DSP2RX_EQ_B1_A - [15:0] */
-#define WM8915_DSP2RX_EQ_B1_A_SHIFT 0 /* DSP2RX_EQ_B1_A - [15:0] */
-#define WM8915_DSP2RX_EQ_B1_A_WIDTH 16 /* DSP2RX_EQ_B1_A - [15:0] */
-
-/*
- * R1411 (0x583) - DSP2 RX EQ Band 1 B
- */
-#define WM8915_DSP2RX_EQ_B1_B_MASK 0xFFFF /* DSP2RX_EQ_B1_B - [15:0] */
-#define WM8915_DSP2RX_EQ_B1_B_SHIFT 0 /* DSP2RX_EQ_B1_B - [15:0] */
-#define WM8915_DSP2RX_EQ_B1_B_WIDTH 16 /* DSP2RX_EQ_B1_B - [15:0] */
-
-/*
- * R1412 (0x584) - DSP2 RX EQ Band 1 PG
- */
-#define WM8915_DSP2RX_EQ_B1_PG_MASK 0xFFFF /* DSP2RX_EQ_B1_PG - [15:0] */
-#define WM8915_DSP2RX_EQ_B1_PG_SHIFT 0 /* DSP2RX_EQ_B1_PG - [15:0] */
-#define WM8915_DSP2RX_EQ_B1_PG_WIDTH 16 /* DSP2RX_EQ_B1_PG - [15:0] */
-
-/*
- * R1413 (0x585) - DSP2 RX EQ Band 2 A
- */
-#define WM8915_DSP2RX_EQ_B2_A_MASK 0xFFFF /* DSP2RX_EQ_B2_A - [15:0] */
-#define WM8915_DSP2RX_EQ_B2_A_SHIFT 0 /* DSP2RX_EQ_B2_A - [15:0] */
-#define WM8915_DSP2RX_EQ_B2_A_WIDTH 16 /* DSP2RX_EQ_B2_A - [15:0] */
-
-/*
- * R1414 (0x586) - DSP2 RX EQ Band 2 B
- */
-#define WM8915_DSP2RX_EQ_B2_B_MASK 0xFFFF /* DSP2RX_EQ_B2_B - [15:0] */
-#define WM8915_DSP2RX_EQ_B2_B_SHIFT 0 /* DSP2RX_EQ_B2_B - [15:0] */
-#define WM8915_DSP2RX_EQ_B2_B_WIDTH 16 /* DSP2RX_EQ_B2_B - [15:0] */
-
-/*
- * R1415 (0x587) - DSP2 RX EQ Band 2 C
- */
-#define WM8915_DSP2RX_EQ_B2_C_MASK 0xFFFF /* DSP2RX_EQ_B2_C - [15:0] */
-#define WM8915_DSP2RX_EQ_B2_C_SHIFT 0 /* DSP2RX_EQ_B2_C - [15:0] */
-#define WM8915_DSP2RX_EQ_B2_C_WIDTH 16 /* DSP2RX_EQ_B2_C - [15:0] */
-
-/*
- * R1416 (0x588) - DSP2 RX EQ Band 2 PG
- */
-#define WM8915_DSP2RX_EQ_B2_PG_MASK 0xFFFF /* DSP2RX_EQ_B2_PG - [15:0] */
-#define WM8915_DSP2RX_EQ_B2_PG_SHIFT 0 /* DSP2RX_EQ_B2_PG - [15:0] */
-#define WM8915_DSP2RX_EQ_B2_PG_WIDTH 16 /* DSP2RX_EQ_B2_PG - [15:0] */
-
-/*
- * R1417 (0x589) - DSP2 RX EQ Band 3 A
- */
-#define WM8915_DSP2RX_EQ_B3_A_MASK 0xFFFF /* DSP2RX_EQ_B3_A - [15:0] */
-#define WM8915_DSP2RX_EQ_B3_A_SHIFT 0 /* DSP2RX_EQ_B3_A - [15:0] */
-#define WM8915_DSP2RX_EQ_B3_A_WIDTH 16 /* DSP2RX_EQ_B3_A - [15:0] */
-
-/*
- * R1418 (0x58A) - DSP2 RX EQ Band 3 B
- */
-#define WM8915_DSP2RX_EQ_B3_B_MASK 0xFFFF /* DSP2RX_EQ_B3_B - [15:0] */
-#define WM8915_DSP2RX_EQ_B3_B_SHIFT 0 /* DSP2RX_EQ_B3_B - [15:0] */
-#define WM8915_DSP2RX_EQ_B3_B_WIDTH 16 /* DSP2RX_EQ_B3_B - [15:0] */
-
-/*
- * R1419 (0x58B) - DSP2 RX EQ Band 3 C
- */
-#define WM8915_DSP2RX_EQ_B3_C_MASK 0xFFFF /* DSP2RX_EQ_B3_C - [15:0] */
-#define WM8915_DSP2RX_EQ_B3_C_SHIFT 0 /* DSP2RX_EQ_B3_C - [15:0] */
-#define WM8915_DSP2RX_EQ_B3_C_WIDTH 16 /* DSP2RX_EQ_B3_C - [15:0] */
-
-/*
- * R1420 (0x58C) - DSP2 RX EQ Band 3 PG
- */
-#define WM8915_DSP2RX_EQ_B3_PG_MASK 0xFFFF /* DSP2RX_EQ_B3_PG - [15:0] */
-#define WM8915_DSP2RX_EQ_B3_PG_SHIFT 0 /* DSP2RX_EQ_B3_PG - [15:0] */
-#define WM8915_DSP2RX_EQ_B3_PG_WIDTH 16 /* DSP2RX_EQ_B3_PG - [15:0] */
-
-/*
- * R1421 (0x58D) - DSP2 RX EQ Band 4 A
- */
-#define WM8915_DSP2RX_EQ_B4_A_MASK 0xFFFF /* DSP2RX_EQ_B4_A - [15:0] */
-#define WM8915_DSP2RX_EQ_B4_A_SHIFT 0 /* DSP2RX_EQ_B4_A - [15:0] */
-#define WM8915_DSP2RX_EQ_B4_A_WIDTH 16 /* DSP2RX_EQ_B4_A - [15:0] */
-
-/*
- * R1422 (0x58E) - DSP2 RX EQ Band 4 B
- */
-#define WM8915_DSP2RX_EQ_B4_B_MASK 0xFFFF /* DSP2RX_EQ_B4_B - [15:0] */
-#define WM8915_DSP2RX_EQ_B4_B_SHIFT 0 /* DSP2RX_EQ_B4_B - [15:0] */
-#define WM8915_DSP2RX_EQ_B4_B_WIDTH 16 /* DSP2RX_EQ_B4_B - [15:0] */
-
-/*
- * R1423 (0x58F) - DSP2 RX EQ Band 4 C
- */
-#define WM8915_DSP2RX_EQ_B4_C_MASK 0xFFFF /* DSP2RX_EQ_B4_C - [15:0] */
-#define WM8915_DSP2RX_EQ_B4_C_SHIFT 0 /* DSP2RX_EQ_B4_C - [15:0] */
-#define WM8915_DSP2RX_EQ_B4_C_WIDTH 16 /* DSP2RX_EQ_B4_C - [15:0] */
-
-/*
- * R1424 (0x590) - DSP2 RX EQ Band 4 PG
- */
-#define WM8915_DSP2RX_EQ_B4_PG_MASK 0xFFFF /* DSP2RX_EQ_B4_PG - [15:0] */
-#define WM8915_DSP2RX_EQ_B4_PG_SHIFT 0 /* DSP2RX_EQ_B4_PG - [15:0] */
-#define WM8915_DSP2RX_EQ_B4_PG_WIDTH 16 /* DSP2RX_EQ_B4_PG - [15:0] */
-
-/*
- * R1425 (0x591) - DSP2 RX EQ Band 5 A
- */
-#define WM8915_DSP2RX_EQ_B5_A_MASK 0xFFFF /* DSP2RX_EQ_B5_A - [15:0] */
-#define WM8915_DSP2RX_EQ_B5_A_SHIFT 0 /* DSP2RX_EQ_B5_A - [15:0] */
-#define WM8915_DSP2RX_EQ_B5_A_WIDTH 16 /* DSP2RX_EQ_B5_A - [15:0] */
-
-/*
- * R1426 (0x592) - DSP2 RX EQ Band 5 B
- */
-#define WM8915_DSP2RX_EQ_B5_B_MASK 0xFFFF /* DSP2RX_EQ_B5_B - [15:0] */
-#define WM8915_DSP2RX_EQ_B5_B_SHIFT 0 /* DSP2RX_EQ_B5_B - [15:0] */
-#define WM8915_DSP2RX_EQ_B5_B_WIDTH 16 /* DSP2RX_EQ_B5_B - [15:0] */
-
-/*
- * R1427 (0x593) - DSP2 RX EQ Band 5 PG
- */
-#define WM8915_DSP2RX_EQ_B5_PG_MASK 0xFFFF /* DSP2RX_EQ_B5_PG - [15:0] */
-#define WM8915_DSP2RX_EQ_B5_PG_SHIFT 0 /* DSP2RX_EQ_B5_PG - [15:0] */
-#define WM8915_DSP2RX_EQ_B5_PG_WIDTH 16 /* DSP2RX_EQ_B5_PG - [15:0] */
-
-/*
- * R1536 (0x600) - DAC1 Mixer Volumes
- */
-#define WM8915_ADCR_DAC1_VOL_MASK 0x03E0 /* ADCR_DAC1_VOL - [9:5] */
-#define WM8915_ADCR_DAC1_VOL_SHIFT 5 /* ADCR_DAC1_VOL - [9:5] */
-#define WM8915_ADCR_DAC1_VOL_WIDTH 5 /* ADCR_DAC1_VOL - [9:5] */
-#define WM8915_ADCL_DAC1_VOL_MASK 0x001F /* ADCL_DAC1_VOL - [4:0] */
-#define WM8915_ADCL_DAC1_VOL_SHIFT 0 /* ADCL_DAC1_VOL - [4:0] */
-#define WM8915_ADCL_DAC1_VOL_WIDTH 5 /* ADCL_DAC1_VOL - [4:0] */
-
-/*
- * R1537 (0x601) - DAC1 Left Mixer Routing
- */
-#define WM8915_ADCR_TO_DAC1L 0x0020 /* ADCR_TO_DAC1L */
-#define WM8915_ADCR_TO_DAC1L_MASK 0x0020 /* ADCR_TO_DAC1L */
-#define WM8915_ADCR_TO_DAC1L_SHIFT 5 /* ADCR_TO_DAC1L */
-#define WM8915_ADCR_TO_DAC1L_WIDTH 1 /* ADCR_TO_DAC1L */
-#define WM8915_ADCL_TO_DAC1L 0x0010 /* ADCL_TO_DAC1L */
-#define WM8915_ADCL_TO_DAC1L_MASK 0x0010 /* ADCL_TO_DAC1L */
-#define WM8915_ADCL_TO_DAC1L_SHIFT 4 /* ADCL_TO_DAC1L */
-#define WM8915_ADCL_TO_DAC1L_WIDTH 1 /* ADCL_TO_DAC1L */
-#define WM8915_DSP2RXL_TO_DAC1L 0x0002 /* DSP2RXL_TO_DAC1L */
-#define WM8915_DSP2RXL_TO_DAC1L_MASK 0x0002 /* DSP2RXL_TO_DAC1L */
-#define WM8915_DSP2RXL_TO_DAC1L_SHIFT 1 /* DSP2RXL_TO_DAC1L */
-#define WM8915_DSP2RXL_TO_DAC1L_WIDTH 1 /* DSP2RXL_TO_DAC1L */
-#define WM8915_DSP1RXL_TO_DAC1L 0x0001 /* DSP1RXL_TO_DAC1L */
-#define WM8915_DSP1RXL_TO_DAC1L_MASK 0x0001 /* DSP1RXL_TO_DAC1L */
-#define WM8915_DSP1RXL_TO_DAC1L_SHIFT 0 /* DSP1RXL_TO_DAC1L */
-#define WM8915_DSP1RXL_TO_DAC1L_WIDTH 1 /* DSP1RXL_TO_DAC1L */
-
-/*
- * R1538 (0x602) - DAC1 Right Mixer Routing
- */
-#define WM8915_ADCR_TO_DAC1R 0x0020 /* ADCR_TO_DAC1R */
-#define WM8915_ADCR_TO_DAC1R_MASK 0x0020 /* ADCR_TO_DAC1R */
-#define WM8915_ADCR_TO_DAC1R_SHIFT 5 /* ADCR_TO_DAC1R */
-#define WM8915_ADCR_TO_DAC1R_WIDTH 1 /* ADCR_TO_DAC1R */
-#define WM8915_ADCL_TO_DAC1R 0x0010 /* ADCL_TO_DAC1R */
-#define WM8915_ADCL_TO_DAC1R_MASK 0x0010 /* ADCL_TO_DAC1R */
-#define WM8915_ADCL_TO_DAC1R_SHIFT 4 /* ADCL_TO_DAC1R */
-#define WM8915_ADCL_TO_DAC1R_WIDTH 1 /* ADCL_TO_DAC1R */
-#define WM8915_DSP2RXR_TO_DAC1R 0x0002 /* DSP2RXR_TO_DAC1R */
-#define WM8915_DSP2RXR_TO_DAC1R_MASK 0x0002 /* DSP2RXR_TO_DAC1R */
-#define WM8915_DSP2RXR_TO_DAC1R_SHIFT 1 /* DSP2RXR_TO_DAC1R */
-#define WM8915_DSP2RXR_TO_DAC1R_WIDTH 1 /* DSP2RXR_TO_DAC1R */
-#define WM8915_DSP1RXR_TO_DAC1R 0x0001 /* DSP1RXR_TO_DAC1R */
-#define WM8915_DSP1RXR_TO_DAC1R_MASK 0x0001 /* DSP1RXR_TO_DAC1R */
-#define WM8915_DSP1RXR_TO_DAC1R_SHIFT 0 /* DSP1RXR_TO_DAC1R */
-#define WM8915_DSP1RXR_TO_DAC1R_WIDTH 1 /* DSP1RXR_TO_DAC1R */
-
-/*
- * R1539 (0x603) - DAC2 Mixer Volumes
- */
-#define WM8915_ADCR_DAC2_VOL_MASK 0x03E0 /* ADCR_DAC2_VOL - [9:5] */
-#define WM8915_ADCR_DAC2_VOL_SHIFT 5 /* ADCR_DAC2_VOL - [9:5] */
-#define WM8915_ADCR_DAC2_VOL_WIDTH 5 /* ADCR_DAC2_VOL - [9:5] */
-#define WM8915_ADCL_DAC2_VOL_MASK 0x001F /* ADCL_DAC2_VOL - [4:0] */
-#define WM8915_ADCL_DAC2_VOL_SHIFT 0 /* ADCL_DAC2_VOL - [4:0] */
-#define WM8915_ADCL_DAC2_VOL_WIDTH 5 /* ADCL_DAC2_VOL - [4:0] */
-
-/*
- * R1540 (0x604) - DAC2 Left Mixer Routing
- */
-#define WM8915_ADCR_TO_DAC2L 0x0020 /* ADCR_TO_DAC2L */
-#define WM8915_ADCR_TO_DAC2L_MASK 0x0020 /* ADCR_TO_DAC2L */
-#define WM8915_ADCR_TO_DAC2L_SHIFT 5 /* ADCR_TO_DAC2L */
-#define WM8915_ADCR_TO_DAC2L_WIDTH 1 /* ADCR_TO_DAC2L */
-#define WM8915_ADCL_TO_DAC2L 0x0010 /* ADCL_TO_DAC2L */
-#define WM8915_ADCL_TO_DAC2L_MASK 0x0010 /* ADCL_TO_DAC2L */
-#define WM8915_ADCL_TO_DAC2L_SHIFT 4 /* ADCL_TO_DAC2L */
-#define WM8915_ADCL_TO_DAC2L_WIDTH 1 /* ADCL_TO_DAC2L */
-#define WM8915_DSP2RXL_TO_DAC2L 0x0002 /* DSP2RXL_TO_DAC2L */
-#define WM8915_DSP2RXL_TO_DAC2L_MASK 0x0002 /* DSP2RXL_TO_DAC2L */
-#define WM8915_DSP2RXL_TO_DAC2L_SHIFT 1 /* DSP2RXL_TO_DAC2L */
-#define WM8915_DSP2RXL_TO_DAC2L_WIDTH 1 /* DSP2RXL_TO_DAC2L */
-#define WM8915_DSP1RXL_TO_DAC2L 0x0001 /* DSP1RXL_TO_DAC2L */
-#define WM8915_DSP1RXL_TO_DAC2L_MASK 0x0001 /* DSP1RXL_TO_DAC2L */
-#define WM8915_DSP1RXL_TO_DAC2L_SHIFT 0 /* DSP1RXL_TO_DAC2L */
-#define WM8915_DSP1RXL_TO_DAC2L_WIDTH 1 /* DSP1RXL_TO_DAC2L */
-
-/*
- * R1541 (0x605) - DAC2 Right Mixer Routing
- */
-#define WM8915_ADCR_TO_DAC2R 0x0020 /* ADCR_TO_DAC2R */
-#define WM8915_ADCR_TO_DAC2R_MASK 0x0020 /* ADCR_TO_DAC2R */
-#define WM8915_ADCR_TO_DAC2R_SHIFT 5 /* ADCR_TO_DAC2R */
-#define WM8915_ADCR_TO_DAC2R_WIDTH 1 /* ADCR_TO_DAC2R */
-#define WM8915_ADCL_TO_DAC2R 0x0010 /* ADCL_TO_DAC2R */
-#define WM8915_ADCL_TO_DAC2R_MASK 0x0010 /* ADCL_TO_DAC2R */
-#define WM8915_ADCL_TO_DAC2R_SHIFT 4 /* ADCL_TO_DAC2R */
-#define WM8915_ADCL_TO_DAC2R_WIDTH 1 /* ADCL_TO_DAC2R */
-#define WM8915_DSP2RXR_TO_DAC2R 0x0002 /* DSP2RXR_TO_DAC2R */
-#define WM8915_DSP2RXR_TO_DAC2R_MASK 0x0002 /* DSP2RXR_TO_DAC2R */
-#define WM8915_DSP2RXR_TO_DAC2R_SHIFT 1 /* DSP2RXR_TO_DAC2R */
-#define WM8915_DSP2RXR_TO_DAC2R_WIDTH 1 /* DSP2RXR_TO_DAC2R */
-#define WM8915_DSP1RXR_TO_DAC2R 0x0001 /* DSP1RXR_TO_DAC2R */
-#define WM8915_DSP1RXR_TO_DAC2R_MASK 0x0001 /* DSP1RXR_TO_DAC2R */
-#define WM8915_DSP1RXR_TO_DAC2R_SHIFT 0 /* DSP1RXR_TO_DAC2R */
-#define WM8915_DSP1RXR_TO_DAC2R_WIDTH 1 /* DSP1RXR_TO_DAC2R */
-
-/*
- * R1542 (0x606) - DSP1 TX Left Mixer Routing
- */
-#define WM8915_ADC1L_TO_DSP1TXL 0x0002 /* ADC1L_TO_DSP1TXL */
-#define WM8915_ADC1L_TO_DSP1TXL_MASK 0x0002 /* ADC1L_TO_DSP1TXL */
-#define WM8915_ADC1L_TO_DSP1TXL_SHIFT 1 /* ADC1L_TO_DSP1TXL */
-#define WM8915_ADC1L_TO_DSP1TXL_WIDTH 1 /* ADC1L_TO_DSP1TXL */
-#define WM8915_DACL_TO_DSP1TXL 0x0001 /* DACL_TO_DSP1TXL */
-#define WM8915_DACL_TO_DSP1TXL_MASK 0x0001 /* DACL_TO_DSP1TXL */
-#define WM8915_DACL_TO_DSP1TXL_SHIFT 0 /* DACL_TO_DSP1TXL */
-#define WM8915_DACL_TO_DSP1TXL_WIDTH 1 /* DACL_TO_DSP1TXL */
-
-/*
- * R1543 (0x607) - DSP1 TX Right Mixer Routing
- */
-#define WM8915_ADC1R_TO_DSP1TXR 0x0002 /* ADC1R_TO_DSP1TXR */
-#define WM8915_ADC1R_TO_DSP1TXR_MASK 0x0002 /* ADC1R_TO_DSP1TXR */
-#define WM8915_ADC1R_TO_DSP1TXR_SHIFT 1 /* ADC1R_TO_DSP1TXR */
-#define WM8915_ADC1R_TO_DSP1TXR_WIDTH 1 /* ADC1R_TO_DSP1TXR */
-#define WM8915_DACR_TO_DSP1TXR 0x0001 /* DACR_TO_DSP1TXR */
-#define WM8915_DACR_TO_DSP1TXR_MASK 0x0001 /* DACR_TO_DSP1TXR */
-#define WM8915_DACR_TO_DSP1TXR_SHIFT 0 /* DACR_TO_DSP1TXR */
-#define WM8915_DACR_TO_DSP1TXR_WIDTH 1 /* DACR_TO_DSP1TXR */
-
-/*
- * R1544 (0x608) - DSP2 TX Left Mixer Routing
- */
-#define WM8915_ADC2L_TO_DSP2TXL 0x0002 /* ADC2L_TO_DSP2TXL */
-#define WM8915_ADC2L_TO_DSP2TXL_MASK 0x0002 /* ADC2L_TO_DSP2TXL */
-#define WM8915_ADC2L_TO_DSP2TXL_SHIFT 1 /* ADC2L_TO_DSP2TXL */
-#define WM8915_ADC2L_TO_DSP2TXL_WIDTH 1 /* ADC2L_TO_DSP2TXL */
-#define WM8915_DACL_TO_DSP2TXL 0x0001 /* DACL_TO_DSP2TXL */
-#define WM8915_DACL_TO_DSP2TXL_MASK 0x0001 /* DACL_TO_DSP2TXL */
-#define WM8915_DACL_TO_DSP2TXL_SHIFT 0 /* DACL_TO_DSP2TXL */
-#define WM8915_DACL_TO_DSP2TXL_WIDTH 1 /* DACL_TO_DSP2TXL */
-
-/*
- * R1545 (0x609) - DSP2 TX Right Mixer Routing
- */
-#define WM8915_ADC2R_TO_DSP2TXR 0x0002 /* ADC2R_TO_DSP2TXR */
-#define WM8915_ADC2R_TO_DSP2TXR_MASK 0x0002 /* ADC2R_TO_DSP2TXR */
-#define WM8915_ADC2R_TO_DSP2TXR_SHIFT 1 /* ADC2R_TO_DSP2TXR */
-#define WM8915_ADC2R_TO_DSP2TXR_WIDTH 1 /* ADC2R_TO_DSP2TXR */
-#define WM8915_DACR_TO_DSP2TXR 0x0001 /* DACR_TO_DSP2TXR */
-#define WM8915_DACR_TO_DSP2TXR_MASK 0x0001 /* DACR_TO_DSP2TXR */
-#define WM8915_DACR_TO_DSP2TXR_SHIFT 0 /* DACR_TO_DSP2TXR */
-#define WM8915_DACR_TO_DSP2TXR_WIDTH 1 /* DACR_TO_DSP2TXR */
-
-/*
- * R1546 (0x60A) - DSP TX Mixer Select
- */
-#define WM8915_DAC_TO_DSPTX_SRC 0x0001 /* DAC_TO_DSPTX_SRC */
-#define WM8915_DAC_TO_DSPTX_SRC_MASK 0x0001 /* DAC_TO_DSPTX_SRC */
-#define WM8915_DAC_TO_DSPTX_SRC_SHIFT 0 /* DAC_TO_DSPTX_SRC */
-#define WM8915_DAC_TO_DSPTX_SRC_WIDTH 1 /* DAC_TO_DSPTX_SRC */
-
-/*
- * R1552 (0x610) - DAC Softmute
- */
-#define WM8915_DAC_SOFTMUTEMODE 0x0002 /* DAC_SOFTMUTEMODE */
-#define WM8915_DAC_SOFTMUTEMODE_MASK 0x0002 /* DAC_SOFTMUTEMODE */
-#define WM8915_DAC_SOFTMUTEMODE_SHIFT 1 /* DAC_SOFTMUTEMODE */
-#define WM8915_DAC_SOFTMUTEMODE_WIDTH 1 /* DAC_SOFTMUTEMODE */
-#define WM8915_DAC_MUTERATE 0x0001 /* DAC_MUTERATE */
-#define WM8915_DAC_MUTERATE_MASK 0x0001 /* DAC_MUTERATE */
-#define WM8915_DAC_MUTERATE_SHIFT 0 /* DAC_MUTERATE */
-#define WM8915_DAC_MUTERATE_WIDTH 1 /* DAC_MUTERATE */
-
-/*
- * R1568 (0x620) - Oversampling
- */
-#define WM8915_SPK_OSR128 0x0008 /* SPK_OSR128 */
-#define WM8915_SPK_OSR128_MASK 0x0008 /* SPK_OSR128 */
-#define WM8915_SPK_OSR128_SHIFT 3 /* SPK_OSR128 */
-#define WM8915_SPK_OSR128_WIDTH 1 /* SPK_OSR128 */
-#define WM8915_DMIC_OSR64 0x0004 /* DMIC_OSR64 */
-#define WM8915_DMIC_OSR64_MASK 0x0004 /* DMIC_OSR64 */
-#define WM8915_DMIC_OSR64_SHIFT 2 /* DMIC_OSR64 */
-#define WM8915_DMIC_OSR64_WIDTH 1 /* DMIC_OSR64 */
-#define WM8915_ADC_OSR128 0x0002 /* ADC_OSR128 */
-#define WM8915_ADC_OSR128_MASK 0x0002 /* ADC_OSR128 */
-#define WM8915_ADC_OSR128_SHIFT 1 /* ADC_OSR128 */
-#define WM8915_ADC_OSR128_WIDTH 1 /* ADC_OSR128 */
-#define WM8915_DAC_OSR128 0x0001 /* DAC_OSR128 */
-#define WM8915_DAC_OSR128_MASK 0x0001 /* DAC_OSR128 */
-#define WM8915_DAC_OSR128_SHIFT 0 /* DAC_OSR128 */
-#define WM8915_DAC_OSR128_WIDTH 1 /* DAC_OSR128 */
-
-/*
- * R1569 (0x621) - Sidetone
- */
-#define WM8915_ST_LPF 0x1000 /* ST_LPF */
-#define WM8915_ST_LPF_MASK 0x1000 /* ST_LPF */
-#define WM8915_ST_LPF_SHIFT 12 /* ST_LPF */
-#define WM8915_ST_LPF_WIDTH 1 /* ST_LPF */
-#define WM8915_ST_HPF_CUT_MASK 0x0380 /* ST_HPF_CUT - [9:7] */
-#define WM8915_ST_HPF_CUT_SHIFT 7 /* ST_HPF_CUT - [9:7] */
-#define WM8915_ST_HPF_CUT_WIDTH 3 /* ST_HPF_CUT - [9:7] */
-#define WM8915_ST_HPF 0x0040 /* ST_HPF */
-#define WM8915_ST_HPF_MASK 0x0040 /* ST_HPF */
-#define WM8915_ST_HPF_SHIFT 6 /* ST_HPF */
-#define WM8915_ST_HPF_WIDTH 1 /* ST_HPF */
-#define WM8915_STR_SEL 0x0002 /* STR_SEL */
-#define WM8915_STR_SEL_MASK 0x0002 /* STR_SEL */
-#define WM8915_STR_SEL_SHIFT 1 /* STR_SEL */
-#define WM8915_STR_SEL_WIDTH 1 /* STR_SEL */
-#define WM8915_STL_SEL 0x0001 /* STL_SEL */
-#define WM8915_STL_SEL_MASK 0x0001 /* STL_SEL */
-#define WM8915_STL_SEL_SHIFT 0 /* STL_SEL */
-#define WM8915_STL_SEL_WIDTH 1 /* STL_SEL */
-
-/*
- * R1792 (0x700) - GPIO 1
- */
-#define WM8915_GP1_DIR 0x8000 /* GP1_DIR */
-#define WM8915_GP1_DIR_MASK 0x8000 /* GP1_DIR */
-#define WM8915_GP1_DIR_SHIFT 15 /* GP1_DIR */
-#define WM8915_GP1_DIR_WIDTH 1 /* GP1_DIR */
-#define WM8915_GP1_PU 0x4000 /* GP1_PU */
-#define WM8915_GP1_PU_MASK 0x4000 /* GP1_PU */
-#define WM8915_GP1_PU_SHIFT 14 /* GP1_PU */
-#define WM8915_GP1_PU_WIDTH 1 /* GP1_PU */
-#define WM8915_GP1_PD 0x2000 /* GP1_PD */
-#define WM8915_GP1_PD_MASK 0x2000 /* GP1_PD */
-#define WM8915_GP1_PD_SHIFT 13 /* GP1_PD */
-#define WM8915_GP1_PD_WIDTH 1 /* GP1_PD */
-#define WM8915_GP1_POL 0x0400 /* GP1_POL */
-#define WM8915_GP1_POL_MASK 0x0400 /* GP1_POL */
-#define WM8915_GP1_POL_SHIFT 10 /* GP1_POL */
-#define WM8915_GP1_POL_WIDTH 1 /* GP1_POL */
-#define WM8915_GP1_OP_CFG 0x0200 /* GP1_OP_CFG */
-#define WM8915_GP1_OP_CFG_MASK 0x0200 /* GP1_OP_CFG */
-#define WM8915_GP1_OP_CFG_SHIFT 9 /* GP1_OP_CFG */
-#define WM8915_GP1_OP_CFG_WIDTH 1 /* GP1_OP_CFG */
-#define WM8915_GP1_DB 0x0100 /* GP1_DB */
-#define WM8915_GP1_DB_MASK 0x0100 /* GP1_DB */
-#define WM8915_GP1_DB_SHIFT 8 /* GP1_DB */
-#define WM8915_GP1_DB_WIDTH 1 /* GP1_DB */
-#define WM8915_GP1_LVL 0x0040 /* GP1_LVL */
-#define WM8915_GP1_LVL_MASK 0x0040 /* GP1_LVL */
-#define WM8915_GP1_LVL_SHIFT 6 /* GP1_LVL */
-#define WM8915_GP1_LVL_WIDTH 1 /* GP1_LVL */
-#define WM8915_GP1_FN_MASK 0x000F /* GP1_FN - [3:0] */
-#define WM8915_GP1_FN_SHIFT 0 /* GP1_FN - [3:0] */
-#define WM8915_GP1_FN_WIDTH 4 /* GP1_FN - [3:0] */
-
-/*
- * R1793 (0x701) - GPIO 2
- */
-#define WM8915_GP2_DIR 0x8000 /* GP2_DIR */
-#define WM8915_GP2_DIR_MASK 0x8000 /* GP2_DIR */
-#define WM8915_GP2_DIR_SHIFT 15 /* GP2_DIR */
-#define WM8915_GP2_DIR_WIDTH 1 /* GP2_DIR */
-#define WM8915_GP2_PU 0x4000 /* GP2_PU */
-#define WM8915_GP2_PU_MASK 0x4000 /* GP2_PU */
-#define WM8915_GP2_PU_SHIFT 14 /* GP2_PU */
-#define WM8915_GP2_PU_WIDTH 1 /* GP2_PU */
-#define WM8915_GP2_PD 0x2000 /* GP2_PD */
-#define WM8915_GP2_PD_MASK 0x2000 /* GP2_PD */
-#define WM8915_GP2_PD_SHIFT 13 /* GP2_PD */
-#define WM8915_GP2_PD_WIDTH 1 /* GP2_PD */
-#define WM8915_GP2_POL 0x0400 /* GP2_POL */
-#define WM8915_GP2_POL_MASK 0x0400 /* GP2_POL */
-#define WM8915_GP2_POL_SHIFT 10 /* GP2_POL */
-#define WM8915_GP2_POL_WIDTH 1 /* GP2_POL */
-#define WM8915_GP2_OP_CFG 0x0200 /* GP2_OP_CFG */
-#define WM8915_GP2_OP_CFG_MASK 0x0200 /* GP2_OP_CFG */
-#define WM8915_GP2_OP_CFG_SHIFT 9 /* GP2_OP_CFG */
-#define WM8915_GP2_OP_CFG_WIDTH 1 /* GP2_OP_CFG */
-#define WM8915_GP2_DB 0x0100 /* GP2_DB */
-#define WM8915_GP2_DB_MASK 0x0100 /* GP2_DB */
-#define WM8915_GP2_DB_SHIFT 8 /* GP2_DB */
-#define WM8915_GP2_DB_WIDTH 1 /* GP2_DB */
-#define WM8915_GP2_LVL 0x0040 /* GP2_LVL */
-#define WM8915_GP2_LVL_MASK 0x0040 /* GP2_LVL */
-#define WM8915_GP2_LVL_SHIFT 6 /* GP2_LVL */
-#define WM8915_GP2_LVL_WIDTH 1 /* GP2_LVL */
-#define WM8915_GP2_FN_MASK 0x000F /* GP2_FN - [3:0] */
-#define WM8915_GP2_FN_SHIFT 0 /* GP2_FN - [3:0] */
-#define WM8915_GP2_FN_WIDTH 4 /* GP2_FN - [3:0] */
-
-/*
- * R1794 (0x702) - GPIO 3
- */
-#define WM8915_GP3_DIR 0x8000 /* GP3_DIR */
-#define WM8915_GP3_DIR_MASK 0x8000 /* GP3_DIR */
-#define WM8915_GP3_DIR_SHIFT 15 /* GP3_DIR */
-#define WM8915_GP3_DIR_WIDTH 1 /* GP3_DIR */
-#define WM8915_GP3_PU 0x4000 /* GP3_PU */
-#define WM8915_GP3_PU_MASK 0x4000 /* GP3_PU */
-#define WM8915_GP3_PU_SHIFT 14 /* GP3_PU */
-#define WM8915_GP3_PU_WIDTH 1 /* GP3_PU */
-#define WM8915_GP3_PD 0x2000 /* GP3_PD */
-#define WM8915_GP3_PD_MASK 0x2000 /* GP3_PD */
-#define WM8915_GP3_PD_SHIFT 13 /* GP3_PD */
-#define WM8915_GP3_PD_WIDTH 1 /* GP3_PD */
-#define WM8915_GP3_POL 0x0400 /* GP3_POL */
-#define WM8915_GP3_POL_MASK 0x0400 /* GP3_POL */
-#define WM8915_GP3_POL_SHIFT 10 /* GP3_POL */
-#define WM8915_GP3_POL_WIDTH 1 /* GP3_POL */
-#define WM8915_GP3_OP_CFG 0x0200 /* GP3_OP_CFG */
-#define WM8915_GP3_OP_CFG_MASK 0x0200 /* GP3_OP_CFG */
-#define WM8915_GP3_OP_CFG_SHIFT 9 /* GP3_OP_CFG */
-#define WM8915_GP3_OP_CFG_WIDTH 1 /* GP3_OP_CFG */
-#define WM8915_GP3_DB 0x0100 /* GP3_DB */
-#define WM8915_GP3_DB_MASK 0x0100 /* GP3_DB */
-#define WM8915_GP3_DB_SHIFT 8 /* GP3_DB */
-#define WM8915_GP3_DB_WIDTH 1 /* GP3_DB */
-#define WM8915_GP3_LVL 0x0040 /* GP3_LVL */
-#define WM8915_GP3_LVL_MASK 0x0040 /* GP3_LVL */
-#define WM8915_GP3_LVL_SHIFT 6 /* GP3_LVL */
-#define WM8915_GP3_LVL_WIDTH 1 /* GP3_LVL */
-#define WM8915_GP3_FN_MASK 0x000F /* GP3_FN - [3:0] */
-#define WM8915_GP3_FN_SHIFT 0 /* GP3_FN - [3:0] */
-#define WM8915_GP3_FN_WIDTH 4 /* GP3_FN - [3:0] */
-
-/*
- * R1795 (0x703) - GPIO 4
- */
-#define WM8915_GP4_DIR 0x8000 /* GP4_DIR */
-#define WM8915_GP4_DIR_MASK 0x8000 /* GP4_DIR */
-#define WM8915_GP4_DIR_SHIFT 15 /* GP4_DIR */
-#define WM8915_GP4_DIR_WIDTH 1 /* GP4_DIR */
-#define WM8915_GP4_PU 0x4000 /* GP4_PU */
-#define WM8915_GP4_PU_MASK 0x4000 /* GP4_PU */
-#define WM8915_GP4_PU_SHIFT 14 /* GP4_PU */
-#define WM8915_GP4_PU_WIDTH 1 /* GP4_PU */
-#define WM8915_GP4_PD 0x2000 /* GP4_PD */
-#define WM8915_GP4_PD_MASK 0x2000 /* GP4_PD */
-#define WM8915_GP4_PD_SHIFT 13 /* GP4_PD */
-#define WM8915_GP4_PD_WIDTH 1 /* GP4_PD */
-#define WM8915_GP4_POL 0x0400 /* GP4_POL */
-#define WM8915_GP4_POL_MASK 0x0400 /* GP4_POL */
-#define WM8915_GP4_POL_SHIFT 10 /* GP4_POL */
-#define WM8915_GP4_POL_WIDTH 1 /* GP4_POL */
-#define WM8915_GP4_OP_CFG 0x0200 /* GP4_OP_CFG */
-#define WM8915_GP4_OP_CFG_MASK 0x0200 /* GP4_OP_CFG */
-#define WM8915_GP4_OP_CFG_SHIFT 9 /* GP4_OP_CFG */
-#define WM8915_GP4_OP_CFG_WIDTH 1 /* GP4_OP_CFG */
-#define WM8915_GP4_DB 0x0100 /* GP4_DB */
-#define WM8915_GP4_DB_MASK 0x0100 /* GP4_DB */
-#define WM8915_GP4_DB_SHIFT 8 /* GP4_DB */
-#define WM8915_GP4_DB_WIDTH 1 /* GP4_DB */
-#define WM8915_GP4_LVL 0x0040 /* GP4_LVL */
-#define WM8915_GP4_LVL_MASK 0x0040 /* GP4_LVL */
-#define WM8915_GP4_LVL_SHIFT 6 /* GP4_LVL */
-#define WM8915_GP4_LVL_WIDTH 1 /* GP4_LVL */
-#define WM8915_GP4_FN_MASK 0x000F /* GP4_FN - [3:0] */
-#define WM8915_GP4_FN_SHIFT 0 /* GP4_FN - [3:0] */
-#define WM8915_GP4_FN_WIDTH 4 /* GP4_FN - [3:0] */
-
-/*
- * R1796 (0x704) - GPIO 5
- */
-#define WM8915_GP5_DIR 0x8000 /* GP5_DIR */
-#define WM8915_GP5_DIR_MASK 0x8000 /* GP5_DIR */
-#define WM8915_GP5_DIR_SHIFT 15 /* GP5_DIR */
-#define WM8915_GP5_DIR_WIDTH 1 /* GP5_DIR */
-#define WM8915_GP5_PU 0x4000 /* GP5_PU */
-#define WM8915_GP5_PU_MASK 0x4000 /* GP5_PU */
-#define WM8915_GP5_PU_SHIFT 14 /* GP5_PU */
-#define WM8915_GP5_PU_WIDTH 1 /* GP5_PU */
-#define WM8915_GP5_PD 0x2000 /* GP5_PD */
-#define WM8915_GP5_PD_MASK 0x2000 /* GP5_PD */
-#define WM8915_GP5_PD_SHIFT 13 /* GP5_PD */
-#define WM8915_GP5_PD_WIDTH 1 /* GP5_PD */
-#define WM8915_GP5_POL 0x0400 /* GP5_POL */
-#define WM8915_GP5_POL_MASK 0x0400 /* GP5_POL */
-#define WM8915_GP5_POL_SHIFT 10 /* GP5_POL */
-#define WM8915_GP5_POL_WIDTH 1 /* GP5_POL */
-#define WM8915_GP5_OP_CFG 0x0200 /* GP5_OP_CFG */
-#define WM8915_GP5_OP_CFG_MASK 0x0200 /* GP5_OP_CFG */
-#define WM8915_GP5_OP_CFG_SHIFT 9 /* GP5_OP_CFG */
-#define WM8915_GP5_OP_CFG_WIDTH 1 /* GP5_OP_CFG */
-#define WM8915_GP5_DB 0x0100 /* GP5_DB */
-#define WM8915_GP5_DB_MASK 0x0100 /* GP5_DB */
-#define WM8915_GP5_DB_SHIFT 8 /* GP5_DB */
-#define WM8915_GP5_DB_WIDTH 1 /* GP5_DB */
-#define WM8915_GP5_LVL 0x0040 /* GP5_LVL */
-#define WM8915_GP5_LVL_MASK 0x0040 /* GP5_LVL */
-#define WM8915_GP5_LVL_SHIFT 6 /* GP5_LVL */
-#define WM8915_GP5_LVL_WIDTH 1 /* GP5_LVL */
-#define WM8915_GP5_FN_MASK 0x000F /* GP5_FN - [3:0] */
-#define WM8915_GP5_FN_SHIFT 0 /* GP5_FN - [3:0] */
-#define WM8915_GP5_FN_WIDTH 4 /* GP5_FN - [3:0] */
-
-/*
- * R1824 (0x720) - Pull Control (1)
- */
-#define WM8915_DMICDAT2_PD 0x1000 /* DMICDAT2_PD */
-#define WM8915_DMICDAT2_PD_MASK 0x1000 /* DMICDAT2_PD */
-#define WM8915_DMICDAT2_PD_SHIFT 12 /* DMICDAT2_PD */
-#define WM8915_DMICDAT2_PD_WIDTH 1 /* DMICDAT2_PD */
-#define WM8915_DMICDAT1_PD 0x0400 /* DMICDAT1_PD */
-#define WM8915_DMICDAT1_PD_MASK 0x0400 /* DMICDAT1_PD */
-#define WM8915_DMICDAT1_PD_SHIFT 10 /* DMICDAT1_PD */
-#define WM8915_DMICDAT1_PD_WIDTH 1 /* DMICDAT1_PD */
-#define WM8915_MCLK2_PU 0x0200 /* MCLK2_PU */
-#define WM8915_MCLK2_PU_MASK 0x0200 /* MCLK2_PU */
-#define WM8915_MCLK2_PU_SHIFT 9 /* MCLK2_PU */
-#define WM8915_MCLK2_PU_WIDTH 1 /* MCLK2_PU */
-#define WM8915_MCLK2_PD 0x0100 /* MCLK2_PD */
-#define WM8915_MCLK2_PD_MASK 0x0100 /* MCLK2_PD */
-#define WM8915_MCLK2_PD_SHIFT 8 /* MCLK2_PD */
-#define WM8915_MCLK2_PD_WIDTH 1 /* MCLK2_PD */
-#define WM8915_MCLK1_PU 0x0080 /* MCLK1_PU */
-#define WM8915_MCLK1_PU_MASK 0x0080 /* MCLK1_PU */
-#define WM8915_MCLK1_PU_SHIFT 7 /* MCLK1_PU */
-#define WM8915_MCLK1_PU_WIDTH 1 /* MCLK1_PU */
-#define WM8915_MCLK1_PD 0x0040 /* MCLK1_PD */
-#define WM8915_MCLK1_PD_MASK 0x0040 /* MCLK1_PD */
-#define WM8915_MCLK1_PD_SHIFT 6 /* MCLK1_PD */
-#define WM8915_MCLK1_PD_WIDTH 1 /* MCLK1_PD */
-#define WM8915_DACDAT1_PU 0x0020 /* DACDAT1_PU */
-#define WM8915_DACDAT1_PU_MASK 0x0020 /* DACDAT1_PU */
-#define WM8915_DACDAT1_PU_SHIFT 5 /* DACDAT1_PU */
-#define WM8915_DACDAT1_PU_WIDTH 1 /* DACDAT1_PU */
-#define WM8915_DACDAT1_PD 0x0010 /* DACDAT1_PD */
-#define WM8915_DACDAT1_PD_MASK 0x0010 /* DACDAT1_PD */
-#define WM8915_DACDAT1_PD_SHIFT 4 /* DACDAT1_PD */
-#define WM8915_DACDAT1_PD_WIDTH 1 /* DACDAT1_PD */
-#define WM8915_DACLRCLK1_PU 0x0008 /* DACLRCLK1_PU */
-#define WM8915_DACLRCLK1_PU_MASK 0x0008 /* DACLRCLK1_PU */
-#define WM8915_DACLRCLK1_PU_SHIFT 3 /* DACLRCLK1_PU */
-#define WM8915_DACLRCLK1_PU_WIDTH 1 /* DACLRCLK1_PU */
-#define WM8915_DACLRCLK1_PD 0x0004 /* DACLRCLK1_PD */
-#define WM8915_DACLRCLK1_PD_MASK 0x0004 /* DACLRCLK1_PD */
-#define WM8915_DACLRCLK1_PD_SHIFT 2 /* DACLRCLK1_PD */
-#define WM8915_DACLRCLK1_PD_WIDTH 1 /* DACLRCLK1_PD */
-#define WM8915_BCLK1_PU 0x0002 /* BCLK1_PU */
-#define WM8915_BCLK1_PU_MASK 0x0002 /* BCLK1_PU */
-#define WM8915_BCLK1_PU_SHIFT 1 /* BCLK1_PU */
-#define WM8915_BCLK1_PU_WIDTH 1 /* BCLK1_PU */
-#define WM8915_BCLK1_PD 0x0001 /* BCLK1_PD */
-#define WM8915_BCLK1_PD_MASK 0x0001 /* BCLK1_PD */
-#define WM8915_BCLK1_PD_SHIFT 0 /* BCLK1_PD */
-#define WM8915_BCLK1_PD_WIDTH 1 /* BCLK1_PD */
-
-/*
- * R1825 (0x721) - Pull Control (2)
- */
-#define WM8915_LDO1ENA_PD 0x0100 /* LDO1ENA_PD */
-#define WM8915_LDO1ENA_PD_MASK 0x0100 /* LDO1ENA_PD */
-#define WM8915_LDO1ENA_PD_SHIFT 8 /* LDO1ENA_PD */
-#define WM8915_LDO1ENA_PD_WIDTH 1 /* LDO1ENA_PD */
-#define WM8915_ADDR_PD 0x0040 /* ADDR_PD */
-#define WM8915_ADDR_PD_MASK 0x0040 /* ADDR_PD */
-#define WM8915_ADDR_PD_SHIFT 6 /* ADDR_PD */
-#define WM8915_ADDR_PD_WIDTH 1 /* ADDR_PD */
-#define WM8915_DACDAT2_PU 0x0020 /* DACDAT2_PU */
-#define WM8915_DACDAT2_PU_MASK 0x0020 /* DACDAT2_PU */
-#define WM8915_DACDAT2_PU_SHIFT 5 /* DACDAT2_PU */
-#define WM8915_DACDAT2_PU_WIDTH 1 /* DACDAT2_PU */
-#define WM8915_DACDAT2_PD 0x0010 /* DACDAT2_PD */
-#define WM8915_DACDAT2_PD_MASK 0x0010 /* DACDAT2_PD */
-#define WM8915_DACDAT2_PD_SHIFT 4 /* DACDAT2_PD */
-#define WM8915_DACDAT2_PD_WIDTH 1 /* DACDAT2_PD */
-#define WM8915_DACLRCLK2_PU 0x0008 /* DACLRCLK2_PU */
-#define WM8915_DACLRCLK2_PU_MASK 0x0008 /* DACLRCLK2_PU */
-#define WM8915_DACLRCLK2_PU_SHIFT 3 /* DACLRCLK2_PU */
-#define WM8915_DACLRCLK2_PU_WIDTH 1 /* DACLRCLK2_PU */
-#define WM8915_DACLRCLK2_PD 0x0004 /* DACLRCLK2_PD */
-#define WM8915_DACLRCLK2_PD_MASK 0x0004 /* DACLRCLK2_PD */
-#define WM8915_DACLRCLK2_PD_SHIFT 2 /* DACLRCLK2_PD */
-#define WM8915_DACLRCLK2_PD_WIDTH 1 /* DACLRCLK2_PD */
-#define WM8915_BCLK2_PU 0x0002 /* BCLK2_PU */
-#define WM8915_BCLK2_PU_MASK 0x0002 /* BCLK2_PU */
-#define WM8915_BCLK2_PU_SHIFT 1 /* BCLK2_PU */
-#define WM8915_BCLK2_PU_WIDTH 1 /* BCLK2_PU */
-#define WM8915_BCLK2_PD 0x0001 /* BCLK2_PD */
-#define WM8915_BCLK2_PD_MASK 0x0001 /* BCLK2_PD */
-#define WM8915_BCLK2_PD_SHIFT 0 /* BCLK2_PD */
-#define WM8915_BCLK2_PD_WIDTH 1 /* BCLK2_PD */
-
-/*
- * R1840 (0x730) - Interrupt Status 1
- */
-#define WM8915_GP5_EINT 0x0010 /* GP5_EINT */
-#define WM8915_GP5_EINT_MASK 0x0010 /* GP5_EINT */
-#define WM8915_GP5_EINT_SHIFT 4 /* GP5_EINT */
-#define WM8915_GP5_EINT_WIDTH 1 /* GP5_EINT */
-#define WM8915_GP4_EINT 0x0008 /* GP4_EINT */
-#define WM8915_GP4_EINT_MASK 0x0008 /* GP4_EINT */
-#define WM8915_GP4_EINT_SHIFT 3 /* GP4_EINT */
-#define WM8915_GP4_EINT_WIDTH 1 /* GP4_EINT */
-#define WM8915_GP3_EINT 0x0004 /* GP3_EINT */
-#define WM8915_GP3_EINT_MASK 0x0004 /* GP3_EINT */
-#define WM8915_GP3_EINT_SHIFT 2 /* GP3_EINT */
-#define WM8915_GP3_EINT_WIDTH 1 /* GP3_EINT */
-#define WM8915_GP2_EINT 0x0002 /* GP2_EINT */
-#define WM8915_GP2_EINT_MASK 0x0002 /* GP2_EINT */
-#define WM8915_GP2_EINT_SHIFT 1 /* GP2_EINT */
-#define WM8915_GP2_EINT_WIDTH 1 /* GP2_EINT */
-#define WM8915_GP1_EINT 0x0001 /* GP1_EINT */
-#define WM8915_GP1_EINT_MASK 0x0001 /* GP1_EINT */
-#define WM8915_GP1_EINT_SHIFT 0 /* GP1_EINT */
-#define WM8915_GP1_EINT_WIDTH 1 /* GP1_EINT */
-
-/*
- * R1841 (0x731) - Interrupt Status 2
- */
-#define WM8915_DCS_DONE_23_EINT 0x1000 /* DCS_DONE_23_EINT */
-#define WM8915_DCS_DONE_23_EINT_MASK 0x1000 /* DCS_DONE_23_EINT */
-#define WM8915_DCS_DONE_23_EINT_SHIFT 12 /* DCS_DONE_23_EINT */
-#define WM8915_DCS_DONE_23_EINT_WIDTH 1 /* DCS_DONE_23_EINT */
-#define WM8915_DCS_DONE_01_EINT 0x0800 /* DCS_DONE_01_EINT */
-#define WM8915_DCS_DONE_01_EINT_MASK 0x0800 /* DCS_DONE_01_EINT */
-#define WM8915_DCS_DONE_01_EINT_SHIFT 11 /* DCS_DONE_01_EINT */
-#define WM8915_DCS_DONE_01_EINT_WIDTH 1 /* DCS_DONE_01_EINT */
-#define WM8915_WSEQ_DONE_EINT 0x0400 /* WSEQ_DONE_EINT */
-#define WM8915_WSEQ_DONE_EINT_MASK 0x0400 /* WSEQ_DONE_EINT */
-#define WM8915_WSEQ_DONE_EINT_SHIFT 10 /* WSEQ_DONE_EINT */
-#define WM8915_WSEQ_DONE_EINT_WIDTH 1 /* WSEQ_DONE_EINT */
-#define WM8915_FIFOS_ERR_EINT 0x0200 /* FIFOS_ERR_EINT */
-#define WM8915_FIFOS_ERR_EINT_MASK 0x0200 /* FIFOS_ERR_EINT */
-#define WM8915_FIFOS_ERR_EINT_SHIFT 9 /* FIFOS_ERR_EINT */
-#define WM8915_FIFOS_ERR_EINT_WIDTH 1 /* FIFOS_ERR_EINT */
-#define WM8915_DSP2DRC_SIG_DET_EINT 0x0080 /* DSP2DRC_SIG_DET_EINT */
-#define WM8915_DSP2DRC_SIG_DET_EINT_MASK 0x0080 /* DSP2DRC_SIG_DET_EINT */
-#define WM8915_DSP2DRC_SIG_DET_EINT_SHIFT 7 /* DSP2DRC_SIG_DET_EINT */
-#define WM8915_DSP2DRC_SIG_DET_EINT_WIDTH 1 /* DSP2DRC_SIG_DET_EINT */
-#define WM8915_DSP1DRC_SIG_DET_EINT 0x0040 /* DSP1DRC_SIG_DET_EINT */
-#define WM8915_DSP1DRC_SIG_DET_EINT_MASK 0x0040 /* DSP1DRC_SIG_DET_EINT */
-#define WM8915_DSP1DRC_SIG_DET_EINT_SHIFT 6 /* DSP1DRC_SIG_DET_EINT */
-#define WM8915_DSP1DRC_SIG_DET_EINT_WIDTH 1 /* DSP1DRC_SIG_DET_EINT */
-#define WM8915_FLL_SW_CLK_DONE_EINT 0x0008 /* FLL_SW_CLK_DONE_EINT */
-#define WM8915_FLL_SW_CLK_DONE_EINT_MASK 0x0008 /* FLL_SW_CLK_DONE_EINT */
-#define WM8915_FLL_SW_CLK_DONE_EINT_SHIFT 3 /* FLL_SW_CLK_DONE_EINT */
-#define WM8915_FLL_SW_CLK_DONE_EINT_WIDTH 1 /* FLL_SW_CLK_DONE_EINT */
-#define WM8915_FLL_LOCK_EINT 0x0004 /* FLL_LOCK_EINT */
-#define WM8915_FLL_LOCK_EINT_MASK 0x0004 /* FLL_LOCK_EINT */
-#define WM8915_FLL_LOCK_EINT_SHIFT 2 /* FLL_LOCK_EINT */
-#define WM8915_FLL_LOCK_EINT_WIDTH 1 /* FLL_LOCK_EINT */
-#define WM8915_HP_DONE_EINT 0x0002 /* HP_DONE_EINT */
-#define WM8915_HP_DONE_EINT_MASK 0x0002 /* HP_DONE_EINT */
-#define WM8915_HP_DONE_EINT_SHIFT 1 /* HP_DONE_EINT */
-#define WM8915_HP_DONE_EINT_WIDTH 1 /* HP_DONE_EINT */
-#define WM8915_MICD_EINT 0x0001 /* MICD_EINT */
-#define WM8915_MICD_EINT_MASK 0x0001 /* MICD_EINT */
-#define WM8915_MICD_EINT_SHIFT 0 /* MICD_EINT */
-#define WM8915_MICD_EINT_WIDTH 1 /* MICD_EINT */
-
-/*
- * R1842 (0x732) - Interrupt Raw Status 2
- */
-#define WM8915_DCS_DONE_23_STS 0x1000 /* DCS_DONE_23_STS */
-#define WM8915_DCS_DONE_23_STS_MASK 0x1000 /* DCS_DONE_23_STS */
-#define WM8915_DCS_DONE_23_STS_SHIFT 12 /* DCS_DONE_23_STS */
-#define WM8915_DCS_DONE_23_STS_WIDTH 1 /* DCS_DONE_23_STS */
-#define WM8915_DCS_DONE_01_STS 0x0800 /* DCS_DONE_01_STS */
-#define WM8915_DCS_DONE_01_STS_MASK 0x0800 /* DCS_DONE_01_STS */
-#define WM8915_DCS_DONE_01_STS_SHIFT 11 /* DCS_DONE_01_STS */
-#define WM8915_DCS_DONE_01_STS_WIDTH 1 /* DCS_DONE_01_STS */
-#define WM8915_WSEQ_DONE_STS 0x0400 /* WSEQ_DONE_STS */
-#define WM8915_WSEQ_DONE_STS_MASK 0x0400 /* WSEQ_DONE_STS */
-#define WM8915_WSEQ_DONE_STS_SHIFT 10 /* WSEQ_DONE_STS */
-#define WM8915_WSEQ_DONE_STS_WIDTH 1 /* WSEQ_DONE_STS */
-#define WM8915_FIFOS_ERR_STS 0x0200 /* FIFOS_ERR_STS */
-#define WM8915_FIFOS_ERR_STS_MASK 0x0200 /* FIFOS_ERR_STS */
-#define WM8915_FIFOS_ERR_STS_SHIFT 9 /* FIFOS_ERR_STS */
-#define WM8915_FIFOS_ERR_STS_WIDTH 1 /* FIFOS_ERR_STS */
-#define WM8915_DSP2DRC_SIG_DET_STS 0x0080 /* DSP2DRC_SIG_DET_STS */
-#define WM8915_DSP2DRC_SIG_DET_STS_MASK 0x0080 /* DSP2DRC_SIG_DET_STS */
-#define WM8915_DSP2DRC_SIG_DET_STS_SHIFT 7 /* DSP2DRC_SIG_DET_STS */
-#define WM8915_DSP2DRC_SIG_DET_STS_WIDTH 1 /* DSP2DRC_SIG_DET_STS */
-#define WM8915_DSP1DRC_SIG_DET_STS 0x0040 /* DSP1DRC_SIG_DET_STS */
-#define WM8915_DSP1DRC_SIG_DET_STS_MASK 0x0040 /* DSP1DRC_SIG_DET_STS */
-#define WM8915_DSP1DRC_SIG_DET_STS_SHIFT 6 /* DSP1DRC_SIG_DET_STS */
-#define WM8915_DSP1DRC_SIG_DET_STS_WIDTH 1 /* DSP1DRC_SIG_DET_STS */
-#define WM8915_FLL_LOCK_STS 0x0004 /* FLL_LOCK_STS */
-#define WM8915_FLL_LOCK_STS_MASK 0x0004 /* FLL_LOCK_STS */
-#define WM8915_FLL_LOCK_STS_SHIFT 2 /* FLL_LOCK_STS */
-#define WM8915_FLL_LOCK_STS_WIDTH 1 /* FLL_LOCK_STS */
-
-/*
- * R1848 (0x738) - Interrupt Status 1 Mask
- */
-#define WM8915_IM_GP5_EINT 0x0010 /* IM_GP5_EINT */
-#define WM8915_IM_GP5_EINT_MASK 0x0010 /* IM_GP5_EINT */
-#define WM8915_IM_GP5_EINT_SHIFT 4 /* IM_GP5_EINT */
-#define WM8915_IM_GP5_EINT_WIDTH 1 /* IM_GP5_EINT */
-#define WM8915_IM_GP4_EINT 0x0008 /* IM_GP4_EINT */
-#define WM8915_IM_GP4_EINT_MASK 0x0008 /* IM_GP4_EINT */
-#define WM8915_IM_GP4_EINT_SHIFT 3 /* IM_GP4_EINT */
-#define WM8915_IM_GP4_EINT_WIDTH 1 /* IM_GP4_EINT */
-#define WM8915_IM_GP3_EINT 0x0004 /* IM_GP3_EINT */
-#define WM8915_IM_GP3_EINT_MASK 0x0004 /* IM_GP3_EINT */
-#define WM8915_IM_GP3_EINT_SHIFT 2 /* IM_GP3_EINT */
-#define WM8915_IM_GP3_EINT_WIDTH 1 /* IM_GP3_EINT */
-#define WM8915_IM_GP2_EINT 0x0002 /* IM_GP2_EINT */
-#define WM8915_IM_GP2_EINT_MASK 0x0002 /* IM_GP2_EINT */
-#define WM8915_IM_GP2_EINT_SHIFT 1 /* IM_GP2_EINT */
-#define WM8915_IM_GP2_EINT_WIDTH 1 /* IM_GP2_EINT */
-#define WM8915_IM_GP1_EINT 0x0001 /* IM_GP1_EINT */
-#define WM8915_IM_GP1_EINT_MASK 0x0001 /* IM_GP1_EINT */
-#define WM8915_IM_GP1_EINT_SHIFT 0 /* IM_GP1_EINT */
-#define WM8915_IM_GP1_EINT_WIDTH 1 /* IM_GP1_EINT */
-
-/*
- * R1849 (0x739) - Interrupt Status 2 Mask
- */
-#define WM8915_IM_DCS_DONE_23_EINT 0x1000 /* IM_DCS_DONE_23_EINT */
-#define WM8915_IM_DCS_DONE_23_EINT_MASK 0x1000 /* IM_DCS_DONE_23_EINT */
-#define WM8915_IM_DCS_DONE_23_EINT_SHIFT 12 /* IM_DCS_DONE_23_EINT */
-#define WM8915_IM_DCS_DONE_23_EINT_WIDTH 1 /* IM_DCS_DONE_23_EINT */
-#define WM8915_IM_DCS_DONE_01_EINT 0x0800 /* IM_DCS_DONE_01_EINT */
-#define WM8915_IM_DCS_DONE_01_EINT_MASK 0x0800 /* IM_DCS_DONE_01_EINT */
-#define WM8915_IM_DCS_DONE_01_EINT_SHIFT 11 /* IM_DCS_DONE_01_EINT */
-#define WM8915_IM_DCS_DONE_01_EINT_WIDTH 1 /* IM_DCS_DONE_01_EINT */
-#define WM8915_IM_WSEQ_DONE_EINT 0x0400 /* IM_WSEQ_DONE_EINT */
-#define WM8915_IM_WSEQ_DONE_EINT_MASK 0x0400 /* IM_WSEQ_DONE_EINT */
-#define WM8915_IM_WSEQ_DONE_EINT_SHIFT 10 /* IM_WSEQ_DONE_EINT */
-#define WM8915_IM_WSEQ_DONE_EINT_WIDTH 1 /* IM_WSEQ_DONE_EINT */
-#define WM8915_IM_FIFOS_ERR_EINT 0x0200 /* IM_FIFOS_ERR_EINT */
-#define WM8915_IM_FIFOS_ERR_EINT_MASK 0x0200 /* IM_FIFOS_ERR_EINT */
-#define WM8915_IM_FIFOS_ERR_EINT_SHIFT 9 /* IM_FIFOS_ERR_EINT */
-#define WM8915_IM_FIFOS_ERR_EINT_WIDTH 1 /* IM_FIFOS_ERR_EINT */
-#define WM8915_IM_DSP2DRC_SIG_DET_EINT 0x0080 /* IM_DSP2DRC_SIG_DET_EINT */
-#define WM8915_IM_DSP2DRC_SIG_DET_EINT_MASK 0x0080 /* IM_DSP2DRC_SIG_DET_EINT */
-#define WM8915_IM_DSP2DRC_SIG_DET_EINT_SHIFT 7 /* IM_DSP2DRC_SIG_DET_EINT */
-#define WM8915_IM_DSP2DRC_SIG_DET_EINT_WIDTH 1 /* IM_DSP2DRC_SIG_DET_EINT */
-#define WM8915_IM_DSP1DRC_SIG_DET_EINT 0x0040 /* IM_DSP1DRC_SIG_DET_EINT */
-#define WM8915_IM_DSP1DRC_SIG_DET_EINT_MASK 0x0040 /* IM_DSP1DRC_SIG_DET_EINT */
-#define WM8915_IM_DSP1DRC_SIG_DET_EINT_SHIFT 6 /* IM_DSP1DRC_SIG_DET_EINT */
-#define WM8915_IM_DSP1DRC_SIG_DET_EINT_WIDTH 1 /* IM_DSP1DRC_SIG_DET_EINT */
-#define WM8915_IM_FLL_SW_CLK_DONE_EINT 0x0008 /* IM_FLL_SW_CLK_DONE_EINT */
-#define WM8915_IM_FLL_SW_CLK_DONE_EINT_MASK 0x0008 /* IM_FLL_SW_CLK_DONE_EINT */
-#define WM8915_IM_FLL_SW_CLK_DONE_EINT_SHIFT 3 /* IM_FLL_SW_CLK_DONE_EINT */
-#define WM8915_IM_FLL_SW_CLK_DONE_EINT_WIDTH 1 /* IM_FLL_SW_CLK_DONE_EINT */
-#define WM8915_IM_FLL_LOCK_EINT 0x0004 /* IM_FLL_LOCK_EINT */
-#define WM8915_IM_FLL_LOCK_EINT_MASK 0x0004 /* IM_FLL_LOCK_EINT */
-#define WM8915_IM_FLL_LOCK_EINT_SHIFT 2 /* IM_FLL_LOCK_EINT */
-#define WM8915_IM_FLL_LOCK_EINT_WIDTH 1 /* IM_FLL_LOCK_EINT */
-#define WM8915_IM_HP_DONE_EINT 0x0002 /* IM_HP_DONE_EINT */
-#define WM8915_IM_HP_DONE_EINT_MASK 0x0002 /* IM_HP_DONE_EINT */
-#define WM8915_IM_HP_DONE_EINT_SHIFT 1 /* IM_HP_DONE_EINT */
-#define WM8915_IM_HP_DONE_EINT_WIDTH 1 /* IM_HP_DONE_EINT */
-#define WM8915_IM_MICD_EINT 0x0001 /* IM_MICD_EINT */
-#define WM8915_IM_MICD_EINT_MASK 0x0001 /* IM_MICD_EINT */
-#define WM8915_IM_MICD_EINT_SHIFT 0 /* IM_MICD_EINT */
-#define WM8915_IM_MICD_EINT_WIDTH 1 /* IM_MICD_EINT */
-
-/*
- * R1856 (0x740) - Interrupt Control
- */
-#define WM8915_IM_IRQ 0x0001 /* IM_IRQ */
-#define WM8915_IM_IRQ_MASK 0x0001 /* IM_IRQ */
-#define WM8915_IM_IRQ_SHIFT 0 /* IM_IRQ */
-#define WM8915_IM_IRQ_WIDTH 1 /* IM_IRQ */
-
-/*
- * R2048 (0x800) - Left PDM Speaker
- */
-#define WM8915_SPKL_ENA 0x0010 /* SPKL_ENA */
-#define WM8915_SPKL_ENA_MASK 0x0010 /* SPKL_ENA */
-#define WM8915_SPKL_ENA_SHIFT 4 /* SPKL_ENA */
-#define WM8915_SPKL_ENA_WIDTH 1 /* SPKL_ENA */
-#define WM8915_SPKL_MUTE 0x0008 /* SPKL_MUTE */
-#define WM8915_SPKL_MUTE_MASK 0x0008 /* SPKL_MUTE */
-#define WM8915_SPKL_MUTE_SHIFT 3 /* SPKL_MUTE */
-#define WM8915_SPKL_MUTE_WIDTH 1 /* SPKL_MUTE */
-#define WM8915_SPKL_MUTE_ZC 0x0004 /* SPKL_MUTE_ZC */
-#define WM8915_SPKL_MUTE_ZC_MASK 0x0004 /* SPKL_MUTE_ZC */
-#define WM8915_SPKL_MUTE_ZC_SHIFT 2 /* SPKL_MUTE_ZC */
-#define WM8915_SPKL_MUTE_ZC_WIDTH 1 /* SPKL_MUTE_ZC */
-#define WM8915_SPKL_SRC_MASK 0x0003 /* SPKL_SRC - [1:0] */
-#define WM8915_SPKL_SRC_SHIFT 0 /* SPKL_SRC - [1:0] */
-#define WM8915_SPKL_SRC_WIDTH 2 /* SPKL_SRC - [1:0] */
-
-/*
- * R2049 (0x801) - Right PDM Speaker
- */
-#define WM8915_SPKR_ENA 0x0010 /* SPKR_ENA */
-#define WM8915_SPKR_ENA_MASK 0x0010 /* SPKR_ENA */
-#define WM8915_SPKR_ENA_SHIFT 4 /* SPKR_ENA */
-#define WM8915_SPKR_ENA_WIDTH 1 /* SPKR_ENA */
-#define WM8915_SPKR_MUTE 0x0008 /* SPKR_MUTE */
-#define WM8915_SPKR_MUTE_MASK 0x0008 /* SPKR_MUTE */
-#define WM8915_SPKR_MUTE_SHIFT 3 /* SPKR_MUTE */
-#define WM8915_SPKR_MUTE_WIDTH 1 /* SPKR_MUTE */
-#define WM8915_SPKR_MUTE_ZC 0x0004 /* SPKR_MUTE_ZC */
-#define WM8915_SPKR_MUTE_ZC_MASK 0x0004 /* SPKR_MUTE_ZC */
-#define WM8915_SPKR_MUTE_ZC_SHIFT 2 /* SPKR_MUTE_ZC */
-#define WM8915_SPKR_MUTE_ZC_WIDTH 1 /* SPKR_MUTE_ZC */
-#define WM8915_SPKR_SRC_MASK 0x0003 /* SPKR_SRC - [1:0] */
-#define WM8915_SPKR_SRC_SHIFT 0 /* SPKR_SRC - [1:0] */
-#define WM8915_SPKR_SRC_WIDTH 2 /* SPKR_SRC - [1:0] */
-
-/*
- * R2050 (0x802) - PDM Speaker Mute Sequence
- */
-#define WM8915_SPK_MUTE_ENDIAN 0x0100 /* SPK_MUTE_ENDIAN */
-#define WM8915_SPK_MUTE_ENDIAN_MASK 0x0100 /* SPK_MUTE_ENDIAN */
-#define WM8915_SPK_MUTE_ENDIAN_SHIFT 8 /* SPK_MUTE_ENDIAN */
-#define WM8915_SPK_MUTE_ENDIAN_WIDTH 1 /* SPK_MUTE_ENDIAN */
-#define WM8915_SPK_MUTE_SEQ1_MASK 0x00FF /* SPK_MUTE_SEQ1 - [7:0] */
-#define WM8915_SPK_MUTE_SEQ1_SHIFT 0 /* SPK_MUTE_SEQ1 - [7:0] */
-#define WM8915_SPK_MUTE_SEQ1_WIDTH 8 /* SPK_MUTE_SEQ1 - [7:0] */
-
-/*
- * R2051 (0x803) - PDM Speaker Volume
- */
-#define WM8915_SPKR_VOL_MASK 0x00F0 /* SPKR_VOL - [7:4] */
-#define WM8915_SPKR_VOL_SHIFT 4 /* SPKR_VOL - [7:4] */
-#define WM8915_SPKR_VOL_WIDTH 4 /* SPKR_VOL - [7:4] */
-#define WM8915_SPKL_VOL_MASK 0x000F /* SPKL_VOL - [3:0] */
-#define WM8915_SPKL_VOL_SHIFT 0 /* SPKL_VOL - [3:0] */
-#define WM8915_SPKL_VOL_WIDTH 4 /* SPKL_VOL - [3:0] */
-
-#endif
diff --git a/sound/soc/codecs/wm8994.c b/sound/soc/codecs/wm8994.c
index 09e680a..b393f9f 100644
--- a/sound/soc/codecs/wm8994.c
+++ b/sound/soc/codecs/wm8994.c
@@ -2981,6 +2981,7 @@
wm8994->hubs.dcs_readback_mode = 1;
break;
}
+ break;
case WM8958:
wm8994->hubs.dcs_readback_mode = 1;
diff --git a/sound/soc/codecs/wm8996.c b/sound/soc/codecs/wm8996.c
new file mode 100644
index 0000000..ab8e9d1
--- /dev/null
+++ b/sound/soc/codecs/wm8996.c
@@ -0,0 +1,2994 @@
+/*
+ * wm8996.c - WM8996 audio codec interface
+ *
+ * Copyright 2011 Wolfson Microelectronics PLC.
+ * Author: Mark Brown <broonie@opensource.wolfsonmicro.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/completion.h>
+#include <linux/delay.h>
+#include <linux/pm.h>
+#include <linux/gcd.h>
+#include <linux/gpio.h>
+#include <linux/i2c.h>
+#include <linux/regulator/consumer.h>
+#include <linux/slab.h>
+#include <linux/workqueue.h>
+#include <sound/core.h>
+#include <sound/jack.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/initval.h>
+#include <sound/tlv.h>
+#include <trace/events/asoc.h>
+
+#include <sound/wm8996.h>
+#include "wm8996.h"
+
+#define WM8996_AIFS 2
+
+#define HPOUT1L 1
+#define HPOUT1R 2
+#define HPOUT2L 4
+#define HPOUT2R 8
+
+#define WM8996_NUM_SUPPLIES 4
+static const char *wm8996_supply_names[WM8996_NUM_SUPPLIES] = {
+ "DBVDD",
+ "AVDD1",
+ "AVDD2",
+ "CPVDD",
+};
+
+struct wm8996_priv {
+ struct snd_soc_codec *codec;
+
+ int ldo1ena;
+
+ int sysclk;
+ int sysclk_src;
+
+ int fll_src;
+ int fll_fref;
+ int fll_fout;
+
+ struct completion fll_lock;
+
+ u16 dcs_pending;
+ struct completion dcs_done;
+
+ u16 hpout_ena;
+ u16 hpout_pending;
+
+ struct regulator_bulk_data supplies[WM8996_NUM_SUPPLIES];
+ struct notifier_block disable_nb[WM8996_NUM_SUPPLIES];
+
+ struct wm8996_pdata pdata;
+
+ int rx_rate[WM8996_AIFS];
+ int bclk_rate[WM8996_AIFS];
+
+ /* Platform dependant ReTune mobile configuration */
+ int num_retune_mobile_texts;
+ const char **retune_mobile_texts;
+ int retune_mobile_cfg[2];
+ struct soc_enum retune_mobile_enum;
+
+ struct snd_soc_jack *jack;
+ bool detecting;
+ bool jack_mic;
+ wm8996_polarity_fn polarity_cb;
+
+#ifdef CONFIG_GPIOLIB
+ struct gpio_chip gpio_chip;
+#endif
+};
+
+/* We can't use the same notifier block for more than one supply and
+ * there's no way I can see to get from a callback to the caller
+ * except container_of().
+ */
+#define WM8996_REGULATOR_EVENT(n) \
+static int wm8996_regulator_event_##n(struct notifier_block *nb, \
+ unsigned long event, void *data) \
+{ \
+ struct wm8996_priv *wm8996 = container_of(nb, struct wm8996_priv, \
+ disable_nb[n]); \
+ if (event & REGULATOR_EVENT_DISABLE) { \
+ wm8996->codec->cache_sync = 1; \
+ } \
+ return 0; \
+}
+
+WM8996_REGULATOR_EVENT(0)
+WM8996_REGULATOR_EVENT(1)
+WM8996_REGULATOR_EVENT(2)
+WM8996_REGULATOR_EVENT(3)
+
+static const u16 wm8996_reg[WM8996_MAX_REGISTER] = {
+ [WM8996_SOFTWARE_RESET] = 0x8996,
+ [WM8996_POWER_MANAGEMENT_7] = 0x10,
+ [WM8996_DAC1_HPOUT1_VOLUME] = 0x88,
+ [WM8996_DAC2_HPOUT2_VOLUME] = 0x88,
+ [WM8996_DAC1_LEFT_VOLUME] = 0x2c0,
+ [WM8996_DAC1_RIGHT_VOLUME] = 0x2c0,
+ [WM8996_DAC2_LEFT_VOLUME] = 0x2c0,
+ [WM8996_DAC2_RIGHT_VOLUME] = 0x2c0,
+ [WM8996_OUTPUT1_LEFT_VOLUME] = 0x80,
+ [WM8996_OUTPUT1_RIGHT_VOLUME] = 0x80,
+ [WM8996_OUTPUT2_LEFT_VOLUME] = 0x80,
+ [WM8996_OUTPUT2_RIGHT_VOLUME] = 0x80,
+ [WM8996_MICBIAS_1] = 0x39,
+ [WM8996_MICBIAS_2] = 0x39,
+ [WM8996_LDO_1] = 0x3,
+ [WM8996_LDO_2] = 0x13,
+ [WM8996_ACCESSORY_DETECT_MODE_1] = 0x4,
+ [WM8996_HEADPHONE_DETECT_1] = 0x20,
+ [WM8996_MIC_DETECT_1] = 0x7600,
+ [WM8996_MIC_DETECT_2] = 0xbf,
+ [WM8996_CHARGE_PUMP_1] = 0x1f25,
+ [WM8996_CHARGE_PUMP_2] = 0xab19,
+ [WM8996_DC_SERVO_5] = 0x2a2a,
+ [WM8996_CONTROL_INTERFACE_1] = 0x8004,
+ [WM8996_CLOCKING_1] = 0x10,
+ [WM8996_AIF_RATE] = 0x83,
+ [WM8996_FLL_CONTROL_4] = 0x5dc0,
+ [WM8996_FLL_CONTROL_5] = 0xc84,
+ [WM8996_FLL_EFS_2] = 0x2,
+ [WM8996_AIF1_TX_LRCLK_1] = 0x80,
+ [WM8996_AIF1_TX_LRCLK_2] = 0x8,
+ [WM8996_AIF1_RX_LRCLK_1] = 0x80,
+ [WM8996_AIF1TX_DATA_CONFIGURATION_1] = 0x1818,
+ [WM8996_AIF1RX_DATA_CONFIGURATION] = 0x1818,
+ [WM8996_AIF1TX_TEST] = 0x7,
+ [WM8996_AIF2_TX_LRCLK_1] = 0x80,
+ [WM8996_AIF2_TX_LRCLK_2] = 0x8,
+ [WM8996_AIF2_RX_LRCLK_1] = 0x80,
+ [WM8996_AIF2TX_DATA_CONFIGURATION_1] = 0x1818,
+ [WM8996_AIF2RX_DATA_CONFIGURATION] = 0x1818,
+ [WM8996_AIF2TX_TEST] = 0x1,
+ [WM8996_DSP1_TX_LEFT_VOLUME] = 0xc0,
+ [WM8996_DSP1_TX_RIGHT_VOLUME] = 0xc0,
+ [WM8996_DSP1_RX_LEFT_VOLUME] = 0xc0,
+ [WM8996_DSP1_RX_RIGHT_VOLUME] = 0xc0,
+ [WM8996_DSP1_TX_FILTERS] = 0x2000,
+ [WM8996_DSP1_RX_FILTERS_1] = 0x200,
+ [WM8996_DSP1_RX_FILTERS_2] = 0x10,
+ [WM8996_DSP1_DRC_1] = 0x98,
+ [WM8996_DSP1_DRC_2] = 0x845,
+ [WM8996_DSP1_RX_EQ_GAINS_1] = 0x6318,
+ [WM8996_DSP1_RX_EQ_GAINS_2] = 0x6300,
+ [WM8996_DSP1_RX_EQ_BAND_1_A] = 0xfca,
+ [WM8996_DSP1_RX_EQ_BAND_1_B] = 0x400,
+ [WM8996_DSP1_RX_EQ_BAND_1_PG] = 0xd8,
+ [WM8996_DSP1_RX_EQ_BAND_2_A] = 0x1eb5,
+ [WM8996_DSP1_RX_EQ_BAND_2_B] = 0xf145,
+ [WM8996_DSP1_RX_EQ_BAND_2_C] = 0xb75,
+ [WM8996_DSP1_RX_EQ_BAND_2_PG] = 0x1c5,
+ [WM8996_DSP1_RX_EQ_BAND_3_A] = 0x1c58,
+ [WM8996_DSP1_RX_EQ_BAND_3_B] = 0xf373,
+ [WM8996_DSP1_RX_EQ_BAND_3_C] = 0xa54,
+ [WM8996_DSP1_RX_EQ_BAND_3_PG] = 0x558,
+ [WM8996_DSP1_RX_EQ_BAND_4_A] = 0x168e,
+ [WM8996_DSP1_RX_EQ_BAND_4_B] = 0xf829,
+ [WM8996_DSP1_RX_EQ_BAND_4_C] = 0x7ad,
+ [WM8996_DSP1_RX_EQ_BAND_4_PG] = 0x1103,
+ [WM8996_DSP1_RX_EQ_BAND_5_A] = 0x564,
+ [WM8996_DSP1_RX_EQ_BAND_5_B] = 0x559,
+ [WM8996_DSP1_RX_EQ_BAND_5_PG] = 0x4000,
+ [WM8996_DSP2_TX_LEFT_VOLUME] = 0xc0,
+ [WM8996_DSP2_TX_RIGHT_VOLUME] = 0xc0,
+ [WM8996_DSP2_RX_LEFT_VOLUME] = 0xc0,
+ [WM8996_DSP2_RX_RIGHT_VOLUME] = 0xc0,
+ [WM8996_DSP2_TX_FILTERS] = 0x2000,
+ [WM8996_DSP2_RX_FILTERS_1] = 0x200,
+ [WM8996_DSP2_RX_FILTERS_2] = 0x10,
+ [WM8996_DSP2_DRC_1] = 0x98,
+ [WM8996_DSP2_DRC_2] = 0x845,
+ [WM8996_DSP2_RX_EQ_GAINS_1] = 0x6318,
+ [WM8996_DSP2_RX_EQ_GAINS_2] = 0x6300,
+ [WM8996_DSP2_RX_EQ_BAND_1_A] = 0xfca,
+ [WM8996_DSP2_RX_EQ_BAND_1_B] = 0x400,
+ [WM8996_DSP2_RX_EQ_BAND_1_PG] = 0xd8,
+ [WM8996_DSP2_RX_EQ_BAND_2_A] = 0x1eb5,
+ [WM8996_DSP2_RX_EQ_BAND_2_B] = 0xf145,
+ [WM8996_DSP2_RX_EQ_BAND_2_C] = 0xb75,
+ [WM8996_DSP2_RX_EQ_BAND_2_PG] = 0x1c5,
+ [WM8996_DSP2_RX_EQ_BAND_3_A] = 0x1c58,
+ [WM8996_DSP2_RX_EQ_BAND_3_B] = 0xf373,
+ [WM8996_DSP2_RX_EQ_BAND_3_C] = 0xa54,
+ [WM8996_DSP2_RX_EQ_BAND_3_PG] = 0x558,
+ [WM8996_DSP2_RX_EQ_BAND_4_A] = 0x168e,
+ [WM8996_DSP2_RX_EQ_BAND_4_B] = 0xf829,
+ [WM8996_DSP2_RX_EQ_BAND_4_C] = 0x7ad,
+ [WM8996_DSP2_RX_EQ_BAND_4_PG] = 0x1103,
+ [WM8996_DSP2_RX_EQ_BAND_5_A] = 0x564,
+ [WM8996_DSP2_RX_EQ_BAND_5_B] = 0x559,
+ [WM8996_DSP2_RX_EQ_BAND_5_PG] = 0x4000,
+ [WM8996_OVERSAMPLING] = 0xd,
+ [WM8996_SIDETONE] = 0x1040,
+ [WM8996_GPIO_1] = 0xa101,
+ [WM8996_GPIO_2] = 0xa101,
+ [WM8996_GPIO_3] = 0xa101,
+ [WM8996_GPIO_4] = 0xa101,
+ [WM8996_GPIO_5] = 0xa101,
+ [WM8996_PULL_CONTROL_2] = 0x140,
+ [WM8996_INTERRUPT_STATUS_1_MASK] = 0x1f,
+ [WM8996_INTERRUPT_STATUS_2_MASK] = 0x1ecf,
+ [WM8996_RIGHT_PDM_SPEAKER] = 0x1,
+ [WM8996_PDM_SPEAKER_MUTE_SEQUENCE] = 0x69,
+ [WM8996_PDM_SPEAKER_VOLUME] = 0x66,
+ [WM8996_WRITE_SEQUENCER_0] = 0x1,
+ [WM8996_WRITE_SEQUENCER_1] = 0x1,
+ [WM8996_WRITE_SEQUENCER_3] = 0x6,
+ [WM8996_WRITE_SEQUENCER_4] = 0x40,
+ [WM8996_WRITE_SEQUENCER_5] = 0x1,
+ [WM8996_WRITE_SEQUENCER_6] = 0xf,
+ [WM8996_WRITE_SEQUENCER_7] = 0x6,
+ [WM8996_WRITE_SEQUENCER_8] = 0x1,
+ [WM8996_WRITE_SEQUENCER_9] = 0x3,
+ [WM8996_WRITE_SEQUENCER_10] = 0x104,
+ [WM8996_WRITE_SEQUENCER_12] = 0x60,
+ [WM8996_WRITE_SEQUENCER_13] = 0x11,
+ [WM8996_WRITE_SEQUENCER_14] = 0x401,
+ [WM8996_WRITE_SEQUENCER_16] = 0x50,
+ [WM8996_WRITE_SEQUENCER_17] = 0x3,
+ [WM8996_WRITE_SEQUENCER_18] = 0x100,
+ [WM8996_WRITE_SEQUENCER_20] = 0x51,
+ [WM8996_WRITE_SEQUENCER_21] = 0x3,
+ [WM8996_WRITE_SEQUENCER_22] = 0x104,
+ [WM8996_WRITE_SEQUENCER_23] = 0xa,
+ [WM8996_WRITE_SEQUENCER_24] = 0x60,
+ [WM8996_WRITE_SEQUENCER_25] = 0x3b,
+ [WM8996_WRITE_SEQUENCER_26] = 0x502,
+ [WM8996_WRITE_SEQUENCER_27] = 0x100,
+ [WM8996_WRITE_SEQUENCER_28] = 0x2fff,
+ [WM8996_WRITE_SEQUENCER_32] = 0x2fff,
+ [WM8996_WRITE_SEQUENCER_36] = 0x2fff,
+ [WM8996_WRITE_SEQUENCER_40] = 0x2fff,
+ [WM8996_WRITE_SEQUENCER_44] = 0x2fff,
+ [WM8996_WRITE_SEQUENCER_48] = 0x2fff,
+ [WM8996_WRITE_SEQUENCER_52] = 0x2fff,
+ [WM8996_WRITE_SEQUENCER_56] = 0x2fff,
+ [WM8996_WRITE_SEQUENCER_60] = 0x2fff,
+ [WM8996_WRITE_SEQUENCER_64] = 0x1,
+ [WM8996_WRITE_SEQUENCER_65] = 0x1,
+ [WM8996_WRITE_SEQUENCER_67] = 0x6,
+ [WM8996_WRITE_SEQUENCER_68] = 0x40,
+ [WM8996_WRITE_SEQUENCER_69] = 0x1,
+ [WM8996_WRITE_SEQUENCER_70] = 0xf,
+ [WM8996_WRITE_SEQUENCER_71] = 0x6,
+ [WM8996_WRITE_SEQUENCER_72] = 0x1,
+ [WM8996_WRITE_SEQUENCER_73] = 0x3,
+ [WM8996_WRITE_SEQUENCER_74] = 0x104,
+ [WM8996_WRITE_SEQUENCER_76] = 0x60,
+ [WM8996_WRITE_SEQUENCER_77] = 0x11,
+ [WM8996_WRITE_SEQUENCER_78] = 0x401,
+ [WM8996_WRITE_SEQUENCER_80] = 0x50,
+ [WM8996_WRITE_SEQUENCER_81] = 0x3,
+ [WM8996_WRITE_SEQUENCER_82] = 0x100,
+ [WM8996_WRITE_SEQUENCER_84] = 0x60,
+ [WM8996_WRITE_SEQUENCER_85] = 0x3b,
+ [WM8996_WRITE_SEQUENCER_86] = 0x502,
+ [WM8996_WRITE_SEQUENCER_87] = 0x100,
+ [WM8996_WRITE_SEQUENCER_88] = 0x2fff,
+ [WM8996_WRITE_SEQUENCER_92] = 0x2fff,
+ [WM8996_WRITE_SEQUENCER_96] = 0x2fff,
+ [WM8996_WRITE_SEQUENCER_100] = 0x2fff,
+ [WM8996_WRITE_SEQUENCER_104] = 0x2fff,
+ [WM8996_WRITE_SEQUENCER_108] = 0x2fff,
+ [WM8996_WRITE_SEQUENCER_112] = 0x2fff,
+ [WM8996_WRITE_SEQUENCER_116] = 0x2fff,
+ [WM8996_WRITE_SEQUENCER_120] = 0x2fff,
+ [WM8996_WRITE_SEQUENCER_124] = 0x2fff,
+ [WM8996_WRITE_SEQUENCER_128] = 0x1,
+ [WM8996_WRITE_SEQUENCER_129] = 0x1,
+ [WM8996_WRITE_SEQUENCER_131] = 0x6,
+ [WM8996_WRITE_SEQUENCER_132] = 0x40,
+ [WM8996_WRITE_SEQUENCER_133] = 0x1,
+ [WM8996_WRITE_SEQUENCER_134] = 0xf,
+ [WM8996_WRITE_SEQUENCER_135] = 0x6,
+ [WM8996_WRITE_SEQUENCER_136] = 0x1,
+ [WM8996_WRITE_SEQUENCER_137] = 0x3,
+ [WM8996_WRITE_SEQUENCER_138] = 0x106,
+ [WM8996_WRITE_SEQUENCER_140] = 0x61,
+ [WM8996_WRITE_SEQUENCER_141] = 0x11,
+ [WM8996_WRITE_SEQUENCER_142] = 0x401,
+ [WM8996_WRITE_SEQUENCER_144] = 0x50,
+ [WM8996_WRITE_SEQUENCER_145] = 0x3,
+ [WM8996_WRITE_SEQUENCER_146] = 0x102,
+ [WM8996_WRITE_SEQUENCER_148] = 0x51,
+ [WM8996_WRITE_SEQUENCER_149] = 0x3,
+ [WM8996_WRITE_SEQUENCER_150] = 0x106,
+ [WM8996_WRITE_SEQUENCER_151] = 0xa,
+ [WM8996_WRITE_SEQUENCER_152] = 0x61,
+ [WM8996_WRITE_SEQUENCER_153] = 0x3b,
+ [WM8996_WRITE_SEQUENCER_154] = 0x502,
+ [WM8996_WRITE_SEQUENCER_155] = 0x100,
+ [WM8996_WRITE_SEQUENCER_156] = 0x2fff,
+ [WM8996_WRITE_SEQUENCER_160] = 0x2fff,
+ [WM8996_WRITE_SEQUENCER_164] = 0x2fff,
+ [WM8996_WRITE_SEQUENCER_168] = 0x2fff,
+ [WM8996_WRITE_SEQUENCER_172] = 0x2fff,
+ [WM8996_WRITE_SEQUENCER_176] = 0x2fff,
+ [WM8996_WRITE_SEQUENCER_180] = 0x2fff,
+ [WM8996_WRITE_SEQUENCER_184] = 0x2fff,
+ [WM8996_WRITE_SEQUENCER_188] = 0x2fff,
+ [WM8996_WRITE_SEQUENCER_192] = 0x1,
+ [WM8996_WRITE_SEQUENCER_193] = 0x1,
+ [WM8996_WRITE_SEQUENCER_195] = 0x6,
+ [WM8996_WRITE_SEQUENCER_196] = 0x40,
+ [WM8996_WRITE_SEQUENCER_197] = 0x1,
+ [WM8996_WRITE_SEQUENCER_198] = 0xf,
+ [WM8996_WRITE_SEQUENCER_199] = 0x6,
+ [WM8996_WRITE_SEQUENCER_200] = 0x1,
+ [WM8996_WRITE_SEQUENCER_201] = 0x3,
+ [WM8996_WRITE_SEQUENCER_202] = 0x106,
+ [WM8996_WRITE_SEQUENCER_204] = 0x61,
+ [WM8996_WRITE_SEQUENCER_205] = 0x11,
+ [WM8996_WRITE_SEQUENCER_206] = 0x401,
+ [WM8996_WRITE_SEQUENCER_208] = 0x50,
+ [WM8996_WRITE_SEQUENCER_209] = 0x3,
+ [WM8996_WRITE_SEQUENCER_210] = 0x102,
+ [WM8996_WRITE_SEQUENCER_212] = 0x61,
+ [WM8996_WRITE_SEQUENCER_213] = 0x3b,
+ [WM8996_WRITE_SEQUENCER_214] = 0x502,
+ [WM8996_WRITE_SEQUENCER_215] = 0x100,
+ [WM8996_WRITE_SEQUENCER_216] = 0x2fff,
+ [WM8996_WRITE_SEQUENCER_220] = 0x2fff,
+ [WM8996_WRITE_SEQUENCER_224] = 0x2fff,
+ [WM8996_WRITE_SEQUENCER_228] = 0x2fff,
+ [WM8996_WRITE_SEQUENCER_232] = 0x2fff,
+ [WM8996_WRITE_SEQUENCER_236] = 0x2fff,
+ [WM8996_WRITE_SEQUENCER_240] = 0x2fff,
+ [WM8996_WRITE_SEQUENCER_244] = 0x2fff,
+ [WM8996_WRITE_SEQUENCER_248] = 0x2fff,
+ [WM8996_WRITE_SEQUENCER_252] = 0x2fff,
+ [WM8996_WRITE_SEQUENCER_256] = 0x60,
+ [WM8996_WRITE_SEQUENCER_258] = 0x601,
+ [WM8996_WRITE_SEQUENCER_260] = 0x50,
+ [WM8996_WRITE_SEQUENCER_262] = 0x100,
+ [WM8996_WRITE_SEQUENCER_264] = 0x1,
+ [WM8996_WRITE_SEQUENCER_266] = 0x104,
+ [WM8996_WRITE_SEQUENCER_267] = 0x100,
+ [WM8996_WRITE_SEQUENCER_268] = 0x2fff,
+ [WM8996_WRITE_SEQUENCER_272] = 0x2fff,
+ [WM8996_WRITE_SEQUENCER_276] = 0x2fff,
+ [WM8996_WRITE_SEQUENCER_280] = 0x2fff,
+ [WM8996_WRITE_SEQUENCER_284] = 0x2fff,
+ [WM8996_WRITE_SEQUENCER_288] = 0x2fff,
+ [WM8996_WRITE_SEQUENCER_292] = 0x2fff,
+ [WM8996_WRITE_SEQUENCER_296] = 0x2fff,
+ [WM8996_WRITE_SEQUENCER_300] = 0x2fff,
+ [WM8996_WRITE_SEQUENCER_304] = 0x2fff,
+ [WM8996_WRITE_SEQUENCER_308] = 0x2fff,
+ [WM8996_WRITE_SEQUENCER_312] = 0x2fff,
+ [WM8996_WRITE_SEQUENCER_316] = 0x2fff,
+ [WM8996_WRITE_SEQUENCER_320] = 0x61,
+ [WM8996_WRITE_SEQUENCER_322] = 0x601,
+ [WM8996_WRITE_SEQUENCER_324] = 0x50,
+ [WM8996_WRITE_SEQUENCER_326] = 0x102,
+ [WM8996_WRITE_SEQUENCER_328] = 0x1,
+ [WM8996_WRITE_SEQUENCER_330] = 0x106,
+ [WM8996_WRITE_SEQUENCER_331] = 0x100,
+ [WM8996_WRITE_SEQUENCER_332] = 0x2fff,
+ [WM8996_WRITE_SEQUENCER_336] = 0x2fff,
+ [WM8996_WRITE_SEQUENCER_340] = 0x2fff,
+ [WM8996_WRITE_SEQUENCER_344] = 0x2fff,
+ [WM8996_WRITE_SEQUENCER_348] = 0x2fff,
+ [WM8996_WRITE_SEQUENCER_352] = 0x2fff,
+ [WM8996_WRITE_SEQUENCER_356] = 0x2fff,
+ [WM8996_WRITE_SEQUENCER_360] = 0x2fff,
+ [WM8996_WRITE_SEQUENCER_364] = 0x2fff,
+ [WM8996_WRITE_SEQUENCER_368] = 0x2fff,
+ [WM8996_WRITE_SEQUENCER_372] = 0x2fff,
+ [WM8996_WRITE_SEQUENCER_376] = 0x2fff,
+ [WM8996_WRITE_SEQUENCER_380] = 0x2fff,
+ [WM8996_WRITE_SEQUENCER_384] = 0x60,
+ [WM8996_WRITE_SEQUENCER_386] = 0x601,
+ [WM8996_WRITE_SEQUENCER_388] = 0x61,
+ [WM8996_WRITE_SEQUENCER_390] = 0x601,
+ [WM8996_WRITE_SEQUENCER_392] = 0x50,
+ [WM8996_WRITE_SEQUENCER_394] = 0x300,
+ [WM8996_WRITE_SEQUENCER_396] = 0x1,
+ [WM8996_WRITE_SEQUENCER_398] = 0x304,
+ [WM8996_WRITE_SEQUENCER_400] = 0x40,
+ [WM8996_WRITE_SEQUENCER_402] = 0xf,
+ [WM8996_WRITE_SEQUENCER_404] = 0x1,
+ [WM8996_WRITE_SEQUENCER_407] = 0x100,
+};
+
+static const DECLARE_TLV_DB_SCALE(inpga_tlv, 0, 100, 0);
+static const DECLARE_TLV_DB_SCALE(sidetone_tlv, -3600, 150, 0);
+static const DECLARE_TLV_DB_SCALE(digital_tlv, -7200, 75, 1);
+static const DECLARE_TLV_DB_SCALE(out_digital_tlv, -1200, 150, 0);
+static const DECLARE_TLV_DB_SCALE(out_tlv, -900, 75, 0);
+static const DECLARE_TLV_DB_SCALE(spk_tlv, -900, 150, 0);
+static const DECLARE_TLV_DB_SCALE(eq_tlv, -1200, 100, 0);
+
+static const char *sidetone_hpf_text[] = {
+ "2.9kHz", "1.5kHz", "735Hz", "403Hz", "196Hz", "98Hz", "49Hz"
+};
+
+static const struct soc_enum sidetone_hpf =
+ SOC_ENUM_SINGLE(WM8996_SIDETONE, 7, 6, sidetone_hpf_text);
+
+static const char *hpf_mode_text[] = {
+ "HiFi", "Custom", "Voice"
+};
+
+static const struct soc_enum dsp1tx_hpf_mode =
+ SOC_ENUM_SINGLE(WM8996_DSP1_TX_FILTERS, 3, 3, hpf_mode_text);
+
+static const struct soc_enum dsp2tx_hpf_mode =
+ SOC_ENUM_SINGLE(WM8996_DSP2_TX_FILTERS, 3, 3, hpf_mode_text);
+
+static const char *hpf_cutoff_text[] = {
+ "50Hz", "75Hz", "100Hz", "150Hz", "200Hz", "300Hz", "400Hz"
+};
+
+static const struct soc_enum dsp1tx_hpf_cutoff =
+ SOC_ENUM_SINGLE(WM8996_DSP1_TX_FILTERS, 0, 7, hpf_cutoff_text);
+
+static const struct soc_enum dsp2tx_hpf_cutoff =
+ SOC_ENUM_SINGLE(WM8996_DSP2_TX_FILTERS, 0, 7, hpf_cutoff_text);
+
+static void wm8996_set_retune_mobile(struct snd_soc_codec *codec, int block)
+{
+ struct wm8996_priv *wm8996 = snd_soc_codec_get_drvdata(codec);
+ struct wm8996_pdata *pdata = &wm8996->pdata;
+ int base, best, best_val, save, i, cfg, iface;
+
+ if (!wm8996->num_retune_mobile_texts)
+ return;
+
+ switch (block) {
+ case 0:
+ base = WM8996_DSP1_RX_EQ_GAINS_1;
+ if (snd_soc_read(codec, WM8996_POWER_MANAGEMENT_8) &
+ WM8996_DSP1RX_SRC)
+ iface = 1;
+ else
+ iface = 0;
+ break;
+ case 1:
+ base = WM8996_DSP1_RX_EQ_GAINS_2;
+ if (snd_soc_read(codec, WM8996_POWER_MANAGEMENT_8) &
+ WM8996_DSP2RX_SRC)
+ iface = 1;
+ else
+ iface = 0;
+ break;
+ default:
+ return;
+ }
+
+ /* Find the version of the currently selected configuration
+ * with the nearest sample rate. */
+ cfg = wm8996->retune_mobile_cfg[block];
+ best = 0;
+ best_val = INT_MAX;
+ for (i = 0; i < pdata->num_retune_mobile_cfgs; i++) {
+ if (strcmp(pdata->retune_mobile_cfgs[i].name,
+ wm8996->retune_mobile_texts[cfg]) == 0 &&
+ abs(pdata->retune_mobile_cfgs[i].rate
+ - wm8996->rx_rate[iface]) < best_val) {
+ best = i;
+ best_val = abs(pdata->retune_mobile_cfgs[i].rate
+ - wm8996->rx_rate[iface]);
+ }
+ }
+
+ dev_dbg(codec->dev, "ReTune Mobile %d %s/%dHz for %dHz sample rate\n",
+ block,
+ pdata->retune_mobile_cfgs[best].name,
+ pdata->retune_mobile_cfgs[best].rate,
+ wm8996->rx_rate[iface]);
+
+ /* The EQ will be disabled while reconfiguring it, remember the
+ * current configuration.
+ */
+ save = snd_soc_read(codec, base);
+ save &= WM8996_DSP1RX_EQ_ENA;
+
+ for (i = 0; i < ARRAY_SIZE(pdata->retune_mobile_cfgs[best].regs); i++)
+ snd_soc_update_bits(codec, base + i, 0xffff,
+ pdata->retune_mobile_cfgs[best].regs[i]);
+
+ snd_soc_update_bits(codec, base, WM8996_DSP1RX_EQ_ENA, save);
+}
+
+/* Icky as hell but saves code duplication */
+static int wm8996_get_retune_mobile_block(const char *name)
+{
+ if (strcmp(name, "DSP1 EQ Mode") == 0)
+ return 0;
+ if (strcmp(name, "DSP2 EQ Mode") == 0)
+ return 1;
+ return -EINVAL;
+}
+
+static int wm8996_put_retune_mobile_enum(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct wm8996_priv *wm8996 = snd_soc_codec_get_drvdata(codec);
+ struct wm8996_pdata *pdata = &wm8996->pdata;
+ int block = wm8996_get_retune_mobile_block(kcontrol->id.name);
+ int value = ucontrol->value.integer.value[0];
+
+ if (block < 0)
+ return block;
+
+ if (value >= pdata->num_retune_mobile_cfgs)
+ return -EINVAL;
+
+ wm8996->retune_mobile_cfg[block] = value;
+
+ wm8996_set_retune_mobile(codec, block);
+
+ return 0;
+}
+
+static int wm8996_get_retune_mobile_enum(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct wm8996_priv *wm8996 = snd_soc_codec_get_drvdata(codec);
+ int block = wm8996_get_retune_mobile_block(kcontrol->id.name);
+
+ ucontrol->value.enumerated.item[0] = wm8996->retune_mobile_cfg[block];
+
+ return 0;
+}
+
+static const struct snd_kcontrol_new wm8996_snd_controls[] = {
+SOC_DOUBLE_R_TLV("Capture Volume", WM8996_LEFT_LINE_INPUT_VOLUME,
+ WM8996_RIGHT_LINE_INPUT_VOLUME, 0, 31, 0, inpga_tlv),
+SOC_DOUBLE_R("Capture ZC Switch", WM8996_LEFT_LINE_INPUT_VOLUME,
+ WM8996_RIGHT_LINE_INPUT_VOLUME, 5, 1, 0),
+
+SOC_DOUBLE_TLV("DAC1 Sidetone Volume", WM8996_DAC1_MIXER_VOLUMES,
+ 0, 5, 24, 0, sidetone_tlv),
+SOC_DOUBLE_TLV("DAC2 Sidetone Volume", WM8996_DAC2_MIXER_VOLUMES,
+ 0, 5, 24, 0, sidetone_tlv),
+SOC_SINGLE("Sidetone LPF Switch", WM8996_SIDETONE, 12, 1, 0),
+SOC_ENUM("Sidetone HPF Cut-off", sidetone_hpf),
+SOC_SINGLE("Sidetone HPF Switch", WM8996_SIDETONE, 6, 1, 0),
+
+SOC_DOUBLE_R_TLV("DSP1 Capture Volume", WM8996_DSP1_TX_LEFT_VOLUME,
+ WM8996_DSP1_TX_RIGHT_VOLUME, 1, 96, 0, digital_tlv),
+SOC_DOUBLE_R_TLV("DSP2 Capture Volume", WM8996_DSP2_TX_LEFT_VOLUME,
+ WM8996_DSP2_TX_RIGHT_VOLUME, 1, 96, 0, digital_tlv),
+
+SOC_SINGLE("DSP1 Capture Notch Filter Switch", WM8996_DSP1_TX_FILTERS,
+ 13, 1, 0),
+SOC_DOUBLE("DSP1 Capture HPF Switch", WM8996_DSP1_TX_FILTERS, 12, 11, 1, 0),
+SOC_ENUM("DSP1 Capture HPF Mode", dsp1tx_hpf_mode),
+SOC_ENUM("DSP1 Capture HPF Cutoff", dsp1tx_hpf_cutoff),
+
+SOC_SINGLE("DSP2 Capture Notch Filter Switch", WM8996_DSP2_TX_FILTERS,
+ 13, 1, 0),
+SOC_DOUBLE("DSP2 Capture HPF Switch", WM8996_DSP2_TX_FILTERS, 12, 11, 1, 0),
+SOC_ENUM("DSP2 Capture HPF Mode", dsp2tx_hpf_mode),
+SOC_ENUM("DSP2 Capture HPF Cutoff", dsp2tx_hpf_cutoff),
+
+SOC_DOUBLE_R_TLV("DSP1 Playback Volume", WM8996_DSP1_RX_LEFT_VOLUME,
+ WM8996_DSP1_RX_RIGHT_VOLUME, 1, 112, 0, digital_tlv),
+SOC_SINGLE("DSP1 Playback Switch", WM8996_DSP1_RX_FILTERS_1, 9, 1, 1),
+
+SOC_DOUBLE_R_TLV("DSP2 Playback Volume", WM8996_DSP2_RX_LEFT_VOLUME,
+ WM8996_DSP2_RX_RIGHT_VOLUME, 1, 112, 0, digital_tlv),
+SOC_SINGLE("DSP2 Playback Switch", WM8996_DSP2_RX_FILTERS_1, 9, 1, 1),
+
+SOC_DOUBLE_R_TLV("DAC1 Volume", WM8996_DAC1_LEFT_VOLUME,
+ WM8996_DAC1_RIGHT_VOLUME, 1, 112, 0, digital_tlv),
+SOC_DOUBLE_R("DAC1 Switch", WM8996_DAC1_LEFT_VOLUME,
+ WM8996_DAC1_RIGHT_VOLUME, 9, 1, 1),
+
+SOC_DOUBLE_R_TLV("DAC2 Volume", WM8996_DAC2_LEFT_VOLUME,
+ WM8996_DAC2_RIGHT_VOLUME, 1, 112, 0, digital_tlv),
+SOC_DOUBLE_R("DAC2 Switch", WM8996_DAC2_LEFT_VOLUME,
+ WM8996_DAC2_RIGHT_VOLUME, 9, 1, 1),
+
+SOC_SINGLE("Speaker High Performance Switch", WM8996_OVERSAMPLING, 3, 1, 0),
+SOC_SINGLE("DMIC High Performance Switch", WM8996_OVERSAMPLING, 2, 1, 0),
+SOC_SINGLE("ADC High Performance Switch", WM8996_OVERSAMPLING, 1, 1, 0),
+SOC_SINGLE("DAC High Performance Switch", WM8996_OVERSAMPLING, 0, 1, 0),
+
+SOC_SINGLE("DAC Soft Mute Switch", WM8996_DAC_SOFTMUTE, 1, 1, 0),
+SOC_SINGLE("DAC Slow Soft Mute Switch", WM8996_DAC_SOFTMUTE, 0, 1, 0),
+
+SOC_DOUBLE_TLV("Digital Output 1 Volume", WM8996_DAC1_HPOUT1_VOLUME, 0, 4,
+ 8, 0, out_digital_tlv),
+SOC_DOUBLE_TLV("Digital Output 2 Volume", WM8996_DAC2_HPOUT2_VOLUME, 0, 4,
+ 8, 0, out_digital_tlv),
+
+SOC_DOUBLE_R_TLV("Output 1 Volume", WM8996_OUTPUT1_LEFT_VOLUME,
+ WM8996_OUTPUT1_RIGHT_VOLUME, 0, 12, 0, out_tlv),
+SOC_DOUBLE_R("Output 1 ZC Switch", WM8996_OUTPUT1_LEFT_VOLUME,
+ WM8996_OUTPUT1_RIGHT_VOLUME, 7, 1, 0),
+
+SOC_DOUBLE_R_TLV("Output 2 Volume", WM8996_OUTPUT2_LEFT_VOLUME,
+ WM8996_OUTPUT2_RIGHT_VOLUME, 0, 12, 0, out_tlv),
+SOC_DOUBLE_R("Output 2 ZC Switch", WM8996_OUTPUT2_LEFT_VOLUME,
+ WM8996_OUTPUT2_RIGHT_VOLUME, 7, 1, 0),
+
+SOC_DOUBLE_TLV("Speaker Volume", WM8996_PDM_SPEAKER_VOLUME, 0, 4, 8, 0,
+ spk_tlv),
+SOC_DOUBLE_R("Speaker Switch", WM8996_LEFT_PDM_SPEAKER,
+ WM8996_RIGHT_PDM_SPEAKER, 3, 1, 1),
+SOC_DOUBLE_R("Speaker ZC Switch", WM8996_LEFT_PDM_SPEAKER,
+ WM8996_RIGHT_PDM_SPEAKER, 2, 1, 0),
+
+SOC_SINGLE("DSP1 EQ Switch", WM8996_DSP1_RX_EQ_GAINS_1, 0, 1, 0),
+SOC_SINGLE("DSP2 EQ Switch", WM8996_DSP2_RX_EQ_GAINS_1, 0, 1, 0),
+};
+
+static const struct snd_kcontrol_new wm8996_eq_controls[] = {
+SOC_SINGLE_TLV("DSP1 EQ B1 Volume", WM8996_DSP1_RX_EQ_GAINS_1, 11, 31, 0,
+ eq_tlv),
+SOC_SINGLE_TLV("DSP1 EQ B2 Volume", WM8996_DSP1_RX_EQ_GAINS_1, 6, 31, 0,
+ eq_tlv),
+SOC_SINGLE_TLV("DSP1 EQ B3 Volume", WM8996_DSP1_RX_EQ_GAINS_1, 1, 31, 0,
+ eq_tlv),
+SOC_SINGLE_TLV("DSP1 EQ B4 Volume", WM8996_DSP1_RX_EQ_GAINS_2, 11, 31, 0,
+ eq_tlv),
+SOC_SINGLE_TLV("DSP1 EQ B5 Volume", WM8996_DSP1_RX_EQ_GAINS_2, 6, 31, 0,
+ eq_tlv),
+
+SOC_SINGLE_TLV("DSP2 EQ B1 Volume", WM8996_DSP2_RX_EQ_GAINS_1, 11, 31, 0,
+ eq_tlv),
+SOC_SINGLE_TLV("DSP2 EQ B2 Volume", WM8996_DSP2_RX_EQ_GAINS_1, 6, 31, 0,
+ eq_tlv),
+SOC_SINGLE_TLV("DSP2 EQ B3 Volume", WM8996_DSP2_RX_EQ_GAINS_1, 1, 31, 0,
+ eq_tlv),
+SOC_SINGLE_TLV("DSP2 EQ B4 Volume", WM8996_DSP2_RX_EQ_GAINS_2, 11, 31, 0,
+ eq_tlv),
+SOC_SINGLE_TLV("DSP2 EQ B5 Volume", WM8996_DSP2_RX_EQ_GAINS_2, 6, 31, 0,
+ eq_tlv),
+};
+
+static int cp_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ msleep(5);
+ break;
+ default:
+ BUG();
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int rmv_short_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct wm8996_priv *wm8996 = snd_soc_codec_get_drvdata(w->codec);
+
+ /* Record which outputs we enabled */
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMD:
+ wm8996->hpout_pending &= ~w->shift;
+ break;
+ case SND_SOC_DAPM_PRE_PMU:
+ wm8996->hpout_pending |= w->shift;
+ break;
+ default:
+ BUG();
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static void wait_for_dc_servo(struct snd_soc_codec *codec, u16 mask)
+{
+ struct i2c_client *i2c = to_i2c_client(codec->dev);
+ struct wm8996_priv *wm8996 = snd_soc_codec_get_drvdata(codec);
+ int i, ret;
+ unsigned long timeout = 200;
+
+ snd_soc_write(codec, WM8996_DC_SERVO_2, mask);
+
+ /* Use the interrupt if possible */
+ do {
+ if (i2c->irq) {
+ timeout = wait_for_completion_timeout(&wm8996->dcs_done,
+ msecs_to_jiffies(200));
+ if (timeout == 0)
+ dev_err(codec->dev, "DC servo timed out\n");
+
+ } else {
+ msleep(1);
+ if (--i) {
+ timeout = 0;
+ break;
+ }
+ }
+
+ ret = snd_soc_read(codec, WM8996_DC_SERVO_2);
+ dev_dbg(codec->dev, "DC servo state: %x\n", ret);
+ } while (ret & mask);
+
+ if (timeout == 0)
+ dev_err(codec->dev, "DC servo timed out for %x\n", mask);
+ else
+ dev_dbg(codec->dev, "DC servo complete for %x\n", mask);
+}
+
+static void wm8996_seq_notifier(struct snd_soc_dapm_context *dapm,
+ enum snd_soc_dapm_type event, int subseq)
+{
+ struct snd_soc_codec *codec = container_of(dapm,
+ struct snd_soc_codec, dapm);
+ struct wm8996_priv *wm8996 = snd_soc_codec_get_drvdata(codec);
+ u16 val, mask;
+
+ /* Complete any pending DC servo starts */
+ if (wm8996->dcs_pending) {
+ dev_dbg(codec->dev, "Starting DC servo for %x\n",
+ wm8996->dcs_pending);
+
+ /* Trigger a startup sequence */
+ wait_for_dc_servo(codec, wm8996->dcs_pending
+ << WM8996_DCS_TRIG_STARTUP_0_SHIFT);
+
+ wm8996->dcs_pending = 0;
+ }
+
+ if (wm8996->hpout_pending != wm8996->hpout_ena) {
+ dev_dbg(codec->dev, "Applying RMV_SHORTs %x->%x\n",
+ wm8996->hpout_ena, wm8996->hpout_pending);
+
+ val = 0;
+ mask = 0;
+ if (wm8996->hpout_pending & HPOUT1L) {
+ val |= WM8996_HPOUT1L_RMV_SHORT;
+ mask |= WM8996_HPOUT1L_RMV_SHORT;
+ } else {
+ mask |= WM8996_HPOUT1L_RMV_SHORT |
+ WM8996_HPOUT1L_OUTP |
+ WM8996_HPOUT1L_DLY;
+ }
+
+ if (wm8996->hpout_pending & HPOUT1R) {
+ val |= WM8996_HPOUT1R_RMV_SHORT;
+ mask |= WM8996_HPOUT1R_RMV_SHORT;
+ } else {
+ mask |= WM8996_HPOUT1R_RMV_SHORT |
+ WM8996_HPOUT1R_OUTP |
+ WM8996_HPOUT1R_DLY;
+ }
+
+ snd_soc_update_bits(codec, WM8996_ANALOGUE_HP_1, mask, val);
+
+ val = 0;
+ mask = 0;
+ if (wm8996->hpout_pending & HPOUT2L) {
+ val |= WM8996_HPOUT2L_RMV_SHORT;
+ mask |= WM8996_HPOUT2L_RMV_SHORT;
+ } else {
+ mask |= WM8996_HPOUT2L_RMV_SHORT |
+ WM8996_HPOUT2L_OUTP |
+ WM8996_HPOUT2L_DLY;
+ }
+
+ if (wm8996->hpout_pending & HPOUT2R) {
+ val |= WM8996_HPOUT2R_RMV_SHORT;
+ mask |= WM8996_HPOUT2R_RMV_SHORT;
+ } else {
+ mask |= WM8996_HPOUT2R_RMV_SHORT |
+ WM8996_HPOUT2R_OUTP |
+ WM8996_HPOUT2R_DLY;
+ }
+
+ snd_soc_update_bits(codec, WM8996_ANALOGUE_HP_2, mask, val);
+
+ wm8996->hpout_ena = wm8996->hpout_pending;
+ }
+}
+
+static int dcs_start(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct wm8996_priv *wm8996 = snd_soc_codec_get_drvdata(w->codec);
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ wm8996->dcs_pending |= 1 << w->shift;
+ break;
+ default:
+ BUG();
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static const char *sidetone_text[] = {
+ "IN1", "IN2",
+};
+
+static const struct soc_enum left_sidetone_enum =
+ SOC_ENUM_SINGLE(WM8996_SIDETONE, 0, 2, sidetone_text);
+
+static const struct snd_kcontrol_new left_sidetone =
+ SOC_DAPM_ENUM("Left Sidetone", left_sidetone_enum);
+
+static const struct soc_enum right_sidetone_enum =
+ SOC_ENUM_SINGLE(WM8996_SIDETONE, 1, 2, sidetone_text);
+
+static const struct snd_kcontrol_new right_sidetone =
+ SOC_DAPM_ENUM("Right Sidetone", right_sidetone_enum);
+
+static const char *spk_text[] = {
+ "DAC1L", "DAC1R", "DAC2L", "DAC2R"
+};
+
+static const struct soc_enum spkl_enum =
+ SOC_ENUM_SINGLE(WM8996_LEFT_PDM_SPEAKER, 0, 4, spk_text);
+
+static const struct snd_kcontrol_new spkl_mux =
+ SOC_DAPM_ENUM("SPKL", spkl_enum);
+
+static const struct soc_enum spkr_enum =
+ SOC_ENUM_SINGLE(WM8996_RIGHT_PDM_SPEAKER, 0, 4, spk_text);
+
+static const struct snd_kcontrol_new spkr_mux =
+ SOC_DAPM_ENUM("SPKR", spkr_enum);
+
+static const char *dsp1rx_text[] = {
+ "AIF1", "AIF2"
+};
+
+static const struct soc_enum dsp1rx_enum =
+ SOC_ENUM_SINGLE(WM8996_POWER_MANAGEMENT_8, 0, 2, dsp1rx_text);
+
+static const struct snd_kcontrol_new dsp1rx =
+ SOC_DAPM_ENUM("DSP1RX", dsp1rx_enum);
+
+static const char *dsp2rx_text[] = {
+ "AIF2", "AIF1"
+};
+
+static const struct soc_enum dsp2rx_enum =
+ SOC_ENUM_SINGLE(WM8996_POWER_MANAGEMENT_8, 4, 2, dsp2rx_text);
+
+static const struct snd_kcontrol_new dsp2rx =
+ SOC_DAPM_ENUM("DSP2RX", dsp2rx_enum);
+
+static const char *aif2tx_text[] = {
+ "DSP2", "DSP1", "AIF1"
+};
+
+static const struct soc_enum aif2tx_enum =
+ SOC_ENUM_SINGLE(WM8996_POWER_MANAGEMENT_8, 6, 3, aif2tx_text);
+
+static const struct snd_kcontrol_new aif2tx =
+ SOC_DAPM_ENUM("AIF2TX", aif2tx_enum);
+
+static const char *inmux_text[] = {
+ "ADC", "DMIC1", "DMIC2"
+};
+
+static const struct soc_enum in1_enum =
+ SOC_ENUM_SINGLE(WM8996_POWER_MANAGEMENT_7, 0, 3, inmux_text);
+
+static const struct snd_kcontrol_new in1_mux =
+ SOC_DAPM_ENUM("IN1 Mux", in1_enum);
+
+static const struct soc_enum in2_enum =
+ SOC_ENUM_SINGLE(WM8996_POWER_MANAGEMENT_7, 4, 3, inmux_text);
+
+static const struct snd_kcontrol_new in2_mux =
+ SOC_DAPM_ENUM("IN2 Mux", in2_enum);
+
+static const struct snd_kcontrol_new dac2r_mix[] = {
+SOC_DAPM_SINGLE("Right Sidetone Switch", WM8996_DAC2_RIGHT_MIXER_ROUTING,
+ 5, 1, 0),
+SOC_DAPM_SINGLE("Left Sidetone Switch", WM8996_DAC2_RIGHT_MIXER_ROUTING,
+ 4, 1, 0),
+SOC_DAPM_SINGLE("DSP2 Switch", WM8996_DAC2_RIGHT_MIXER_ROUTING, 1, 1, 0),
+SOC_DAPM_SINGLE("DSP1 Switch", WM8996_DAC2_RIGHT_MIXER_ROUTING, 0, 1, 0),
+};
+
+static const struct snd_kcontrol_new dac2l_mix[] = {
+SOC_DAPM_SINGLE("Right Sidetone Switch", WM8996_DAC2_LEFT_MIXER_ROUTING,
+ 5, 1, 0),
+SOC_DAPM_SINGLE("Left Sidetone Switch", WM8996_DAC2_LEFT_MIXER_ROUTING,
+ 4, 1, 0),
+SOC_DAPM_SINGLE("DSP2 Switch", WM8996_DAC2_LEFT_MIXER_ROUTING, 1, 1, 0),
+SOC_DAPM_SINGLE("DSP1 Switch", WM8996_DAC2_LEFT_MIXER_ROUTING, 0, 1, 0),
+};
+
+static const struct snd_kcontrol_new dac1r_mix[] = {
+SOC_DAPM_SINGLE("Right Sidetone Switch", WM8996_DAC1_RIGHT_MIXER_ROUTING,
+ 5, 1, 0),
+SOC_DAPM_SINGLE("Left Sidetone Switch", WM8996_DAC1_RIGHT_MIXER_ROUTING,
+ 4, 1, 0),
+SOC_DAPM_SINGLE("DSP2 Switch", WM8996_DAC1_RIGHT_MIXER_ROUTING, 1, 1, 0),
+SOC_DAPM_SINGLE("DSP1 Switch", WM8996_DAC1_RIGHT_MIXER_ROUTING, 0, 1, 0),
+};
+
+static const struct snd_kcontrol_new dac1l_mix[] = {
+SOC_DAPM_SINGLE("Right Sidetone Switch", WM8996_DAC1_LEFT_MIXER_ROUTING,
+ 5, 1, 0),
+SOC_DAPM_SINGLE("Left Sidetone Switch", WM8996_DAC1_LEFT_MIXER_ROUTING,
+ 4, 1, 0),
+SOC_DAPM_SINGLE("DSP2 Switch", WM8996_DAC1_LEFT_MIXER_ROUTING, 1, 1, 0),
+SOC_DAPM_SINGLE("DSP1 Switch", WM8996_DAC1_LEFT_MIXER_ROUTING, 0, 1, 0),
+};
+
+static const struct snd_kcontrol_new dsp1txl[] = {
+SOC_DAPM_SINGLE("IN1 Switch", WM8996_DSP1_TX_LEFT_MIXER_ROUTING,
+ 1, 1, 0),
+SOC_DAPM_SINGLE("DAC Switch", WM8996_DSP1_TX_LEFT_MIXER_ROUTING,
+ 0, 1, 0),
+};
+
+static const struct snd_kcontrol_new dsp1txr[] = {
+SOC_DAPM_SINGLE("IN1 Switch", WM8996_DSP1_TX_RIGHT_MIXER_ROUTING,
+ 1, 1, 0),
+SOC_DAPM_SINGLE("DAC Switch", WM8996_DSP1_TX_RIGHT_MIXER_ROUTING,
+ 0, 1, 0),
+};
+
+static const struct snd_kcontrol_new dsp2txl[] = {
+SOC_DAPM_SINGLE("IN1 Switch", WM8996_DSP2_TX_LEFT_MIXER_ROUTING,
+ 1, 1, 0),
+SOC_DAPM_SINGLE("DAC Switch", WM8996_DSP2_TX_LEFT_MIXER_ROUTING,
+ 0, 1, 0),
+};
+
+static const struct snd_kcontrol_new dsp2txr[] = {
+SOC_DAPM_SINGLE("IN1 Switch", WM8996_DSP2_TX_RIGHT_MIXER_ROUTING,
+ 1, 1, 0),
+SOC_DAPM_SINGLE("DAC Switch", WM8996_DSP2_TX_RIGHT_MIXER_ROUTING,
+ 0, 1, 0),
+};
+
+
+static const struct snd_soc_dapm_widget wm8996_dapm_widgets[] = {
+SND_SOC_DAPM_INPUT("IN1LN"),
+SND_SOC_DAPM_INPUT("IN1LP"),
+SND_SOC_DAPM_INPUT("IN1RN"),
+SND_SOC_DAPM_INPUT("IN1RP"),
+
+SND_SOC_DAPM_INPUT("IN2LN"),
+SND_SOC_DAPM_INPUT("IN2LP"),
+SND_SOC_DAPM_INPUT("IN2RN"),
+SND_SOC_DAPM_INPUT("IN2RP"),
+
+SND_SOC_DAPM_INPUT("DMIC1DAT"),
+SND_SOC_DAPM_INPUT("DMIC2DAT"),
+
+SND_SOC_DAPM_SUPPLY_S("SYSCLK", 1, WM8996_AIF_CLOCKING_1, 0, 0, NULL, 0),
+SND_SOC_DAPM_SUPPLY_S("SYSDSPCLK", 2, WM8996_CLOCKING_1, 1, 0, NULL, 0),
+SND_SOC_DAPM_SUPPLY_S("AIFCLK", 2, WM8996_CLOCKING_1, 2, 0, NULL, 0),
+SND_SOC_DAPM_SUPPLY_S("Charge Pump", 2, WM8996_CHARGE_PUMP_1, 15, 0, cp_event,
+ SND_SOC_DAPM_POST_PMU),
+
+SND_SOC_DAPM_SUPPLY("LDO2", WM8996_POWER_MANAGEMENT_2, 1, 0, NULL, 0),
+SND_SOC_DAPM_MICBIAS("MICB2", WM8996_POWER_MANAGEMENT_1, 9, 0),
+SND_SOC_DAPM_MICBIAS("MICB1", WM8996_POWER_MANAGEMENT_1, 8, 0),
+
+SND_SOC_DAPM_PGA("IN1L PGA", WM8996_POWER_MANAGEMENT_2, 5, 0, NULL, 0),
+SND_SOC_DAPM_PGA("IN1R PGA", WM8996_POWER_MANAGEMENT_2, 4, 0, NULL, 0),
+
+SND_SOC_DAPM_MUX("IN1L Mux", SND_SOC_NOPM, 0, 0, &in1_mux),
+SND_SOC_DAPM_MUX("IN1R Mux", SND_SOC_NOPM, 0, 0, &in1_mux),
+SND_SOC_DAPM_MUX("IN2L Mux", SND_SOC_NOPM, 0, 0, &in2_mux),
+SND_SOC_DAPM_MUX("IN2R Mux", SND_SOC_NOPM, 0, 0, &in2_mux),
+
+SND_SOC_DAPM_PGA("IN1L", WM8996_POWER_MANAGEMENT_7, 2, 0, NULL, 0),
+SND_SOC_DAPM_PGA("IN1R", WM8996_POWER_MANAGEMENT_7, 3, 0, NULL, 0),
+SND_SOC_DAPM_PGA("IN2L", WM8996_POWER_MANAGEMENT_7, 6, 0, NULL, 0),
+SND_SOC_DAPM_PGA("IN2R", WM8996_POWER_MANAGEMENT_7, 7, 0, NULL, 0),
+
+SND_SOC_DAPM_SUPPLY("DMIC2", WM8996_POWER_MANAGEMENT_7, 9, 0, NULL, 0),
+SND_SOC_DAPM_SUPPLY("DMIC1", WM8996_POWER_MANAGEMENT_7, 8, 0, NULL, 0),
+
+SND_SOC_DAPM_ADC("DMIC2L", NULL, WM8996_POWER_MANAGEMENT_3, 5, 0),
+SND_SOC_DAPM_ADC("DMIC2R", NULL, WM8996_POWER_MANAGEMENT_3, 4, 0),
+SND_SOC_DAPM_ADC("DMIC1L", NULL, WM8996_POWER_MANAGEMENT_3, 3, 0),
+SND_SOC_DAPM_ADC("DMIC1R", NULL, WM8996_POWER_MANAGEMENT_3, 2, 0),
+
+SND_SOC_DAPM_ADC("ADCL", NULL, WM8996_POWER_MANAGEMENT_3, 1, 0),
+SND_SOC_DAPM_ADC("ADCR", NULL, WM8996_POWER_MANAGEMENT_3, 0, 0),
+
+SND_SOC_DAPM_MUX("Left Sidetone", SND_SOC_NOPM, 0, 0, &left_sidetone),
+SND_SOC_DAPM_MUX("Right Sidetone", SND_SOC_NOPM, 0, 0, &right_sidetone),
+
+SND_SOC_DAPM_AIF_IN("DSP2RXL", NULL, 0, WM8996_POWER_MANAGEMENT_3, 11, 0),
+SND_SOC_DAPM_AIF_IN("DSP2RXR", NULL, 1, WM8996_POWER_MANAGEMENT_3, 10, 0),
+SND_SOC_DAPM_AIF_IN("DSP1RXL", NULL, 0, WM8996_POWER_MANAGEMENT_3, 9, 0),
+SND_SOC_DAPM_AIF_IN("DSP1RXR", NULL, 1, WM8996_POWER_MANAGEMENT_3, 8, 0),
+
+SND_SOC_DAPM_MIXER("DSP2TXL", WM8996_POWER_MANAGEMENT_5, 11, 0,
+ dsp2txl, ARRAY_SIZE(dsp2txl)),
+SND_SOC_DAPM_MIXER("DSP2TXR", WM8996_POWER_MANAGEMENT_5, 10, 0,
+ dsp2txr, ARRAY_SIZE(dsp2txr)),
+SND_SOC_DAPM_MIXER("DSP1TXL", WM8996_POWER_MANAGEMENT_5, 9, 0,
+ dsp1txl, ARRAY_SIZE(dsp1txl)),
+SND_SOC_DAPM_MIXER("DSP1TXR", WM8996_POWER_MANAGEMENT_5, 8, 0,
+ dsp1txr, ARRAY_SIZE(dsp1txr)),
+
+SND_SOC_DAPM_MIXER("DAC2L Mixer", SND_SOC_NOPM, 0, 0,
+ dac2l_mix, ARRAY_SIZE(dac2l_mix)),
+SND_SOC_DAPM_MIXER("DAC2R Mixer", SND_SOC_NOPM, 0, 0,
+ dac2r_mix, ARRAY_SIZE(dac2r_mix)),
+SND_SOC_DAPM_MIXER("DAC1L Mixer", SND_SOC_NOPM, 0, 0,
+ dac1l_mix, ARRAY_SIZE(dac1l_mix)),
+SND_SOC_DAPM_MIXER("DAC1R Mixer", SND_SOC_NOPM, 0, 0,
+ dac1r_mix, ARRAY_SIZE(dac1r_mix)),
+
+SND_SOC_DAPM_DAC("DAC2L", NULL, WM8996_POWER_MANAGEMENT_5, 3, 0),
+SND_SOC_DAPM_DAC("DAC2R", NULL, WM8996_POWER_MANAGEMENT_5, 2, 0),
+SND_SOC_DAPM_DAC("DAC1L", NULL, WM8996_POWER_MANAGEMENT_5, 1, 0),
+SND_SOC_DAPM_DAC("DAC1R", NULL, WM8996_POWER_MANAGEMENT_5, 0, 0),
+
+SND_SOC_DAPM_AIF_IN("AIF2RX1", "AIF2 Playback", 1,
+ WM8996_POWER_MANAGEMENT_4, 9, 0),
+SND_SOC_DAPM_AIF_IN("AIF2RX0", "AIF2 Playback", 2,
+ WM8996_POWER_MANAGEMENT_4, 8, 0),
+
+SND_SOC_DAPM_AIF_IN("AIF2TX1", "AIF2 Capture", 1,
+ WM8996_POWER_MANAGEMENT_6, 9, 0),
+SND_SOC_DAPM_AIF_IN("AIF2TX0", "AIF2 Capture", 2,
+ WM8996_POWER_MANAGEMENT_6, 8, 0),
+
+SND_SOC_DAPM_AIF_IN("AIF1RX5", "AIF1 Playback", 5,
+ WM8996_POWER_MANAGEMENT_4, 5, 0),
+SND_SOC_DAPM_AIF_IN("AIF1RX4", "AIF1 Playback", 4,
+ WM8996_POWER_MANAGEMENT_4, 4, 0),
+SND_SOC_DAPM_AIF_IN("AIF1RX3", "AIF1 Playback", 3,
+ WM8996_POWER_MANAGEMENT_4, 3, 0),
+SND_SOC_DAPM_AIF_IN("AIF1RX2", "AIF1 Playback", 2,
+ WM8996_POWER_MANAGEMENT_4, 2, 0),
+SND_SOC_DAPM_AIF_IN("AIF1RX1", "AIF1 Playback", 1,
+ WM8996_POWER_MANAGEMENT_4, 1, 0),
+SND_SOC_DAPM_AIF_IN("AIF1RX0", "AIF1 Playback", 0,
+ WM8996_POWER_MANAGEMENT_4, 0, 0),
+
+SND_SOC_DAPM_AIF_OUT("AIF1TX5", "AIF1 Capture", 5,
+ WM8996_POWER_MANAGEMENT_6, 5, 0),
+SND_SOC_DAPM_AIF_OUT("AIF1TX4", "AIF1 Capture", 4,
+ WM8996_POWER_MANAGEMENT_6, 4, 0),
+SND_SOC_DAPM_AIF_OUT("AIF1TX3", "AIF1 Capture", 3,
+ WM8996_POWER_MANAGEMENT_6, 3, 0),
+SND_SOC_DAPM_AIF_OUT("AIF1TX2", "AIF1 Capture", 2,
+ WM8996_POWER_MANAGEMENT_6, 2, 0),
+SND_SOC_DAPM_AIF_OUT("AIF1TX1", "AIF1 Capture", 1,
+ WM8996_POWER_MANAGEMENT_6, 1, 0),
+SND_SOC_DAPM_AIF_OUT("AIF1TX0", "AIF1 Capture", 0,
+ WM8996_POWER_MANAGEMENT_6, 0, 0),
+
+/* We route as stereo pairs so define some dummy widgets to squash
+ * things down for now. RXA = 0,1, RXB = 2,3 and so on */
+SND_SOC_DAPM_PGA("AIF1RXA", SND_SOC_NOPM, 0, 0, NULL, 0),
+SND_SOC_DAPM_PGA("AIF1RXB", SND_SOC_NOPM, 0, 0, NULL, 0),
+SND_SOC_DAPM_PGA("AIF1RXC", SND_SOC_NOPM, 0, 0, NULL, 0),
+SND_SOC_DAPM_PGA("AIF2RX", SND_SOC_NOPM, 0, 0, NULL, 0),
+SND_SOC_DAPM_PGA("DSP2TX", SND_SOC_NOPM, 0, 0, NULL, 0),
+
+SND_SOC_DAPM_MUX("DSP1RX", SND_SOC_NOPM, 0, 0, &dsp1rx),
+SND_SOC_DAPM_MUX("DSP2RX", SND_SOC_NOPM, 0, 0, &dsp2rx),
+SND_SOC_DAPM_MUX("AIF2TX", SND_SOC_NOPM, 0, 0, &aif2tx),
+
+SND_SOC_DAPM_MUX("SPKL", SND_SOC_NOPM, 0, 0, &spkl_mux),
+SND_SOC_DAPM_MUX("SPKR", SND_SOC_NOPM, 0, 0, &spkr_mux),
+SND_SOC_DAPM_PGA("SPKL PGA", WM8996_LEFT_PDM_SPEAKER, 4, 0, NULL, 0),
+SND_SOC_DAPM_PGA("SPKR PGA", WM8996_RIGHT_PDM_SPEAKER, 4, 0, NULL, 0),
+
+SND_SOC_DAPM_PGA_S("HPOUT2L PGA", 0, WM8996_POWER_MANAGEMENT_1, 7, 0, NULL, 0),
+SND_SOC_DAPM_PGA_S("HPOUT2L_DLY", 1, WM8996_ANALOGUE_HP_2, 5, 0, NULL, 0),
+SND_SOC_DAPM_PGA_S("HPOUT2L_DCS", 2, WM8996_DC_SERVO_1, 2, 0, dcs_start,
+ SND_SOC_DAPM_POST_PMU),
+SND_SOC_DAPM_PGA_S("HPOUT2L_OUTP", 3, WM8996_ANALOGUE_HP_2, 6, 0, NULL, 0),
+SND_SOC_DAPM_PGA_S("HPOUT2L_RMV_SHORT", 3, SND_SOC_NOPM, HPOUT2L, 0,
+ rmv_short_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_PRE_PMD),
+
+SND_SOC_DAPM_PGA_S("HPOUT2R PGA", 0, WM8996_POWER_MANAGEMENT_1, 6, 0,NULL, 0),
+SND_SOC_DAPM_PGA_S("HPOUT2R_DLY", 1, WM8996_ANALOGUE_HP_2, 1, 0, NULL, 0),
+SND_SOC_DAPM_PGA_S("HPOUT2R_DCS", 2, WM8996_DC_SERVO_1, 3, 0, dcs_start,
+ SND_SOC_DAPM_POST_PMU),
+SND_SOC_DAPM_PGA_S("HPOUT2R_OUTP", 3, WM8996_ANALOGUE_HP_2, 2, 0, NULL, 0),
+SND_SOC_DAPM_PGA_S("HPOUT2R_RMV_SHORT", 3, SND_SOC_NOPM, HPOUT2R, 0,
+ rmv_short_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_PRE_PMD),
+
+SND_SOC_DAPM_PGA_S("HPOUT1L PGA", 0, WM8996_POWER_MANAGEMENT_1, 5, 0, NULL, 0),
+SND_SOC_DAPM_PGA_S("HPOUT1L_DLY", 1, WM8996_ANALOGUE_HP_1, 5, 0, NULL, 0),
+SND_SOC_DAPM_PGA_S("HPOUT1L_DCS", 2, WM8996_DC_SERVO_1, 0, 0, dcs_start,
+ SND_SOC_DAPM_POST_PMU),
+SND_SOC_DAPM_PGA_S("HPOUT1L_OUTP", 3, WM8996_ANALOGUE_HP_1, 6, 0, NULL, 0),
+SND_SOC_DAPM_PGA_S("HPOUT1L_RMV_SHORT", 3, SND_SOC_NOPM, HPOUT1L, 0,
+ rmv_short_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_PRE_PMD),
+
+SND_SOC_DAPM_PGA_S("HPOUT1R PGA", 0, WM8996_POWER_MANAGEMENT_1, 4, 0, NULL, 0),
+SND_SOC_DAPM_PGA_S("HPOUT1R_DLY", 1, WM8996_ANALOGUE_HP_1, 1, 0, NULL, 0),
+SND_SOC_DAPM_PGA_S("HPOUT1R_DCS", 2, WM8996_DC_SERVO_1, 1, 0, dcs_start,
+ SND_SOC_DAPM_POST_PMU),
+SND_SOC_DAPM_PGA_S("HPOUT1R_OUTP", 3, WM8996_ANALOGUE_HP_1, 2, 0, NULL, 0),
+SND_SOC_DAPM_PGA_S("HPOUT1R_RMV_SHORT", 3, SND_SOC_NOPM, HPOUT1R, 0,
+ rmv_short_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_PRE_PMD),
+
+SND_SOC_DAPM_OUTPUT("HPOUT1L"),
+SND_SOC_DAPM_OUTPUT("HPOUT1R"),
+SND_SOC_DAPM_OUTPUT("HPOUT2L"),
+SND_SOC_DAPM_OUTPUT("HPOUT2R"),
+SND_SOC_DAPM_OUTPUT("SPKDAT"),
+};
+
+static const struct snd_soc_dapm_route wm8996_dapm_routes[] = {
+ { "AIFCLK", NULL, "SYSCLK" },
+ { "SYSDSPCLK", NULL, "SYSCLK" },
+ { "Charge Pump", NULL, "SYSCLK" },
+
+ { "MICB1", NULL, "LDO2" },
+ { "MICB2", NULL, "LDO2" },
+
+ { "IN1L PGA", NULL, "IN2LN" },
+ { "IN1L PGA", NULL, "IN2LP" },
+ { "IN1L PGA", NULL, "IN1LN" },
+ { "IN1L PGA", NULL, "IN1LP" },
+
+ { "IN1R PGA", NULL, "IN2RN" },
+ { "IN1R PGA", NULL, "IN2RP" },
+ { "IN1R PGA", NULL, "IN1RN" },
+ { "IN1R PGA", NULL, "IN1RP" },
+
+ { "ADCL", NULL, "IN1L PGA" },
+
+ { "ADCR", NULL, "IN1R PGA" },
+
+ { "DMIC1L", NULL, "DMIC1DAT" },
+ { "DMIC1R", NULL, "DMIC1DAT" },
+ { "DMIC2L", NULL, "DMIC2DAT" },
+ { "DMIC2R", NULL, "DMIC2DAT" },
+
+ { "DMIC2L", NULL, "DMIC2" },
+ { "DMIC2R", NULL, "DMIC2" },
+ { "DMIC1L", NULL, "DMIC1" },
+ { "DMIC1R", NULL, "DMIC1" },
+
+ { "IN1L Mux", "ADC", "ADCL" },
+ { "IN1L Mux", "DMIC1", "DMIC1L" },
+ { "IN1L Mux", "DMIC2", "DMIC2L" },
+
+ { "IN1R Mux", "ADC", "ADCR" },
+ { "IN1R Mux", "DMIC1", "DMIC1R" },
+ { "IN1R Mux", "DMIC2", "DMIC2R" },
+
+ { "IN2L Mux", "ADC", "ADCL" },
+ { "IN2L Mux", "DMIC1", "DMIC1L" },
+ { "IN2L Mux", "DMIC2", "DMIC2L" },
+
+ { "IN2R Mux", "ADC", "ADCR" },
+ { "IN2R Mux", "DMIC1", "DMIC1R" },
+ { "IN2R Mux", "DMIC2", "DMIC2R" },
+
+ { "Left Sidetone", "IN1", "IN1L Mux" },
+ { "Left Sidetone", "IN2", "IN2L Mux" },
+
+ { "Right Sidetone", "IN1", "IN1R Mux" },
+ { "Right Sidetone", "IN2", "IN2R Mux" },
+
+ { "DSP1TXL", "IN1 Switch", "IN1L Mux" },
+ { "DSP1TXR", "IN1 Switch", "IN1R Mux" },
+
+ { "DSP2TXL", "IN1 Switch", "IN2L Mux" },
+ { "DSP2TXR", "IN1 Switch", "IN2R Mux" },
+
+ { "AIF1TX0", NULL, "DSP1TXL" },
+ { "AIF1TX1", NULL, "DSP1TXR" },
+ { "AIF1TX2", NULL, "DSP2TXL" },
+ { "AIF1TX3", NULL, "DSP2TXR" },
+ { "AIF1TX4", NULL, "AIF2RX0" },
+ { "AIF1TX5", NULL, "AIF2RX1" },
+
+ { "AIF1RX0", NULL, "AIFCLK" },
+ { "AIF1RX1", NULL, "AIFCLK" },
+ { "AIF1RX2", NULL, "AIFCLK" },
+ { "AIF1RX3", NULL, "AIFCLK" },
+ { "AIF1RX4", NULL, "AIFCLK" },
+ { "AIF1RX5", NULL, "AIFCLK" },
+
+ { "AIF2RX0", NULL, "AIFCLK" },
+ { "AIF2RX1", NULL, "AIFCLK" },
+
+ { "DSP1RXL", NULL, "SYSDSPCLK" },
+ { "DSP1RXR", NULL, "SYSDSPCLK" },
+ { "DSP2RXL", NULL, "SYSDSPCLK" },
+ { "DSP2RXR", NULL, "SYSDSPCLK" },
+ { "DSP1TXL", NULL, "SYSDSPCLK" },
+ { "DSP1TXR", NULL, "SYSDSPCLK" },
+ { "DSP2TXL", NULL, "SYSDSPCLK" },
+ { "DSP2TXR", NULL, "SYSDSPCLK" },
+
+ { "AIF1RXA", NULL, "AIF1RX0" },
+ { "AIF1RXA", NULL, "AIF1RX1" },
+ { "AIF1RXB", NULL, "AIF1RX2" },
+ { "AIF1RXB", NULL, "AIF1RX3" },
+ { "AIF1RXC", NULL, "AIF1RX4" },
+ { "AIF1RXC", NULL, "AIF1RX5" },
+
+ { "AIF2RX", NULL, "AIF2RX0" },
+ { "AIF2RX", NULL, "AIF2RX1" },
+
+ { "AIF2TX", "DSP2", "DSP2TX" },
+ { "AIF2TX", "DSP1", "DSP1RX" },
+ { "AIF2TX", "AIF1", "AIF1RXC" },
+
+ { "DSP1RXL", NULL, "DSP1RX" },
+ { "DSP1RXR", NULL, "DSP1RX" },
+ { "DSP2RXL", NULL, "DSP2RX" },
+ { "DSP2RXR", NULL, "DSP2RX" },
+
+ { "DSP2TX", NULL, "DSP2TXL" },
+ { "DSP2TX", NULL, "DSP2TXR" },
+
+ { "DSP1RX", "AIF1", "AIF1RXA" },
+ { "DSP1RX", "AIF2", "AIF2RX" },
+
+ { "DSP2RX", "AIF1", "AIF1RXB" },
+ { "DSP2RX", "AIF2", "AIF2RX" },
+
+ { "DAC2L Mixer", "DSP2 Switch", "DSP2RXL" },
+ { "DAC2L Mixer", "DSP1 Switch", "DSP1RXL" },
+ { "DAC2L Mixer", "Right Sidetone Switch", "Right Sidetone" },
+ { "DAC2L Mixer", "Left Sidetone Switch", "Left Sidetone" },
+
+ { "DAC2R Mixer", "DSP2 Switch", "DSP2RXR" },
+ { "DAC2R Mixer", "DSP1 Switch", "DSP1RXR" },
+ { "DAC2R Mixer", "Right Sidetone Switch", "Right Sidetone" },
+ { "DAC2R Mixer", "Left Sidetone Switch", "Left Sidetone" },
+
+ { "DAC1L Mixer", "DSP2 Switch", "DSP2RXL" },
+ { "DAC1L Mixer", "DSP1 Switch", "DSP1RXL" },
+ { "DAC1L Mixer", "Right Sidetone Switch", "Right Sidetone" },
+ { "DAC1L Mixer", "Left Sidetone Switch", "Left Sidetone" },
+
+ { "DAC1R Mixer", "DSP2 Switch", "DSP2RXR" },
+ { "DAC1R Mixer", "DSP1 Switch", "DSP1RXR" },
+ { "DAC1R Mixer", "Right Sidetone Switch", "Right Sidetone" },
+ { "DAC1R Mixer", "Left Sidetone Switch", "Left Sidetone" },
+
+ { "DAC1L", NULL, "DAC1L Mixer" },
+ { "DAC1R", NULL, "DAC1R Mixer" },
+ { "DAC2L", NULL, "DAC2L Mixer" },
+ { "DAC2R", NULL, "DAC2R Mixer" },
+
+ { "HPOUT2L PGA", NULL, "Charge Pump" },
+ { "HPOUT2L PGA", NULL, "DAC2L" },
+ { "HPOUT2L_DLY", NULL, "HPOUT2L PGA" },
+ { "HPOUT2L_DCS", NULL, "HPOUT2L_DLY" },
+ { "HPOUT2L_OUTP", NULL, "HPOUT2L_DCS" },
+ { "HPOUT2L_RMV_SHORT", NULL, "HPOUT2L_OUTP" },
+
+ { "HPOUT2R PGA", NULL, "Charge Pump" },
+ { "HPOUT2R PGA", NULL, "DAC2R" },
+ { "HPOUT2R_DLY", NULL, "HPOUT2R PGA" },
+ { "HPOUT2R_DCS", NULL, "HPOUT2R_DLY" },
+ { "HPOUT2R_OUTP", NULL, "HPOUT2R_DCS" },
+ { "HPOUT2R_RMV_SHORT", NULL, "HPOUT2R_OUTP" },
+
+ { "HPOUT1L PGA", NULL, "Charge Pump" },
+ { "HPOUT1L PGA", NULL, "DAC1L" },
+ { "HPOUT1L_DLY", NULL, "HPOUT1L PGA" },
+ { "HPOUT1L_DCS", NULL, "HPOUT1L_DLY" },
+ { "HPOUT1L_OUTP", NULL, "HPOUT1L_DCS" },
+ { "HPOUT1L_RMV_SHORT", NULL, "HPOUT1L_OUTP" },
+
+ { "HPOUT1R PGA", NULL, "Charge Pump" },
+ { "HPOUT1R PGA", NULL, "DAC1R" },
+ { "HPOUT1R_DLY", NULL, "HPOUT1R PGA" },
+ { "HPOUT1R_DCS", NULL, "HPOUT1R_DLY" },
+ { "HPOUT1R_OUTP", NULL, "HPOUT1R_DCS" },
+ { "HPOUT1R_RMV_SHORT", NULL, "HPOUT1R_OUTP" },
+
+ { "HPOUT2L", NULL, "HPOUT2L_RMV_SHORT" },
+ { "HPOUT2R", NULL, "HPOUT2R_RMV_SHORT" },
+ { "HPOUT1L", NULL, "HPOUT1L_RMV_SHORT" },
+ { "HPOUT1R", NULL, "HPOUT1R_RMV_SHORT" },
+
+ { "SPKL", "DAC1L", "DAC1L" },
+ { "SPKL", "DAC1R", "DAC1R" },
+ { "SPKL", "DAC2L", "DAC2L" },
+ { "SPKL", "DAC2R", "DAC2R" },
+
+ { "SPKR", "DAC1L", "DAC1L" },
+ { "SPKR", "DAC1R", "DAC1R" },
+ { "SPKR", "DAC2L", "DAC2L" },
+ { "SPKR", "DAC2R", "DAC2R" },
+
+ { "SPKL PGA", NULL, "SPKL" },
+ { "SPKR PGA", NULL, "SPKR" },
+
+ { "SPKDAT", NULL, "SPKL PGA" },
+ { "SPKDAT", NULL, "SPKR PGA" },
+};
+
+static int wm8996_readable_register(struct snd_soc_codec *codec,
+ unsigned int reg)
+{
+ /* Due to the sparseness of the register map the compiler
+ * output from an explicit switch statement ends up being much
+ * more efficient than a table.
+ */
+ switch (reg) {
+ case WM8996_SOFTWARE_RESET:
+ case WM8996_POWER_MANAGEMENT_1:
+ case WM8996_POWER_MANAGEMENT_2:
+ case WM8996_POWER_MANAGEMENT_3:
+ case WM8996_POWER_MANAGEMENT_4:
+ case WM8996_POWER_MANAGEMENT_5:
+ case WM8996_POWER_MANAGEMENT_6:
+ case WM8996_POWER_MANAGEMENT_7:
+ case WM8996_POWER_MANAGEMENT_8:
+ case WM8996_LEFT_LINE_INPUT_VOLUME:
+ case WM8996_RIGHT_LINE_INPUT_VOLUME:
+ case WM8996_LINE_INPUT_CONTROL:
+ case WM8996_DAC1_HPOUT1_VOLUME:
+ case WM8996_DAC2_HPOUT2_VOLUME:
+ case WM8996_DAC1_LEFT_VOLUME:
+ case WM8996_DAC1_RIGHT_VOLUME:
+ case WM8996_DAC2_LEFT_VOLUME:
+ case WM8996_DAC2_RIGHT_VOLUME:
+ case WM8996_OUTPUT1_LEFT_VOLUME:
+ case WM8996_OUTPUT1_RIGHT_VOLUME:
+ case WM8996_OUTPUT2_LEFT_VOLUME:
+ case WM8996_OUTPUT2_RIGHT_VOLUME:
+ case WM8996_MICBIAS_1:
+ case WM8996_MICBIAS_2:
+ case WM8996_LDO_1:
+ case WM8996_LDO_2:
+ case WM8996_ACCESSORY_DETECT_MODE_1:
+ case WM8996_ACCESSORY_DETECT_MODE_2:
+ case WM8996_HEADPHONE_DETECT_1:
+ case WM8996_HEADPHONE_DETECT_2:
+ case WM8996_MIC_DETECT_1:
+ case WM8996_MIC_DETECT_2:
+ case WM8996_MIC_DETECT_3:
+ case WM8996_CHARGE_PUMP_1:
+ case WM8996_CHARGE_PUMP_2:
+ case WM8996_DC_SERVO_1:
+ case WM8996_DC_SERVO_2:
+ case WM8996_DC_SERVO_3:
+ case WM8996_DC_SERVO_5:
+ case WM8996_DC_SERVO_6:
+ case WM8996_DC_SERVO_7:
+ case WM8996_DC_SERVO_READBACK_0:
+ case WM8996_ANALOGUE_HP_1:
+ case WM8996_ANALOGUE_HP_2:
+ case WM8996_CHIP_REVISION:
+ case WM8996_CONTROL_INTERFACE_1:
+ case WM8996_WRITE_SEQUENCER_CTRL_1:
+ case WM8996_WRITE_SEQUENCER_CTRL_2:
+ case WM8996_AIF_CLOCKING_1:
+ case WM8996_AIF_CLOCKING_2:
+ case WM8996_CLOCKING_1:
+ case WM8996_CLOCKING_2:
+ case WM8996_AIF_RATE:
+ case WM8996_FLL_CONTROL_1:
+ case WM8996_FLL_CONTROL_2:
+ case WM8996_FLL_CONTROL_3:
+ case WM8996_FLL_CONTROL_4:
+ case WM8996_FLL_CONTROL_5:
+ case WM8996_FLL_CONTROL_6:
+ case WM8996_FLL_EFS_1:
+ case WM8996_FLL_EFS_2:
+ case WM8996_AIF1_CONTROL:
+ case WM8996_AIF1_BCLK:
+ case WM8996_AIF1_TX_LRCLK_1:
+ case WM8996_AIF1_TX_LRCLK_2:
+ case WM8996_AIF1_RX_LRCLK_1:
+ case WM8996_AIF1_RX_LRCLK_2:
+ case WM8996_AIF1TX_DATA_CONFIGURATION_1:
+ case WM8996_AIF1TX_DATA_CONFIGURATION_2:
+ case WM8996_AIF1RX_DATA_CONFIGURATION:
+ case WM8996_AIF1TX_CHANNEL_0_CONFIGURATION:
+ case WM8996_AIF1TX_CHANNEL_1_CONFIGURATION:
+ case WM8996_AIF1TX_CHANNEL_2_CONFIGURATION:
+ case WM8996_AIF1TX_CHANNEL_3_CONFIGURATION:
+ case WM8996_AIF1TX_CHANNEL_4_CONFIGURATION:
+ case WM8996_AIF1TX_CHANNEL_5_CONFIGURATION:
+ case WM8996_AIF1RX_CHANNEL_0_CONFIGURATION:
+ case WM8996_AIF1RX_CHANNEL_1_CONFIGURATION:
+ case WM8996_AIF1RX_CHANNEL_2_CONFIGURATION:
+ case WM8996_AIF1RX_CHANNEL_3_CONFIGURATION:
+ case WM8996_AIF1RX_CHANNEL_4_CONFIGURATION:
+ case WM8996_AIF1RX_CHANNEL_5_CONFIGURATION:
+ case WM8996_AIF1RX_MONO_CONFIGURATION:
+ case WM8996_AIF1TX_TEST:
+ case WM8996_AIF2_CONTROL:
+ case WM8996_AIF2_BCLK:
+ case WM8996_AIF2_TX_LRCLK_1:
+ case WM8996_AIF2_TX_LRCLK_2:
+ case WM8996_AIF2_RX_LRCLK_1:
+ case WM8996_AIF2_RX_LRCLK_2:
+ case WM8996_AIF2TX_DATA_CONFIGURATION_1:
+ case WM8996_AIF2TX_DATA_CONFIGURATION_2:
+ case WM8996_AIF2RX_DATA_CONFIGURATION:
+ case WM8996_AIF2TX_CHANNEL_0_CONFIGURATION:
+ case WM8996_AIF2TX_CHANNEL_1_CONFIGURATION:
+ case WM8996_AIF2RX_CHANNEL_0_CONFIGURATION:
+ case WM8996_AIF2RX_CHANNEL_1_CONFIGURATION:
+ case WM8996_AIF2RX_MONO_CONFIGURATION:
+ case WM8996_AIF2TX_TEST:
+ case WM8996_DSP1_TX_LEFT_VOLUME:
+ case WM8996_DSP1_TX_RIGHT_VOLUME:
+ case WM8996_DSP1_RX_LEFT_VOLUME:
+ case WM8996_DSP1_RX_RIGHT_VOLUME:
+ case WM8996_DSP1_TX_FILTERS:
+ case WM8996_DSP1_RX_FILTERS_1:
+ case WM8996_DSP1_RX_FILTERS_2:
+ case WM8996_DSP1_DRC_1:
+ case WM8996_DSP1_DRC_2:
+ case WM8996_DSP1_DRC_3:
+ case WM8996_DSP1_DRC_4:
+ case WM8996_DSP1_DRC_5:
+ case WM8996_DSP1_RX_EQ_GAINS_1:
+ case WM8996_DSP1_RX_EQ_GAINS_2:
+ case WM8996_DSP1_RX_EQ_BAND_1_A:
+ case WM8996_DSP1_RX_EQ_BAND_1_B:
+ case WM8996_DSP1_RX_EQ_BAND_1_PG:
+ case WM8996_DSP1_RX_EQ_BAND_2_A:
+ case WM8996_DSP1_RX_EQ_BAND_2_B:
+ case WM8996_DSP1_RX_EQ_BAND_2_C:
+ case WM8996_DSP1_RX_EQ_BAND_2_PG:
+ case WM8996_DSP1_RX_EQ_BAND_3_A:
+ case WM8996_DSP1_RX_EQ_BAND_3_B:
+ case WM8996_DSP1_RX_EQ_BAND_3_C:
+ case WM8996_DSP1_RX_EQ_BAND_3_PG:
+ case WM8996_DSP1_RX_EQ_BAND_4_A:
+ case WM8996_DSP1_RX_EQ_BAND_4_B:
+ case WM8996_DSP1_RX_EQ_BAND_4_C:
+ case WM8996_DSP1_RX_EQ_BAND_4_PG:
+ case WM8996_DSP1_RX_EQ_BAND_5_A:
+ case WM8996_DSP1_RX_EQ_BAND_5_B:
+ case WM8996_DSP1_RX_EQ_BAND_5_PG:
+ case WM8996_DSP2_TX_LEFT_VOLUME:
+ case WM8996_DSP2_TX_RIGHT_VOLUME:
+ case WM8996_DSP2_RX_LEFT_VOLUME:
+ case WM8996_DSP2_RX_RIGHT_VOLUME:
+ case WM8996_DSP2_TX_FILTERS:
+ case WM8996_DSP2_RX_FILTERS_1:
+ case WM8996_DSP2_RX_FILTERS_2:
+ case WM8996_DSP2_DRC_1:
+ case WM8996_DSP2_DRC_2:
+ case WM8996_DSP2_DRC_3:
+ case WM8996_DSP2_DRC_4:
+ case WM8996_DSP2_DRC_5:
+ case WM8996_DSP2_RX_EQ_GAINS_1:
+ case WM8996_DSP2_RX_EQ_GAINS_2:
+ case WM8996_DSP2_RX_EQ_BAND_1_A:
+ case WM8996_DSP2_RX_EQ_BAND_1_B:
+ case WM8996_DSP2_RX_EQ_BAND_1_PG:
+ case WM8996_DSP2_RX_EQ_BAND_2_A:
+ case WM8996_DSP2_RX_EQ_BAND_2_B:
+ case WM8996_DSP2_RX_EQ_BAND_2_C:
+ case WM8996_DSP2_RX_EQ_BAND_2_PG:
+ case WM8996_DSP2_RX_EQ_BAND_3_A:
+ case WM8996_DSP2_RX_EQ_BAND_3_B:
+ case WM8996_DSP2_RX_EQ_BAND_3_C:
+ case WM8996_DSP2_RX_EQ_BAND_3_PG:
+ case WM8996_DSP2_RX_EQ_BAND_4_A:
+ case WM8996_DSP2_RX_EQ_BAND_4_B:
+ case WM8996_DSP2_RX_EQ_BAND_4_C:
+ case WM8996_DSP2_RX_EQ_BAND_4_PG:
+ case WM8996_DSP2_RX_EQ_BAND_5_A:
+ case WM8996_DSP2_RX_EQ_BAND_5_B:
+ case WM8996_DSP2_RX_EQ_BAND_5_PG:
+ case WM8996_DAC1_MIXER_VOLUMES:
+ case WM8996_DAC1_LEFT_MIXER_ROUTING:
+ case WM8996_DAC1_RIGHT_MIXER_ROUTING:
+ case WM8996_DAC2_MIXER_VOLUMES:
+ case WM8996_DAC2_LEFT_MIXER_ROUTING:
+ case WM8996_DAC2_RIGHT_MIXER_ROUTING:
+ case WM8996_DSP1_TX_LEFT_MIXER_ROUTING:
+ case WM8996_DSP1_TX_RIGHT_MIXER_ROUTING:
+ case WM8996_DSP2_TX_LEFT_MIXER_ROUTING:
+ case WM8996_DSP2_TX_RIGHT_MIXER_ROUTING:
+ case WM8996_DSP_TX_MIXER_SELECT:
+ case WM8996_DAC_SOFTMUTE:
+ case WM8996_OVERSAMPLING:
+ case WM8996_SIDETONE:
+ case WM8996_GPIO_1:
+ case WM8996_GPIO_2:
+ case WM8996_GPIO_3:
+ case WM8996_GPIO_4:
+ case WM8996_GPIO_5:
+ case WM8996_PULL_CONTROL_1:
+ case WM8996_PULL_CONTROL_2:
+ case WM8996_INTERRUPT_STATUS_1:
+ case WM8996_INTERRUPT_STATUS_2:
+ case WM8996_INTERRUPT_RAW_STATUS_2:
+ case WM8996_INTERRUPT_STATUS_1_MASK:
+ case WM8996_INTERRUPT_STATUS_2_MASK:
+ case WM8996_INTERRUPT_CONTROL:
+ case WM8996_LEFT_PDM_SPEAKER:
+ case WM8996_RIGHT_PDM_SPEAKER:
+ case WM8996_PDM_SPEAKER_MUTE_SEQUENCE:
+ case WM8996_PDM_SPEAKER_VOLUME:
+ return 1;
+ default:
+ return 0;
+ }
+}
+
+static int wm8996_volatile_register(struct snd_soc_codec *codec,
+ unsigned int reg)
+{
+ switch (reg) {
+ case WM8996_SOFTWARE_RESET:
+ case WM8996_CHIP_REVISION:
+ case WM8996_LDO_1:
+ case WM8996_LDO_2:
+ case WM8996_INTERRUPT_STATUS_1:
+ case WM8996_INTERRUPT_STATUS_2:
+ case WM8996_INTERRUPT_RAW_STATUS_2:
+ case WM8996_DC_SERVO_READBACK_0:
+ case WM8996_DC_SERVO_2:
+ case WM8996_DC_SERVO_6:
+ case WM8996_DC_SERVO_7:
+ case WM8996_FLL_CONTROL_6:
+ case WM8996_MIC_DETECT_3:
+ case WM8996_HEADPHONE_DETECT_1:
+ case WM8996_HEADPHONE_DETECT_2:
+ return 1;
+ default:
+ return 0;
+ }
+}
+
+static int wm8996_reset(struct snd_soc_codec *codec)
+{
+ return snd_soc_write(codec, WM8996_SOFTWARE_RESET, 0x8915);
+}
+
+static const int bclk_divs[] = {
+ 1, 2, 3, 4, 6, 8, 12, 16, 24, 32, 48, 64, 96
+};
+
+static void wm8996_update_bclk(struct snd_soc_codec *codec)
+{
+ struct wm8996_priv *wm8996 = snd_soc_codec_get_drvdata(codec);
+ int aif, best, cur_val, bclk_rate, bclk_reg, i;
+
+ /* Don't bother if we're in a low frequency idle mode that
+ * can't support audio.
+ */
+ if (wm8996->sysclk < 64000)
+ return;
+
+ for (aif = 0; aif < WM8996_AIFS; aif++) {
+ switch (aif) {
+ case 0:
+ bclk_reg = WM8996_AIF1_BCLK;
+ break;
+ case 1:
+ bclk_reg = WM8996_AIF2_BCLK;
+ break;
+ }
+
+ bclk_rate = wm8996->bclk_rate[aif];
+
+ /* Pick a divisor for BCLK as close as we can get to ideal */
+ best = 0;
+ for (i = 0; i < ARRAY_SIZE(bclk_divs); i++) {
+ cur_val = (wm8996->sysclk / bclk_divs[i]) - bclk_rate;
+ if (cur_val < 0) /* BCLK table is sorted */
+ break;
+ best = i;
+ }
+ bclk_rate = wm8996->sysclk / bclk_divs[best];
+ dev_dbg(codec->dev, "Using BCLK_DIV %d for actual BCLK %dHz\n",
+ bclk_divs[best], bclk_rate);
+
+ snd_soc_update_bits(codec, bclk_reg,
+ WM8996_AIF1_BCLK_DIV_MASK, best);
+ }
+}
+
+static int wm8996_set_bias_level(struct snd_soc_codec *codec,
+ enum snd_soc_bias_level level)
+{
+ struct wm8996_priv *wm8996 = snd_soc_codec_get_drvdata(codec);
+ int ret;
+
+ switch (level) {
+ case SND_SOC_BIAS_ON:
+ break;
+
+ case SND_SOC_BIAS_PREPARE:
+ if (codec->dapm.bias_level == SND_SOC_BIAS_STANDBY) {
+ snd_soc_update_bits(codec, WM8996_POWER_MANAGEMENT_1,
+ WM8996_BG_ENA, WM8996_BG_ENA);
+ msleep(2);
+ }
+ break;
+
+ case SND_SOC_BIAS_STANDBY:
+ if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) {
+ ret = regulator_bulk_enable(ARRAY_SIZE(wm8996->supplies),
+ wm8996->supplies);
+ if (ret != 0) {
+ dev_err(codec->dev,
+ "Failed to enable supplies: %d\n",
+ ret);
+ return ret;
+ }
+
+ if (wm8996->pdata.ldo_ena >= 0) {
+ gpio_set_value_cansleep(wm8996->pdata.ldo_ena,
+ 1);
+ msleep(5);
+ }
+
+ codec->cache_only = false;
+ snd_soc_cache_sync(codec);
+ }
+
+ snd_soc_update_bits(codec, WM8996_POWER_MANAGEMENT_1,
+ WM8996_BG_ENA, 0);
+ break;
+
+ case SND_SOC_BIAS_OFF:
+ codec->cache_only = true;
+ if (wm8996->pdata.ldo_ena >= 0)
+ gpio_set_value_cansleep(wm8996->pdata.ldo_ena, 0);
+ regulator_bulk_disable(ARRAY_SIZE(wm8996->supplies),
+ wm8996->supplies);
+ break;
+ }
+
+ codec->dapm.bias_level = level;
+
+ return 0;
+}
+
+static int wm8996_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ int aifctrl = 0;
+ int bclk = 0;
+ int lrclk_tx = 0;
+ int lrclk_rx = 0;
+ int aifctrl_reg, bclk_reg, lrclk_tx_reg, lrclk_rx_reg;
+
+ switch (dai->id) {
+ case 0:
+ aifctrl_reg = WM8996_AIF1_CONTROL;
+ bclk_reg = WM8996_AIF1_BCLK;
+ lrclk_tx_reg = WM8996_AIF1_TX_LRCLK_2;
+ lrclk_rx_reg = WM8996_AIF1_RX_LRCLK_2;
+ break;
+ case 1:
+ aifctrl_reg = WM8996_AIF2_CONTROL;
+ bclk_reg = WM8996_AIF2_BCLK;
+ lrclk_tx_reg = WM8996_AIF2_TX_LRCLK_2;
+ lrclk_rx_reg = WM8996_AIF2_RX_LRCLK_2;
+ break;
+ default:
+ BUG();
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_NF:
+ break;
+ case SND_SOC_DAIFMT_IB_NF:
+ bclk |= WM8996_AIF1_BCLK_INV;
+ break;
+ case SND_SOC_DAIFMT_NB_IF:
+ lrclk_tx |= WM8996_AIF1TX_LRCLK_INV;
+ lrclk_rx |= WM8996_AIF1RX_LRCLK_INV;
+ break;
+ case SND_SOC_DAIFMT_IB_IF:
+ bclk |= WM8996_AIF1_BCLK_INV;
+ lrclk_tx |= WM8996_AIF1TX_LRCLK_INV;
+ lrclk_rx |= WM8996_AIF1RX_LRCLK_INV;
+ break;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+ case SND_SOC_DAIFMT_CBS_CFS:
+ break;
+ case SND_SOC_DAIFMT_CBS_CFM:
+ lrclk_tx |= WM8996_AIF1TX_LRCLK_MSTR;
+ lrclk_rx |= WM8996_AIF1RX_LRCLK_MSTR;
+ break;
+ case SND_SOC_DAIFMT_CBM_CFS:
+ bclk |= WM8996_AIF1_BCLK_MSTR;
+ break;
+ case SND_SOC_DAIFMT_CBM_CFM:
+ bclk |= WM8996_AIF1_BCLK_MSTR;
+ lrclk_tx |= WM8996_AIF1TX_LRCLK_MSTR;
+ lrclk_rx |= WM8996_AIF1RX_LRCLK_MSTR;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_DSP_A:
+ break;
+ case SND_SOC_DAIFMT_DSP_B:
+ aifctrl |= 1;
+ break;
+ case SND_SOC_DAIFMT_I2S:
+ aifctrl |= 2;
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ aifctrl |= 3;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ snd_soc_update_bits(codec, aifctrl_reg, WM8996_AIF1_FMT_MASK, aifctrl);
+ snd_soc_update_bits(codec, bclk_reg,
+ WM8996_AIF1_BCLK_INV | WM8996_AIF1_BCLK_MSTR,
+ bclk);
+ snd_soc_update_bits(codec, lrclk_tx_reg,
+ WM8996_AIF1TX_LRCLK_INV |
+ WM8996_AIF1TX_LRCLK_MSTR,
+ lrclk_tx);
+ snd_soc_update_bits(codec, lrclk_rx_reg,
+ WM8996_AIF1RX_LRCLK_INV |
+ WM8996_AIF1RX_LRCLK_MSTR,
+ lrclk_rx);
+
+ return 0;
+}
+
+static const int dsp_divs[] = {
+ 48000, 32000, 16000, 8000
+};
+
+static int wm8996_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ struct wm8996_priv *wm8996 = snd_soc_codec_get_drvdata(codec);
+ int bits, i, bclk_rate;
+ int aifdata = 0;
+ int lrclk = 0;
+ int dsp = 0;
+ int aifdata_reg, lrclk_reg, dsp_shift;
+
+ switch (dai->id) {
+ case 0:
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK ||
+ (snd_soc_read(codec, WM8996_GPIO_1)) & WM8996_GP1_FN_MASK) {
+ aifdata_reg = WM8996_AIF1RX_DATA_CONFIGURATION;
+ lrclk_reg = WM8996_AIF1_RX_LRCLK_1;
+ } else {
+ aifdata_reg = WM8996_AIF1TX_DATA_CONFIGURATION_1;
+ lrclk_reg = WM8996_AIF1_TX_LRCLK_1;
+ }
+ dsp_shift = 0;
+ break;
+ case 1:
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK ||
+ (snd_soc_read(codec, WM8996_GPIO_2)) & WM8996_GP2_FN_MASK) {
+ aifdata_reg = WM8996_AIF2RX_DATA_CONFIGURATION;
+ lrclk_reg = WM8996_AIF2_RX_LRCLK_1;
+ } else {
+ aifdata_reg = WM8996_AIF2TX_DATA_CONFIGURATION_1;
+ lrclk_reg = WM8996_AIF2_TX_LRCLK_1;
+ }
+ dsp_shift = WM8996_DSP2_DIV_SHIFT;
+ break;
+ default:
+ BUG();
+ return -EINVAL;
+ }
+
+ bclk_rate = snd_soc_params_to_bclk(params);
+ if (bclk_rate < 0) {
+ dev_err(codec->dev, "Unsupported BCLK rate: %d\n", bclk_rate);
+ return bclk_rate;
+ }
+
+ wm8996->bclk_rate[dai->id] = bclk_rate;
+ wm8996->rx_rate[dai->id] = params_rate(params);
+
+ /* Needs looking at for TDM */
+ bits = snd_pcm_format_width(params_format(params));
+ if (bits < 0)
+ return bits;
+ aifdata |= (bits << WM8996_AIF1TX_WL_SHIFT) | bits;
+
+ for (i = 0; i < ARRAY_SIZE(dsp_divs); i++) {
+ if (dsp_divs[i] == params_rate(params))
+ break;
+ }
+ if (i == ARRAY_SIZE(dsp_divs)) {
+ dev_err(codec->dev, "Unsupported sample rate %dHz\n",
+ params_rate(params));
+ return -EINVAL;
+ }
+ dsp |= i << dsp_shift;
+
+ wm8996_update_bclk(codec);
+
+ lrclk = bclk_rate / params_rate(params);
+ dev_dbg(dai->dev, "Using LRCLK rate %d for actual LRCLK %dHz\n",
+ lrclk, bclk_rate / lrclk);
+
+ snd_soc_update_bits(codec, aifdata_reg,
+ WM8996_AIF1TX_WL_MASK |
+ WM8996_AIF1TX_SLOT_LEN_MASK,
+ aifdata);
+ snd_soc_update_bits(codec, lrclk_reg, WM8996_AIF1RX_RATE_MASK,
+ lrclk);
+ snd_soc_update_bits(codec, WM8996_AIF_CLOCKING_2,
+ WM8996_DSP1_DIV_SHIFT << dsp_shift, dsp);
+
+ return 0;
+}
+
+static int wm8996_set_sysclk(struct snd_soc_dai *dai,
+ int clk_id, unsigned int freq, int dir)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ struct wm8996_priv *wm8996 = snd_soc_codec_get_drvdata(codec);
+ int lfclk = 0;
+ int ratediv = 0;
+ int src;
+ int old;
+
+ if (freq == wm8996->sysclk && clk_id == wm8996->sysclk_src)
+ return 0;
+
+ /* Disable SYSCLK while we reconfigure */
+ old = snd_soc_read(codec, WM8996_AIF_CLOCKING_1) & WM8996_SYSCLK_ENA;
+ snd_soc_update_bits(codec, WM8996_AIF_CLOCKING_1,
+ WM8996_SYSCLK_ENA, 0);
+
+ switch (clk_id) {
+ case WM8996_SYSCLK_MCLK1:
+ wm8996->sysclk = freq;
+ src = 0;
+ break;
+ case WM8996_SYSCLK_MCLK2:
+ wm8996->sysclk = freq;
+ src = 1;
+ break;
+ case WM8996_SYSCLK_FLL:
+ wm8996->sysclk = freq;
+ src = 2;
+ break;
+ default:
+ dev_err(codec->dev, "Unsupported clock source %d\n", clk_id);
+ return -EINVAL;
+ }
+
+ switch (wm8996->sysclk) {
+ case 6144000:
+ snd_soc_update_bits(codec, WM8996_AIF_RATE,
+ WM8996_SYSCLK_RATE, 0);
+ break;
+ case 24576000:
+ ratediv = WM8996_SYSCLK_DIV;
+ case 12288000:
+ snd_soc_update_bits(codec, WM8996_AIF_RATE,
+ WM8996_SYSCLK_RATE, WM8996_SYSCLK_RATE);
+ break;
+ case 32000:
+ case 32768:
+ lfclk = WM8996_LFCLK_ENA;
+ break;
+ default:
+ dev_warn(codec->dev, "Unsupported clock rate %dHz\n",
+ wm8996->sysclk);
+ return -EINVAL;
+ }
+
+ wm8996_update_bclk(codec);
+
+ snd_soc_update_bits(codec, WM8996_AIF_CLOCKING_1,
+ WM8996_SYSCLK_SRC_MASK | WM8996_SYSCLK_DIV_MASK,
+ src << WM8996_SYSCLK_SRC_SHIFT | ratediv);
+ snd_soc_update_bits(codec, WM8996_CLOCKING_1, WM8996_LFCLK_ENA, lfclk);
+ snd_soc_update_bits(codec, WM8996_AIF_CLOCKING_1,
+ WM8996_SYSCLK_ENA, old);
+
+ wm8996->sysclk_src = clk_id;
+
+ return 0;
+}
+
+struct _fll_div {
+ u16 fll_fratio;
+ u16 fll_outdiv;
+ u16 fll_refclk_div;
+ u16 fll_loop_gain;
+ u16 fll_ref_freq;
+ u16 n;
+ u16 theta;
+ u16 lambda;
+};
+
+static struct {
+ unsigned int min;
+ unsigned int max;
+ u16 fll_fratio;
+ int ratio;
+} fll_fratios[] = {
+ { 0, 64000, 4, 16 },
+ { 64000, 128000, 3, 8 },
+ { 128000, 256000, 2, 4 },
+ { 256000, 1000000, 1, 2 },
+ { 1000000, 13500000, 0, 1 },
+};
+
+static int fll_factors(struct _fll_div *fll_div, unsigned int Fref,
+ unsigned int Fout)
+{
+ unsigned int target;
+ unsigned int div;
+ unsigned int fratio, gcd_fll;
+ int i;
+
+ /* Fref must be <=13.5MHz */
+ div = 1;
+ fll_div->fll_refclk_div = 0;
+ while ((Fref / div) > 13500000) {
+ div *= 2;
+ fll_div->fll_refclk_div++;
+
+ if (div > 8) {
+ pr_err("Can't scale %dMHz input down to <=13.5MHz\n",
+ Fref);
+ return -EINVAL;
+ }
+ }
+
+ pr_debug("FLL Fref=%u Fout=%u\n", Fref, Fout);
+
+ /* Apply the division for our remaining calculations */
+ Fref /= div;
+
+ if (Fref >= 3000000)
+ fll_div->fll_loop_gain = 5;
+ else
+ fll_div->fll_loop_gain = 0;
+
+ if (Fref >= 48000)
+ fll_div->fll_ref_freq = 0;
+ else
+ fll_div->fll_ref_freq = 1;
+
+ /* Fvco should be 90-100MHz; don't check the upper bound */
+ div = 2;
+ while (Fout * div < 90000000) {
+ div++;
+ if (div > 64) {
+ pr_err("Unable to find FLL_OUTDIV for Fout=%uHz\n",
+ Fout);
+ return -EINVAL;
+ }
+ }
+ target = Fout * div;
+ fll_div->fll_outdiv = div - 1;
+
+ pr_debug("FLL Fvco=%dHz\n", target);
+
+ /* Find an appropraite FLL_FRATIO and factor it out of the target */
+ for (i = 0; i < ARRAY_SIZE(fll_fratios); i++) {
+ if (fll_fratios[i].min <= Fref && Fref <= fll_fratios[i].max) {
+ fll_div->fll_fratio = fll_fratios[i].fll_fratio;
+ fratio = fll_fratios[i].ratio;
+ break;
+ }
+ }
+ if (i == ARRAY_SIZE(fll_fratios)) {
+ pr_err("Unable to find FLL_FRATIO for Fref=%uHz\n", Fref);
+ return -EINVAL;
+ }
+
+ fll_div->n = target / (fratio * Fref);
+
+ if (target % Fref == 0) {
+ fll_div->theta = 0;
+ fll_div->lambda = 0;
+ } else {
+ gcd_fll = gcd(target, fratio * Fref);
+
+ fll_div->theta = (target - (fll_div->n * fratio * Fref))
+ / gcd_fll;
+ fll_div->lambda = (fratio * Fref) / gcd_fll;
+ }
+
+ pr_debug("FLL N=%x THETA=%x LAMBDA=%x\n",
+ fll_div->n, fll_div->theta, fll_div->lambda);
+ pr_debug("FLL_FRATIO=%x FLL_OUTDIV=%x FLL_REFCLK_DIV=%x\n",
+ fll_div->fll_fratio, fll_div->fll_outdiv,
+ fll_div->fll_refclk_div);
+
+ return 0;
+}
+
+static int wm8996_set_fll(struct snd_soc_codec *codec, int fll_id, int source,
+ unsigned int Fref, unsigned int Fout)
+{
+ struct wm8996_priv *wm8996 = snd_soc_codec_get_drvdata(codec);
+ struct i2c_client *i2c = to_i2c_client(codec->dev);
+ struct _fll_div fll_div;
+ unsigned long timeout;
+ int ret, reg;
+
+ /* Any change? */
+ if (source == wm8996->fll_src && Fref == wm8996->fll_fref &&
+ Fout == wm8996->fll_fout)
+ return 0;
+
+ if (Fout == 0) {
+ dev_dbg(codec->dev, "FLL disabled\n");
+
+ wm8996->fll_fref = 0;
+ wm8996->fll_fout = 0;
+
+ snd_soc_update_bits(codec, WM8996_FLL_CONTROL_1,
+ WM8996_FLL_ENA, 0);
+
+ return 0;
+ }
+
+ ret = fll_factors(&fll_div, Fref, Fout);
+ if (ret != 0)
+ return ret;
+
+ switch (source) {
+ case WM8996_FLL_MCLK1:
+ reg = 0;
+ break;
+ case WM8996_FLL_MCLK2:
+ reg = 1;
+ break;
+ case WM8996_FLL_DACLRCLK1:
+ reg = 2;
+ break;
+ case WM8996_FLL_BCLK1:
+ reg = 3;
+ break;
+ default:
+ dev_err(codec->dev, "Unknown FLL source %d\n", ret);
+ return -EINVAL;
+ }
+
+ reg |= fll_div.fll_refclk_div << WM8996_FLL_REFCLK_DIV_SHIFT;
+ reg |= fll_div.fll_ref_freq << WM8996_FLL_REF_FREQ_SHIFT;
+
+ snd_soc_update_bits(codec, WM8996_FLL_CONTROL_5,
+ WM8996_FLL_REFCLK_DIV_MASK | WM8996_FLL_REF_FREQ |
+ WM8996_FLL_REFCLK_SRC_MASK, reg);
+
+ reg = 0;
+ if (fll_div.theta || fll_div.lambda)
+ reg |= WM8996_FLL_EFS_ENA | (3 << WM8996_FLL_LFSR_SEL_SHIFT);
+ else
+ reg |= 1 << WM8996_FLL_LFSR_SEL_SHIFT;
+ snd_soc_write(codec, WM8996_FLL_EFS_2, reg);
+
+ snd_soc_update_bits(codec, WM8996_FLL_CONTROL_2,
+ WM8996_FLL_OUTDIV_MASK |
+ WM8996_FLL_FRATIO_MASK,
+ (fll_div.fll_outdiv << WM8996_FLL_OUTDIV_SHIFT) |
+ (fll_div.fll_fratio));
+
+ snd_soc_write(codec, WM8996_FLL_CONTROL_3, fll_div.theta);
+
+ snd_soc_update_bits(codec, WM8996_FLL_CONTROL_4,
+ WM8996_FLL_N_MASK | WM8996_FLL_LOOP_GAIN_MASK,
+ (fll_div.n << WM8996_FLL_N_SHIFT) |
+ fll_div.fll_loop_gain);
+
+ snd_soc_write(codec, WM8996_FLL_EFS_1, fll_div.lambda);
+
+ snd_soc_update_bits(codec, WM8996_FLL_CONTROL_1,
+ WM8996_FLL_ENA, WM8996_FLL_ENA);
+
+ /* The FLL supports live reconfiguration - kick that in case we were
+ * already enabled.
+ */
+ snd_soc_write(codec, WM8996_FLL_CONTROL_6, WM8996_FLL_SWITCH_CLK);
+
+ /* Wait for the FLL to lock, using the interrupt if possible */
+ if (Fref > 1000000)
+ timeout = usecs_to_jiffies(300);
+ else
+ timeout = msecs_to_jiffies(2);
+
+ /* Allow substantially longer if we've actually got the IRQ */
+ if (i2c->irq)
+ timeout *= 1000;
+
+ ret = wait_for_completion_timeout(&wm8996->fll_lock, timeout);
+
+ if (ret == 0 && i2c->irq) {
+ dev_err(codec->dev, "Timed out waiting for FLL\n");
+ ret = -ETIMEDOUT;
+ } else {
+ ret = 0;
+ }
+
+ dev_dbg(codec->dev, "FLL configured for %dHz->%dHz\n", Fref, Fout);
+
+ wm8996->fll_fref = Fref;
+ wm8996->fll_fout = Fout;
+ wm8996->fll_src = source;
+
+ return ret;
+}
+
+#ifdef CONFIG_GPIOLIB
+static inline struct wm8996_priv *gpio_to_wm8996(struct gpio_chip *chip)
+{
+ return container_of(chip, struct wm8996_priv, gpio_chip);
+}
+
+static void wm8996_gpio_set(struct gpio_chip *chip, unsigned offset, int value)
+{
+ struct wm8996_priv *wm8996 = gpio_to_wm8996(chip);
+ struct snd_soc_codec *codec = wm8996->codec;
+
+ snd_soc_update_bits(codec, WM8996_GPIO_1 + offset,
+ WM8996_GP1_LVL, !!value << WM8996_GP1_LVL_SHIFT);
+}
+
+static int wm8996_gpio_direction_out(struct gpio_chip *chip,
+ unsigned offset, int value)
+{
+ struct wm8996_priv *wm8996 = gpio_to_wm8996(chip);
+ struct snd_soc_codec *codec = wm8996->codec;
+ int val;
+
+ val = (1 << WM8996_GP1_FN_SHIFT) | (!!value << WM8996_GP1_LVL_SHIFT);
+
+ return snd_soc_update_bits(codec, WM8996_GPIO_1 + offset,
+ WM8996_GP1_FN_MASK | WM8996_GP1_DIR |
+ WM8996_GP1_LVL, val);
+}
+
+static int wm8996_gpio_get(struct gpio_chip *chip, unsigned offset)
+{
+ struct wm8996_priv *wm8996 = gpio_to_wm8996(chip);
+ struct snd_soc_codec *codec = wm8996->codec;
+ int ret;
+
+ ret = snd_soc_read(codec, WM8996_GPIO_1 + offset);
+ if (ret < 0)
+ return ret;
+
+ return (ret & WM8996_GP1_LVL) != 0;
+}
+
+static int wm8996_gpio_direction_in(struct gpio_chip *chip, unsigned offset)
+{
+ struct wm8996_priv *wm8996 = gpio_to_wm8996(chip);
+ struct snd_soc_codec *codec = wm8996->codec;
+
+ return snd_soc_update_bits(codec, WM8996_GPIO_1 + offset,
+ WM8996_GP1_FN_MASK | WM8996_GP1_DIR,
+ (1 << WM8996_GP1_FN_SHIFT) |
+ (1 << WM8996_GP1_DIR_SHIFT));
+}
+
+static struct gpio_chip wm8996_template_chip = {
+ .label = "wm8996",
+ .owner = THIS_MODULE,
+ .direction_output = wm8996_gpio_direction_out,
+ .set = wm8996_gpio_set,
+ .direction_input = wm8996_gpio_direction_in,
+ .get = wm8996_gpio_get,
+ .can_sleep = 1,
+};
+
+static void wm8996_init_gpio(struct snd_soc_codec *codec)
+{
+ struct wm8996_priv *wm8996 = snd_soc_codec_get_drvdata(codec);
+ int ret;
+
+ wm8996->gpio_chip = wm8996_template_chip;
+ wm8996->gpio_chip.ngpio = 5;
+ wm8996->gpio_chip.dev = codec->dev;
+
+ if (wm8996->pdata.gpio_base)
+ wm8996->gpio_chip.base = wm8996->pdata.gpio_base;
+ else
+ wm8996->gpio_chip.base = -1;
+
+ ret = gpiochip_add(&wm8996->gpio_chip);
+ if (ret != 0)
+ dev_err(codec->dev, "Failed to add GPIOs: %d\n", ret);
+}
+
+static void wm8996_free_gpio(struct snd_soc_codec *codec)
+{
+ struct wm8996_priv *wm8996 = snd_soc_codec_get_drvdata(codec);
+ int ret;
+
+ ret = gpiochip_remove(&wm8996->gpio_chip);
+ if (ret != 0)
+ dev_err(codec->dev, "Failed to remove GPIOs: %d\n", ret);
+}
+#else
+static void wm8996_init_gpio(struct snd_soc_codec *codec)
+{
+}
+
+static void wm8996_free_gpio(struct snd_soc_codec *codec)
+{
+}
+#endif
+
+/**
+ * wm8996_detect - Enable default WM8996 jack detection
+ *
+ * The WM8996 has advanced accessory detection support for headsets.
+ * This function provides a default implementation which integrates
+ * the majority of this functionality with minimal user configuration.
+ *
+ * This will detect headset, headphone and short circuit button and
+ * will also detect inverted microphone ground connections and update
+ * the polarity of the connections.
+ */
+int wm8996_detect(struct snd_soc_codec *codec, struct snd_soc_jack *jack,
+ wm8996_polarity_fn polarity_cb)
+{
+ struct wm8996_priv *wm8996 = snd_soc_codec_get_drvdata(codec);
+
+ wm8996->jack = jack;
+ wm8996->detecting = true;
+ wm8996->polarity_cb = polarity_cb;
+
+ if (wm8996->polarity_cb)
+ wm8996->polarity_cb(codec, 0);
+
+ /* Clear discarge to avoid noise during detection */
+ snd_soc_update_bits(codec, WM8996_MICBIAS_1,
+ WM8996_MICB1_DISCH, 0);
+ snd_soc_update_bits(codec, WM8996_MICBIAS_2,
+ WM8996_MICB2_DISCH, 0);
+
+ /* LDO2 powers the microphones, SYSCLK clocks detection */
+ snd_soc_dapm_force_enable_pin(&codec->dapm, "LDO2");
+ snd_soc_dapm_force_enable_pin(&codec->dapm, "SYSCLK");
+
+ /* We start off just enabling microphone detection - even a
+ * plain headphone will trigger detection.
+ */
+ snd_soc_update_bits(codec, WM8996_MIC_DETECT_1,
+ WM8996_MICD_ENA, WM8996_MICD_ENA);
+
+ /* Slowest detection rate, gives debounce for initial detection */
+ snd_soc_update_bits(codec, WM8996_MIC_DETECT_1,
+ WM8996_MICD_RATE_MASK,
+ WM8996_MICD_RATE_MASK);
+
+ /* Enable interrupts and we're off */
+ snd_soc_update_bits(codec, WM8996_INTERRUPT_STATUS_2_MASK,
+ WM8996_IM_MICD_EINT, 0);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(wm8996_detect);
+
+static void wm8996_micd(struct snd_soc_codec *codec)
+{
+ struct wm8996_priv *wm8996 = snd_soc_codec_get_drvdata(codec);
+ int val, reg;
+
+ val = snd_soc_read(codec, WM8996_MIC_DETECT_3);
+
+ dev_dbg(codec->dev, "Microphone event: %x\n", val);
+
+ if (!(val & WM8996_MICD_VALID)) {
+ dev_warn(codec->dev, "Microphone detection state invalid\n");
+ return;
+ }
+
+ /* No accessory, reset everything and report removal */
+ if (!(val & WM8996_MICD_STS)) {
+ dev_dbg(codec->dev, "Jack removal detected\n");
+ wm8996->jack_mic = false;
+ wm8996->detecting = true;
+ snd_soc_jack_report(wm8996->jack, 0,
+ SND_JACK_HEADSET | SND_JACK_BTN_0);
+ snd_soc_update_bits(codec, WM8996_MIC_DETECT_1,
+ WM8996_MICD_RATE_MASK,
+ WM8996_MICD_RATE_MASK);
+ return;
+ }
+
+ /* If the measurement is very high we've got a microphone but
+ * do a little debounce to account for mechanical issues.
+ */
+ if (val & 0x400) {
+ dev_dbg(codec->dev, "Microphone detected\n");
+ snd_soc_jack_report(wm8996->jack, SND_JACK_HEADSET,
+ SND_JACK_HEADSET | SND_JACK_BTN_0);
+ wm8996->jack_mic = true;
+ wm8996->detecting = false;
+
+ /* Increase poll rate to give better responsiveness
+ * for buttons */
+ snd_soc_update_bits(codec, WM8996_MIC_DETECT_1,
+ WM8996_MICD_RATE_MASK,
+ 5 << WM8996_MICD_RATE_SHIFT);
+ }
+
+ /* If we detected a lower impedence during initial startup
+ * then we probably have the wrong polarity, flip it. Don't
+ * do this for the lowest impedences to speed up detection of
+ * plain headphones.
+ */
+ if (wm8996->detecting && (val & 0x3f0)) {
+ reg = snd_soc_read(codec, WM8996_ACCESSORY_DETECT_MODE_2);
+ reg ^= WM8996_HPOUT1FB_SRC | WM8996_MICD_SRC |
+ WM8996_MICD_BIAS_SRC;
+ snd_soc_update_bits(codec, WM8996_ACCESSORY_DETECT_MODE_2,
+ WM8996_HPOUT1FB_SRC | WM8996_MICD_SRC |
+ WM8996_MICD_BIAS_SRC, reg);
+
+ if (wm8996->polarity_cb)
+ wm8996->polarity_cb(codec,
+ (reg & WM8996_MICD_SRC) != 0);
+
+ dev_dbg(codec->dev, "Set microphone polarity to %d\n",
+ (reg & WM8996_MICD_SRC) != 0);
+
+ return;
+ }
+
+ /* Don't distinguish between buttons, just report any low
+ * impedence as BTN_0.
+ */
+ if (val & 0x3fc) {
+ if (wm8996->jack_mic) {
+ dev_dbg(codec->dev, "Mic button detected\n");
+ snd_soc_jack_report(wm8996->jack,
+ SND_JACK_HEADSET | SND_JACK_BTN_0,
+ SND_JACK_HEADSET | SND_JACK_BTN_0);
+ } else {
+ dev_dbg(codec->dev, "Headphone detected\n");
+ snd_soc_jack_report(wm8996->jack,
+ SND_JACK_HEADPHONE,
+ SND_JACK_HEADSET |
+ SND_JACK_BTN_0);
+
+ /* Increase the detection rate a bit for
+ * responsiveness.
+ */
+ snd_soc_update_bits(codec, WM8996_MIC_DETECT_1,
+ WM8996_MICD_RATE_MASK,
+ 7 << WM8996_MICD_RATE_SHIFT);
+
+ wm8996->detecting = false;
+ }
+ }
+}
+
+static irqreturn_t wm8996_irq(int irq, void *data)
+{
+ struct snd_soc_codec *codec = data;
+ struct wm8996_priv *wm8996 = snd_soc_codec_get_drvdata(codec);
+ int irq_val;
+
+ irq_val = snd_soc_read(codec, WM8996_INTERRUPT_STATUS_2);
+ if (irq_val < 0) {
+ dev_err(codec->dev, "Failed to read IRQ status: %d\n",
+ irq_val);
+ return IRQ_NONE;
+ }
+ irq_val &= ~snd_soc_read(codec, WM8996_INTERRUPT_STATUS_2_MASK);
+
+ snd_soc_write(codec, WM8996_INTERRUPT_STATUS_2, irq_val);
+
+ if (irq_val & (WM8996_DCS_DONE_01_EINT | WM8996_DCS_DONE_23_EINT)) {
+ dev_dbg(codec->dev, "DC servo IRQ\n");
+ complete(&wm8996->dcs_done);
+ }
+
+ if (irq_val & WM8996_FIFOS_ERR_EINT)
+ dev_err(codec->dev, "Digital core FIFO error\n");
+
+ if (irq_val & WM8996_FLL_LOCK_EINT) {
+ dev_dbg(codec->dev, "FLL locked\n");
+ complete(&wm8996->fll_lock);
+ }
+
+ if (irq_val & WM8996_MICD_EINT)
+ wm8996_micd(codec);
+
+ if (irq_val)
+ return IRQ_HANDLED;
+ else
+ return IRQ_NONE;
+}
+
+static irqreturn_t wm8996_edge_irq(int irq, void *data)
+{
+ irqreturn_t ret = IRQ_NONE;
+ irqreturn_t val;
+
+ do {
+ val = wm8996_irq(irq, data);
+ if (val != IRQ_NONE)
+ ret = val;
+ } while (val != IRQ_NONE);
+
+ return ret;
+}
+
+static void wm8996_retune_mobile_pdata(struct snd_soc_codec *codec)
+{
+ struct wm8996_priv *wm8996 = snd_soc_codec_get_drvdata(codec);
+ struct wm8996_pdata *pdata = &wm8996->pdata;
+
+ struct snd_kcontrol_new controls[] = {
+ SOC_ENUM_EXT("DSP1 EQ Mode",
+ wm8996->retune_mobile_enum,
+ wm8996_get_retune_mobile_enum,
+ wm8996_put_retune_mobile_enum),
+ SOC_ENUM_EXT("DSP2 EQ Mode",
+ wm8996->retune_mobile_enum,
+ wm8996_get_retune_mobile_enum,
+ wm8996_put_retune_mobile_enum),
+ };
+ int ret, i, j;
+ const char **t;
+
+ /* We need an array of texts for the enum API but the number
+ * of texts is likely to be less than the number of
+ * configurations due to the sample rate dependency of the
+ * configurations. */
+ wm8996->num_retune_mobile_texts = 0;
+ wm8996->retune_mobile_texts = NULL;
+ for (i = 0; i < pdata->num_retune_mobile_cfgs; i++) {
+ for (j = 0; j < wm8996->num_retune_mobile_texts; j++) {
+ if (strcmp(pdata->retune_mobile_cfgs[i].name,
+ wm8996->retune_mobile_texts[j]) == 0)
+ break;
+ }
+
+ if (j != wm8996->num_retune_mobile_texts)
+ continue;
+
+ /* Expand the array... */
+ t = krealloc(wm8996->retune_mobile_texts,
+ sizeof(char *) *
+ (wm8996->num_retune_mobile_texts + 1),
+ GFP_KERNEL);
+ if (t == NULL)
+ continue;
+
+ /* ...store the new entry... */
+ t[wm8996->num_retune_mobile_texts] =
+ pdata->retune_mobile_cfgs[i].name;
+
+ /* ...and remember the new version. */
+ wm8996->num_retune_mobile_texts++;
+ wm8996->retune_mobile_texts = t;
+ }
+
+ dev_dbg(codec->dev, "Allocated %d unique ReTune Mobile names\n",
+ wm8996->num_retune_mobile_texts);
+
+ wm8996->retune_mobile_enum.max = wm8996->num_retune_mobile_texts;
+ wm8996->retune_mobile_enum.texts = wm8996->retune_mobile_texts;
+
+ ret = snd_soc_add_controls(codec, controls, ARRAY_SIZE(controls));
+ if (ret != 0)
+ dev_err(codec->dev,
+ "Failed to add ReTune Mobile controls: %d\n", ret);
+}
+
+static int wm8996_probe(struct snd_soc_codec *codec)
+{
+ int ret;
+ struct wm8996_priv *wm8996 = snd_soc_codec_get_drvdata(codec);
+ struct i2c_client *i2c = to_i2c_client(codec->dev);
+ struct snd_soc_dapm_context *dapm = &codec->dapm;
+ int i, irq_flags;
+
+ wm8996->codec = codec;
+
+ init_completion(&wm8996->dcs_done);
+ init_completion(&wm8996->fll_lock);
+
+ dapm->idle_bias_off = true;
+ dapm->bias_level = SND_SOC_BIAS_OFF;
+
+ ret = snd_soc_codec_set_cache_io(codec, 16, 16, SND_SOC_I2C);
+ if (ret != 0) {
+ dev_err(codec->dev, "Failed to set cache I/O: %d\n", ret);
+ goto err;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(wm8996->supplies); i++)
+ wm8996->supplies[i].supply = wm8996_supply_names[i];
+
+ ret = regulator_bulk_get(codec->dev, ARRAY_SIZE(wm8996->supplies),
+ wm8996->supplies);
+ if (ret != 0) {
+ dev_err(codec->dev, "Failed to request supplies: %d\n", ret);
+ goto err;
+ }
+
+ wm8996->disable_nb[0].notifier_call = wm8996_regulator_event_0;
+ wm8996->disable_nb[1].notifier_call = wm8996_regulator_event_1;
+ wm8996->disable_nb[2].notifier_call = wm8996_regulator_event_2;
+ wm8996->disable_nb[3].notifier_call = wm8996_regulator_event_3;
+
+ /* This should really be moved into the regulator core */
+ for (i = 0; i < ARRAY_SIZE(wm8996->supplies); i++) {
+ ret = regulator_register_notifier(wm8996->supplies[i].consumer,
+ &wm8996->disable_nb[i]);
+ if (ret != 0) {
+ dev_err(codec->dev,
+ "Failed to register regulator notifier: %d\n",
+ ret);
+ }
+ }
+
+ ret = regulator_bulk_enable(ARRAY_SIZE(wm8996->supplies),
+ wm8996->supplies);
+ if (ret != 0) {
+ dev_err(codec->dev, "Failed to enable supplies: %d\n", ret);
+ goto err_get;
+ }
+
+ if (wm8996->pdata.ldo_ena >= 0) {
+ gpio_set_value_cansleep(wm8996->pdata.ldo_ena, 1);
+ msleep(5);
+ }
+
+ ret = snd_soc_read(codec, WM8996_SOFTWARE_RESET);
+ if (ret < 0) {
+ dev_err(codec->dev, "Failed to read ID register: %d\n", ret);
+ goto err_enable;
+ }
+ if (ret != 0x8915) {
+ dev_err(codec->dev, "Device is not a WM8996, ID %x\n", ret);
+ ret = -EINVAL;
+ goto err_enable;
+ }
+
+ ret = snd_soc_read(codec, WM8996_CHIP_REVISION);
+ if (ret < 0) {
+ dev_err(codec->dev, "Failed to read device revision: %d\n",
+ ret);
+ goto err_enable;
+ }
+
+ dev_info(codec->dev, "revision %c\n",
+ (ret & WM8996_CHIP_REV_MASK) + 'A');
+
+ if (wm8996->pdata.ldo_ena >= 0) {
+ gpio_set_value_cansleep(wm8996->pdata.ldo_ena, 0);
+ } else {
+ ret = wm8996_reset(codec);
+ if (ret < 0) {
+ dev_err(codec->dev, "Failed to issue reset\n");
+ goto err_enable;
+ }
+ }
+
+ codec->cache_only = true;
+
+ /* Apply platform data settings */
+ snd_soc_update_bits(codec, WM8996_LINE_INPUT_CONTROL,
+ WM8996_INL_MODE_MASK | WM8996_INR_MODE_MASK,
+ wm8996->pdata.inl_mode << WM8996_INL_MODE_SHIFT |
+ wm8996->pdata.inr_mode);
+
+ for (i = 0; i < ARRAY_SIZE(wm8996->pdata.gpio_default); i++) {
+ if (!wm8996->pdata.gpio_default[i])
+ continue;
+
+ snd_soc_write(codec, WM8996_GPIO_1 + i,
+ wm8996->pdata.gpio_default[i] & 0xffff);
+ }
+
+ if (wm8996->pdata.spkmute_seq)
+ snd_soc_update_bits(codec, WM8996_PDM_SPEAKER_MUTE_SEQUENCE,
+ WM8996_SPK_MUTE_ENDIAN |
+ WM8996_SPK_MUTE_SEQ1_MASK,
+ wm8996->pdata.spkmute_seq);
+
+ snd_soc_update_bits(codec, WM8996_ACCESSORY_DETECT_MODE_2,
+ WM8996_MICD_BIAS_SRC | WM8996_HPOUT1FB_SRC |
+ WM8996_MICD_SRC, wm8996->pdata.micdet_def);
+
+ /* Latch volume update bits */
+ snd_soc_update_bits(codec, WM8996_LEFT_LINE_INPUT_VOLUME,
+ WM8996_IN1_VU, WM8996_IN1_VU);
+ snd_soc_update_bits(codec, WM8996_RIGHT_LINE_INPUT_VOLUME,
+ WM8996_IN1_VU, WM8996_IN1_VU);
+
+ snd_soc_update_bits(codec, WM8996_DAC1_LEFT_VOLUME,
+ WM8996_DAC1_VU, WM8996_DAC1_VU);
+ snd_soc_update_bits(codec, WM8996_DAC1_RIGHT_VOLUME,
+ WM8996_DAC1_VU, WM8996_DAC1_VU);
+ snd_soc_update_bits(codec, WM8996_DAC2_LEFT_VOLUME,
+ WM8996_DAC2_VU, WM8996_DAC2_VU);
+ snd_soc_update_bits(codec, WM8996_DAC2_RIGHT_VOLUME,
+ WM8996_DAC2_VU, WM8996_DAC2_VU);
+
+ snd_soc_update_bits(codec, WM8996_OUTPUT1_LEFT_VOLUME,
+ WM8996_DAC1_VU, WM8996_DAC1_VU);
+ snd_soc_update_bits(codec, WM8996_OUTPUT1_RIGHT_VOLUME,
+ WM8996_DAC1_VU, WM8996_DAC1_VU);
+ snd_soc_update_bits(codec, WM8996_OUTPUT2_LEFT_VOLUME,
+ WM8996_DAC2_VU, WM8996_DAC2_VU);
+ snd_soc_update_bits(codec, WM8996_OUTPUT2_RIGHT_VOLUME,
+ WM8996_DAC2_VU, WM8996_DAC2_VU);
+
+ snd_soc_update_bits(codec, WM8996_DSP1_TX_LEFT_VOLUME,
+ WM8996_DSP1TX_VU, WM8996_DSP1TX_VU);
+ snd_soc_update_bits(codec, WM8996_DSP1_TX_RIGHT_VOLUME,
+ WM8996_DSP1TX_VU, WM8996_DSP1TX_VU);
+ snd_soc_update_bits(codec, WM8996_DSP2_TX_LEFT_VOLUME,
+ WM8996_DSP2TX_VU, WM8996_DSP2TX_VU);
+ snd_soc_update_bits(codec, WM8996_DSP2_TX_RIGHT_VOLUME,
+ WM8996_DSP2TX_VU, WM8996_DSP2TX_VU);
+
+ snd_soc_update_bits(codec, WM8996_DSP1_RX_LEFT_VOLUME,
+ WM8996_DSP1RX_VU, WM8996_DSP1RX_VU);
+ snd_soc_update_bits(codec, WM8996_DSP1_RX_RIGHT_VOLUME,
+ WM8996_DSP1RX_VU, WM8996_DSP1RX_VU);
+ snd_soc_update_bits(codec, WM8996_DSP2_RX_LEFT_VOLUME,
+ WM8996_DSP2RX_VU, WM8996_DSP2RX_VU);
+ snd_soc_update_bits(codec, WM8996_DSP2_RX_RIGHT_VOLUME,
+ WM8996_DSP2RX_VU, WM8996_DSP2RX_VU);
+
+ /* No support currently for the underclocked TDM modes and
+ * pick a default TDM layout with each channel pair working with
+ * slots 0 and 1. */
+ snd_soc_update_bits(codec, WM8996_AIF1RX_CHANNEL_0_CONFIGURATION,
+ WM8996_AIF1RX_CHAN0_SLOTS_MASK |
+ WM8996_AIF1RX_CHAN0_START_SLOT_MASK,
+ 1 << WM8996_AIF1RX_CHAN0_SLOTS_SHIFT | 0);
+ snd_soc_update_bits(codec, WM8996_AIF1RX_CHANNEL_1_CONFIGURATION,
+ WM8996_AIF1RX_CHAN1_SLOTS_MASK |
+ WM8996_AIF1RX_CHAN1_START_SLOT_MASK,
+ 1 << WM8996_AIF1RX_CHAN1_SLOTS_SHIFT | 1);
+ snd_soc_update_bits(codec, WM8996_AIF1RX_CHANNEL_2_CONFIGURATION,
+ WM8996_AIF1RX_CHAN2_SLOTS_MASK |
+ WM8996_AIF1RX_CHAN2_START_SLOT_MASK,
+ 1 << WM8996_AIF1RX_CHAN2_SLOTS_SHIFT | 0);
+ snd_soc_update_bits(codec, WM8996_AIF1RX_CHANNEL_3_CONFIGURATION,
+ WM8996_AIF1RX_CHAN3_SLOTS_MASK |
+ WM8996_AIF1RX_CHAN0_START_SLOT_MASK,
+ 1 << WM8996_AIF1RX_CHAN3_SLOTS_SHIFT | 1);
+ snd_soc_update_bits(codec, WM8996_AIF1RX_CHANNEL_4_CONFIGURATION,
+ WM8996_AIF1RX_CHAN4_SLOTS_MASK |
+ WM8996_AIF1RX_CHAN0_START_SLOT_MASK,
+ 1 << WM8996_AIF1RX_CHAN4_SLOTS_SHIFT | 0);
+ snd_soc_update_bits(codec, WM8996_AIF1RX_CHANNEL_5_CONFIGURATION,
+ WM8996_AIF1RX_CHAN5_SLOTS_MASK |
+ WM8996_AIF1RX_CHAN0_START_SLOT_MASK,
+ 1 << WM8996_AIF1RX_CHAN5_SLOTS_SHIFT | 1);
+
+ snd_soc_update_bits(codec, WM8996_AIF2RX_CHANNEL_0_CONFIGURATION,
+ WM8996_AIF2RX_CHAN0_SLOTS_MASK |
+ WM8996_AIF2RX_CHAN0_START_SLOT_MASK,
+ 1 << WM8996_AIF2RX_CHAN0_SLOTS_SHIFT | 0);
+ snd_soc_update_bits(codec, WM8996_AIF2RX_CHANNEL_1_CONFIGURATION,
+ WM8996_AIF2RX_CHAN1_SLOTS_MASK |
+ WM8996_AIF2RX_CHAN1_START_SLOT_MASK,
+ 1 << WM8996_AIF2RX_CHAN1_SLOTS_SHIFT | 1);
+
+ snd_soc_update_bits(codec, WM8996_AIF1TX_CHANNEL_0_CONFIGURATION,
+ WM8996_AIF1TX_CHAN0_SLOTS_MASK |
+ WM8996_AIF1TX_CHAN0_START_SLOT_MASK,
+ 1 << WM8996_AIF1TX_CHAN0_SLOTS_SHIFT | 0);
+ snd_soc_update_bits(codec, WM8996_AIF1TX_CHANNEL_1_CONFIGURATION,
+ WM8996_AIF1TX_CHAN1_SLOTS_MASK |
+ WM8996_AIF1TX_CHAN0_START_SLOT_MASK,
+ 1 << WM8996_AIF1TX_CHAN1_SLOTS_SHIFT | 1);
+ snd_soc_update_bits(codec, WM8996_AIF1TX_CHANNEL_2_CONFIGURATION,
+ WM8996_AIF1TX_CHAN2_SLOTS_MASK |
+ WM8996_AIF1TX_CHAN0_START_SLOT_MASK,
+ 1 << WM8996_AIF1TX_CHAN2_SLOTS_SHIFT | 0);
+ snd_soc_update_bits(codec, WM8996_AIF1TX_CHANNEL_3_CONFIGURATION,
+ WM8996_AIF1TX_CHAN3_SLOTS_MASK |
+ WM8996_AIF1TX_CHAN0_START_SLOT_MASK,
+ 1 << WM8996_AIF1TX_CHAN3_SLOTS_SHIFT | 1);
+ snd_soc_update_bits(codec, WM8996_AIF1TX_CHANNEL_4_CONFIGURATION,
+ WM8996_AIF1TX_CHAN4_SLOTS_MASK |
+ WM8996_AIF1TX_CHAN0_START_SLOT_MASK,
+ 1 << WM8996_AIF1TX_CHAN4_SLOTS_SHIFT | 0);
+ snd_soc_update_bits(codec, WM8996_AIF1TX_CHANNEL_5_CONFIGURATION,
+ WM8996_AIF1TX_CHAN5_SLOTS_MASK |
+ WM8996_AIF1TX_CHAN0_START_SLOT_MASK,
+ 1 << WM8996_AIF1TX_CHAN5_SLOTS_SHIFT | 1);
+
+ snd_soc_update_bits(codec, WM8996_AIF2TX_CHANNEL_0_CONFIGURATION,
+ WM8996_AIF2TX_CHAN0_SLOTS_MASK |
+ WM8996_AIF2TX_CHAN0_START_SLOT_MASK,
+ 1 << WM8996_AIF2TX_CHAN0_SLOTS_SHIFT | 0);
+ snd_soc_update_bits(codec, WM8996_AIF1TX_CHANNEL_1_CONFIGURATION,
+ WM8996_AIF2TX_CHAN1_SLOTS_MASK |
+ WM8996_AIF2TX_CHAN1_START_SLOT_MASK,
+ 1 << WM8996_AIF1TX_CHAN1_SLOTS_SHIFT | 1);
+
+ if (wm8996->pdata.num_retune_mobile_cfgs)
+ wm8996_retune_mobile_pdata(codec);
+ else
+ snd_soc_add_controls(codec, wm8996_eq_controls,
+ ARRAY_SIZE(wm8996_eq_controls));
+
+ /* If the TX LRCLK pins are not in LRCLK mode configure the
+ * AIFs to source their clocks from the RX LRCLKs.
+ */
+ if ((snd_soc_read(codec, WM8996_GPIO_1)))
+ snd_soc_update_bits(codec, WM8996_AIF1_TX_LRCLK_2,
+ WM8996_AIF1TX_LRCLK_MODE,
+ WM8996_AIF1TX_LRCLK_MODE);
+
+ if ((snd_soc_read(codec, WM8996_GPIO_2)))
+ snd_soc_update_bits(codec, WM8996_AIF2_TX_LRCLK_2,
+ WM8996_AIF2TX_LRCLK_MODE,
+ WM8996_AIF2TX_LRCLK_MODE);
+
+ regulator_bulk_disable(ARRAY_SIZE(wm8996->supplies), wm8996->supplies);
+
+ wm8996_init_gpio(codec);
+
+ if (i2c->irq) {
+ if (wm8996->pdata.irq_flags)
+ irq_flags = wm8996->pdata.irq_flags;
+ else
+ irq_flags = IRQF_TRIGGER_LOW;
+
+ irq_flags |= IRQF_ONESHOT;
+
+ if (irq_flags & (IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING))
+ ret = request_threaded_irq(i2c->irq, NULL,
+ wm8996_edge_irq,
+ irq_flags, "wm8996", codec);
+ else
+ ret = request_threaded_irq(i2c->irq, NULL, wm8996_irq,
+ irq_flags, "wm8996", codec);
+
+ if (ret == 0) {
+ /* Unmask the interrupt */
+ snd_soc_update_bits(codec, WM8996_INTERRUPT_CONTROL,
+ WM8996_IM_IRQ, 0);
+
+ /* Enable error reporting and DC servo status */
+ snd_soc_update_bits(codec,
+ WM8996_INTERRUPT_STATUS_2_MASK,
+ WM8996_IM_DCS_DONE_23_EINT |
+ WM8996_IM_DCS_DONE_01_EINT |
+ WM8996_IM_FLL_LOCK_EINT |
+ WM8996_IM_FIFOS_ERR_EINT,
+ 0);
+ } else {
+ dev_err(codec->dev, "Failed to request IRQ: %d\n",
+ ret);
+ }
+ }
+
+ return 0;
+
+err_enable:
+ if (wm8996->pdata.ldo_ena >= 0)
+ gpio_set_value_cansleep(wm8996->pdata.ldo_ena, 0);
+
+ regulator_bulk_disable(ARRAY_SIZE(wm8996->supplies), wm8996->supplies);
+err_get:
+ regulator_bulk_free(ARRAY_SIZE(wm8996->supplies), wm8996->supplies);
+err:
+ return ret;
+}
+
+static int wm8996_remove(struct snd_soc_codec *codec)
+{
+ struct wm8996_priv *wm8996 = snd_soc_codec_get_drvdata(codec);
+ struct i2c_client *i2c = to_i2c_client(codec->dev);
+ int i;
+
+ snd_soc_update_bits(codec, WM8996_INTERRUPT_CONTROL,
+ WM8996_IM_IRQ, WM8996_IM_IRQ);
+
+ if (i2c->irq)
+ free_irq(i2c->irq, codec);
+
+ wm8996_free_gpio(codec);
+
+ for (i = 0; i < ARRAY_SIZE(wm8996->supplies); i++)
+ regulator_unregister_notifier(wm8996->supplies[i].consumer,
+ &wm8996->disable_nb[i]);
+ regulator_bulk_free(ARRAY_SIZE(wm8996->supplies), wm8996->supplies);
+
+ return 0;
+}
+
+static struct snd_soc_codec_driver soc_codec_dev_wm8996 = {
+ .probe = wm8996_probe,
+ .remove = wm8996_remove,
+ .set_bias_level = wm8996_set_bias_level,
+ .seq_notifier = wm8996_seq_notifier,
+ .reg_cache_size = WM8996_MAX_REGISTER + 1,
+ .reg_word_size = sizeof(u16),
+ .reg_cache_default = wm8996_reg,
+ .volatile_register = wm8996_volatile_register,
+ .readable_register = wm8996_readable_register,
+ .compress_type = SND_SOC_RBTREE_COMPRESSION,
+ .controls = wm8996_snd_controls,
+ .num_controls = ARRAY_SIZE(wm8996_snd_controls),
+ .dapm_widgets = wm8996_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(wm8996_dapm_widgets),
+ .dapm_routes = wm8996_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(wm8996_dapm_routes),
+ .set_pll = wm8996_set_fll,
+};
+
+#define WM8996_RATES (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |\
+ SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000)
+#define WM8996_FORMATS (SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_S16_LE |\
+ SNDRV_PCM_FMTBIT_S20_3LE | SNDRV_PCM_FMTBIT_S24_LE |\
+ SNDRV_PCM_FMTBIT_S32_LE)
+
+static struct snd_soc_dai_ops wm8996_dai_ops = {
+ .set_fmt = wm8996_set_fmt,
+ .hw_params = wm8996_hw_params,
+ .set_sysclk = wm8996_set_sysclk,
+};
+
+static struct snd_soc_dai_driver wm8996_dai[] = {
+ {
+ .name = "wm8996-aif1",
+ .playback = {
+ .stream_name = "AIF1 Playback",
+ .channels_min = 1,
+ .channels_max = 6,
+ .rates = WM8996_RATES,
+ .formats = WM8996_FORMATS,
+ },
+ .capture = {
+ .stream_name = "AIF1 Capture",
+ .channels_min = 1,
+ .channels_max = 6,
+ .rates = WM8996_RATES,
+ .formats = WM8996_FORMATS,
+ },
+ .ops = &wm8996_dai_ops,
+ },
+ {
+ .name = "wm8996-aif2",
+ .playback = {
+ .stream_name = "AIF2 Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = WM8996_RATES,
+ .formats = WM8996_FORMATS,
+ },
+ .capture = {
+ .stream_name = "AIF2 Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = WM8996_RATES,
+ .formats = WM8996_FORMATS,
+ },
+ .ops = &wm8996_dai_ops,
+ },
+};
+
+static __devinit int wm8996_i2c_probe(struct i2c_client *i2c,
+ const struct i2c_device_id *id)
+{
+ struct wm8996_priv *wm8996;
+ int ret;
+
+ wm8996 = kzalloc(sizeof(struct wm8996_priv), GFP_KERNEL);
+ if (wm8996 == NULL)
+ return -ENOMEM;
+
+ i2c_set_clientdata(i2c, wm8996);
+
+ if (dev_get_platdata(&i2c->dev))
+ memcpy(&wm8996->pdata, dev_get_platdata(&i2c->dev),
+ sizeof(wm8996->pdata));
+
+ if (wm8996->pdata.ldo_ena > 0) {
+ ret = gpio_request_one(wm8996->pdata.ldo_ena,
+ GPIOF_OUT_INIT_LOW, "WM8996 ENA");
+ if (ret < 0) {
+ dev_err(&i2c->dev, "Failed to request GPIO %d: %d\n",
+ wm8996->pdata.ldo_ena, ret);
+ goto err;
+ }
+ }
+
+ ret = snd_soc_register_codec(&i2c->dev,
+ &soc_codec_dev_wm8996, wm8996_dai,
+ ARRAY_SIZE(wm8996_dai));
+ if (ret < 0)
+ goto err_gpio;
+
+ return ret;
+
+err_gpio:
+ if (wm8996->pdata.ldo_ena > 0)
+ gpio_free(wm8996->pdata.ldo_ena);
+err:
+ kfree(wm8996);
+
+ return ret;
+}
+
+static __devexit int wm8996_i2c_remove(struct i2c_client *client)
+{
+ struct wm8996_priv *wm8996 = i2c_get_clientdata(client);
+
+ snd_soc_unregister_codec(&client->dev);
+ if (wm8996->pdata.ldo_ena > 0)
+ gpio_free(wm8996->pdata.ldo_ena);
+ kfree(i2c_get_clientdata(client));
+ return 0;
+}
+
+static const struct i2c_device_id wm8996_i2c_id[] = {
+ { "wm8996", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, wm8996_i2c_id);
+
+static struct i2c_driver wm8996_i2c_driver = {
+ .driver = {
+ .name = "wm8996",
+ .owner = THIS_MODULE,
+ },
+ .probe = wm8996_i2c_probe,
+ .remove = __devexit_p(wm8996_i2c_remove),
+ .id_table = wm8996_i2c_id,
+};
+
+static int __init wm8996_modinit(void)
+{
+ int ret;
+
+ ret = i2c_add_driver(&wm8996_i2c_driver);
+ if (ret != 0) {
+ printk(KERN_ERR "Failed to register WM8996 I2C driver: %d\n",
+ ret);
+ }
+
+ return ret;
+}
+module_init(wm8996_modinit);
+
+static void __exit wm8996_exit(void)
+{
+ i2c_del_driver(&wm8996_i2c_driver);
+}
+module_exit(wm8996_exit);
+
+MODULE_DESCRIPTION("ASoC WM8996 driver");
+MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/wm8996.h b/sound/soc/codecs/wm8996.h
new file mode 100644
index 0000000..0fde643
--- /dev/null
+++ b/sound/soc/codecs/wm8996.h
@@ -0,0 +1,3717 @@
+/*
+ * wm8996.h - WM8996 audio codec interface
+ *
+ * Copyright 2011 Wolfson Microelectronics PLC.
+ * Author: Mark Brown <broonie@opensource.wolfsonmicro.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ */
+
+#ifndef _WM8996_H
+#define _WM8996_H
+
+#define WM8996_SYSCLK_MCLK1 1
+#define WM8996_SYSCLK_MCLK2 2
+#define WM8996_SYSCLK_FLL 3
+
+#define WM8996_FLL_MCLK1 1
+#define WM8996_FLL_MCLK2 2
+#define WM8996_FLL_DACLRCLK1 3
+#define WM8996_FLL_BCLK1 4
+
+typedef void (*wm8996_polarity_fn)(struct snd_soc_codec *codec, int polarity);
+
+int wm8996_detect(struct snd_soc_codec *codec, struct snd_soc_jack *jack,
+ wm8996_polarity_fn polarity_cb);
+
+/*
+ * Register values.
+ */
+#define WM8996_SOFTWARE_RESET 0x00
+#define WM8996_POWER_MANAGEMENT_1 0x01
+#define WM8996_POWER_MANAGEMENT_2 0x02
+#define WM8996_POWER_MANAGEMENT_3 0x03
+#define WM8996_POWER_MANAGEMENT_4 0x04
+#define WM8996_POWER_MANAGEMENT_5 0x05
+#define WM8996_POWER_MANAGEMENT_6 0x06
+#define WM8996_POWER_MANAGEMENT_7 0x07
+#define WM8996_POWER_MANAGEMENT_8 0x08
+#define WM8996_LEFT_LINE_INPUT_VOLUME 0x10
+#define WM8996_RIGHT_LINE_INPUT_VOLUME 0x11
+#define WM8996_LINE_INPUT_CONTROL 0x12
+#define WM8996_DAC1_HPOUT1_VOLUME 0x15
+#define WM8996_DAC2_HPOUT2_VOLUME 0x16
+#define WM8996_DAC1_LEFT_VOLUME 0x18
+#define WM8996_DAC1_RIGHT_VOLUME 0x19
+#define WM8996_DAC2_LEFT_VOLUME 0x1A
+#define WM8996_DAC2_RIGHT_VOLUME 0x1B
+#define WM8996_OUTPUT1_LEFT_VOLUME 0x1C
+#define WM8996_OUTPUT1_RIGHT_VOLUME 0x1D
+#define WM8996_OUTPUT2_LEFT_VOLUME 0x1E
+#define WM8996_OUTPUT2_RIGHT_VOLUME 0x1F
+#define WM8996_MICBIAS_1 0x20
+#define WM8996_MICBIAS_2 0x21
+#define WM8996_LDO_1 0x28
+#define WM8996_LDO_2 0x29
+#define WM8996_ACCESSORY_DETECT_MODE_1 0x30
+#define WM8996_ACCESSORY_DETECT_MODE_2 0x31
+#define WM8996_HEADPHONE_DETECT_1 0x34
+#define WM8996_HEADPHONE_DETECT_2 0x35
+#define WM8996_MIC_DETECT_1 0x38
+#define WM8996_MIC_DETECT_2 0x39
+#define WM8996_MIC_DETECT_3 0x3A
+#define WM8996_CHARGE_PUMP_1 0x40
+#define WM8996_CHARGE_PUMP_2 0x41
+#define WM8996_DC_SERVO_1 0x50
+#define WM8996_DC_SERVO_2 0x51
+#define WM8996_DC_SERVO_3 0x52
+#define WM8996_DC_SERVO_5 0x54
+#define WM8996_DC_SERVO_6 0x55
+#define WM8996_DC_SERVO_7 0x56
+#define WM8996_DC_SERVO_READBACK_0 0x57
+#define WM8996_ANALOGUE_HP_1 0x60
+#define WM8996_ANALOGUE_HP_2 0x61
+#define WM8996_CHIP_REVISION 0x100
+#define WM8996_CONTROL_INTERFACE_1 0x101
+#define WM8996_WRITE_SEQUENCER_CTRL_1 0x110
+#define WM8996_WRITE_SEQUENCER_CTRL_2 0x111
+#define WM8996_AIF_CLOCKING_1 0x200
+#define WM8996_AIF_CLOCKING_2 0x201
+#define WM8996_CLOCKING_1 0x208
+#define WM8996_CLOCKING_2 0x209
+#define WM8996_AIF_RATE 0x210
+#define WM8996_FLL_CONTROL_1 0x220
+#define WM8996_FLL_CONTROL_2 0x221
+#define WM8996_FLL_CONTROL_3 0x222
+#define WM8996_FLL_CONTROL_4 0x223
+#define WM8996_FLL_CONTROL_5 0x224
+#define WM8996_FLL_CONTROL_6 0x225
+#define WM8996_FLL_EFS_1 0x226
+#define WM8996_FLL_EFS_2 0x227
+#define WM8996_AIF1_CONTROL 0x300
+#define WM8996_AIF1_BCLK 0x301
+#define WM8996_AIF1_TX_LRCLK_1 0x302
+#define WM8996_AIF1_TX_LRCLK_2 0x303
+#define WM8996_AIF1_RX_LRCLK_1 0x304
+#define WM8996_AIF1_RX_LRCLK_2 0x305
+#define WM8996_AIF1TX_DATA_CONFIGURATION_1 0x306
+#define WM8996_AIF1TX_DATA_CONFIGURATION_2 0x307
+#define WM8996_AIF1RX_DATA_CONFIGURATION 0x308
+#define WM8996_AIF1TX_CHANNEL_0_CONFIGURATION 0x309
+#define WM8996_AIF1TX_CHANNEL_1_CONFIGURATION 0x30A
+#define WM8996_AIF1TX_CHANNEL_2_CONFIGURATION 0x30B
+#define WM8996_AIF1TX_CHANNEL_3_CONFIGURATION 0x30C
+#define WM8996_AIF1TX_CHANNEL_4_CONFIGURATION 0x30D
+#define WM8996_AIF1TX_CHANNEL_5_CONFIGURATION 0x30E
+#define WM8996_AIF1RX_CHANNEL_0_CONFIGURATION 0x30F
+#define WM8996_AIF1RX_CHANNEL_1_CONFIGURATION 0x310
+#define WM8996_AIF1RX_CHANNEL_2_CONFIGURATION 0x311
+#define WM8996_AIF1RX_CHANNEL_3_CONFIGURATION 0x312
+#define WM8996_AIF1RX_CHANNEL_4_CONFIGURATION 0x313
+#define WM8996_AIF1RX_CHANNEL_5_CONFIGURATION 0x314
+#define WM8996_AIF1RX_MONO_CONFIGURATION 0x315
+#define WM8996_AIF1TX_TEST 0x31A
+#define WM8996_AIF2_CONTROL 0x320
+#define WM8996_AIF2_BCLK 0x321
+#define WM8996_AIF2_TX_LRCLK_1 0x322
+#define WM8996_AIF2_TX_LRCLK_2 0x323
+#define WM8996_AIF2_RX_LRCLK_1 0x324
+#define WM8996_AIF2_RX_LRCLK_2 0x325
+#define WM8996_AIF2TX_DATA_CONFIGURATION_1 0x326
+#define WM8996_AIF2TX_DATA_CONFIGURATION_2 0x327
+#define WM8996_AIF2RX_DATA_CONFIGURATION 0x328
+#define WM8996_AIF2TX_CHANNEL_0_CONFIGURATION 0x329
+#define WM8996_AIF2TX_CHANNEL_1_CONFIGURATION 0x32A
+#define WM8996_AIF2RX_CHANNEL_0_CONFIGURATION 0x32B
+#define WM8996_AIF2RX_CHANNEL_1_CONFIGURATION 0x32C
+#define WM8996_AIF2RX_MONO_CONFIGURATION 0x32D
+#define WM8996_AIF2TX_TEST 0x32F
+#define WM8996_DSP1_TX_LEFT_VOLUME 0x400
+#define WM8996_DSP1_TX_RIGHT_VOLUME 0x401
+#define WM8996_DSP1_RX_LEFT_VOLUME 0x402
+#define WM8996_DSP1_RX_RIGHT_VOLUME 0x403
+#define WM8996_DSP1_TX_FILTERS 0x410
+#define WM8996_DSP1_RX_FILTERS_1 0x420
+#define WM8996_DSP1_RX_FILTERS_2 0x421
+#define WM8996_DSP1_DRC_1 0x440
+#define WM8996_DSP1_DRC_2 0x441
+#define WM8996_DSP1_DRC_3 0x442
+#define WM8996_DSP1_DRC_4 0x443
+#define WM8996_DSP1_DRC_5 0x444
+#define WM8996_DSP1_RX_EQ_GAINS_1 0x480
+#define WM8996_DSP1_RX_EQ_GAINS_2 0x481
+#define WM8996_DSP1_RX_EQ_BAND_1_A 0x482
+#define WM8996_DSP1_RX_EQ_BAND_1_B 0x483
+#define WM8996_DSP1_RX_EQ_BAND_1_PG 0x484
+#define WM8996_DSP1_RX_EQ_BAND_2_A 0x485
+#define WM8996_DSP1_RX_EQ_BAND_2_B 0x486
+#define WM8996_DSP1_RX_EQ_BAND_2_C 0x487
+#define WM8996_DSP1_RX_EQ_BAND_2_PG 0x488
+#define WM8996_DSP1_RX_EQ_BAND_3_A 0x489
+#define WM8996_DSP1_RX_EQ_BAND_3_B 0x48A
+#define WM8996_DSP1_RX_EQ_BAND_3_C 0x48B
+#define WM8996_DSP1_RX_EQ_BAND_3_PG 0x48C
+#define WM8996_DSP1_RX_EQ_BAND_4_A 0x48D
+#define WM8996_DSP1_RX_EQ_BAND_4_B 0x48E
+#define WM8996_DSP1_RX_EQ_BAND_4_C 0x48F
+#define WM8996_DSP1_RX_EQ_BAND_4_PG 0x490
+#define WM8996_DSP1_RX_EQ_BAND_5_A 0x491
+#define WM8996_DSP1_RX_EQ_BAND_5_B 0x492
+#define WM8996_DSP1_RX_EQ_BAND_5_PG 0x493
+#define WM8996_DSP2_TX_LEFT_VOLUME 0x500
+#define WM8996_DSP2_TX_RIGHT_VOLUME 0x501
+#define WM8996_DSP2_RX_LEFT_VOLUME 0x502
+#define WM8996_DSP2_RX_RIGHT_VOLUME 0x503
+#define WM8996_DSP2_TX_FILTERS 0x510
+#define WM8996_DSP2_RX_FILTERS_1 0x520
+#define WM8996_DSP2_RX_FILTERS_2 0x521
+#define WM8996_DSP2_DRC_1 0x540
+#define WM8996_DSP2_DRC_2 0x541
+#define WM8996_DSP2_DRC_3 0x542
+#define WM8996_DSP2_DRC_4 0x543
+#define WM8996_DSP2_DRC_5 0x544
+#define WM8996_DSP2_RX_EQ_GAINS_1 0x580
+#define WM8996_DSP2_RX_EQ_GAINS_2 0x581
+#define WM8996_DSP2_RX_EQ_BAND_1_A 0x582
+#define WM8996_DSP2_RX_EQ_BAND_1_B 0x583
+#define WM8996_DSP2_RX_EQ_BAND_1_PG 0x584
+#define WM8996_DSP2_RX_EQ_BAND_2_A 0x585
+#define WM8996_DSP2_RX_EQ_BAND_2_B 0x586
+#define WM8996_DSP2_RX_EQ_BAND_2_C 0x587
+#define WM8996_DSP2_RX_EQ_BAND_2_PG 0x588
+#define WM8996_DSP2_RX_EQ_BAND_3_A 0x589
+#define WM8996_DSP2_RX_EQ_BAND_3_B 0x58A
+#define WM8996_DSP2_RX_EQ_BAND_3_C 0x58B
+#define WM8996_DSP2_RX_EQ_BAND_3_PG 0x58C
+#define WM8996_DSP2_RX_EQ_BAND_4_A 0x58D
+#define WM8996_DSP2_RX_EQ_BAND_4_B 0x58E
+#define WM8996_DSP2_RX_EQ_BAND_4_C 0x58F
+#define WM8996_DSP2_RX_EQ_BAND_4_PG 0x590
+#define WM8996_DSP2_RX_EQ_BAND_5_A 0x591
+#define WM8996_DSP2_RX_EQ_BAND_5_B 0x592
+#define WM8996_DSP2_RX_EQ_BAND_5_PG 0x593
+#define WM8996_DAC1_MIXER_VOLUMES 0x600
+#define WM8996_DAC1_LEFT_MIXER_ROUTING 0x601
+#define WM8996_DAC1_RIGHT_MIXER_ROUTING 0x602
+#define WM8996_DAC2_MIXER_VOLUMES 0x603
+#define WM8996_DAC2_LEFT_MIXER_ROUTING 0x604
+#define WM8996_DAC2_RIGHT_MIXER_ROUTING 0x605
+#define WM8996_DSP1_TX_LEFT_MIXER_ROUTING 0x606
+#define WM8996_DSP1_TX_RIGHT_MIXER_ROUTING 0x607
+#define WM8996_DSP2_TX_LEFT_MIXER_ROUTING 0x608
+#define WM8996_DSP2_TX_RIGHT_MIXER_ROUTING 0x609
+#define WM8996_DSP_TX_MIXER_SELECT 0x60A
+#define WM8996_DAC_SOFTMUTE 0x610
+#define WM8996_OVERSAMPLING 0x620
+#define WM8996_SIDETONE 0x621
+#define WM8996_GPIO_1 0x700
+#define WM8996_GPIO_2 0x701
+#define WM8996_GPIO_3 0x702
+#define WM8996_GPIO_4 0x703
+#define WM8996_GPIO_5 0x704
+#define WM8996_PULL_CONTROL_1 0x720
+#define WM8996_PULL_CONTROL_2 0x721
+#define WM8996_INTERRUPT_STATUS_1 0x730
+#define WM8996_INTERRUPT_STATUS_2 0x731
+#define WM8996_INTERRUPT_RAW_STATUS_2 0x732
+#define WM8996_INTERRUPT_STATUS_1_MASK 0x738
+#define WM8996_INTERRUPT_STATUS_2_MASK 0x739
+#define WM8996_INTERRUPT_CONTROL 0x740
+#define WM8996_LEFT_PDM_SPEAKER 0x800
+#define WM8996_RIGHT_PDM_SPEAKER 0x801
+#define WM8996_PDM_SPEAKER_MUTE_SEQUENCE 0x802
+#define WM8996_PDM_SPEAKER_VOLUME 0x803
+#define WM8996_WRITE_SEQUENCER_0 0x3000
+#define WM8996_WRITE_SEQUENCER_1 0x3001
+#define WM8996_WRITE_SEQUENCER_2 0x3002
+#define WM8996_WRITE_SEQUENCER_3 0x3003
+#define WM8996_WRITE_SEQUENCER_4 0x3004
+#define WM8996_WRITE_SEQUENCER_5 0x3005
+#define WM8996_WRITE_SEQUENCER_6 0x3006
+#define WM8996_WRITE_SEQUENCER_7 0x3007
+#define WM8996_WRITE_SEQUENCER_8 0x3008
+#define WM8996_WRITE_SEQUENCER_9 0x3009
+#define WM8996_WRITE_SEQUENCER_10 0x300A
+#define WM8996_WRITE_SEQUENCER_11 0x300B
+#define WM8996_WRITE_SEQUENCER_12 0x300C
+#define WM8996_WRITE_SEQUENCER_13 0x300D
+#define WM8996_WRITE_SEQUENCER_14 0x300E
+#define WM8996_WRITE_SEQUENCER_15 0x300F
+#define WM8996_WRITE_SEQUENCER_16 0x3010
+#define WM8996_WRITE_SEQUENCER_17 0x3011
+#define WM8996_WRITE_SEQUENCER_18 0x3012
+#define WM8996_WRITE_SEQUENCER_19 0x3013
+#define WM8996_WRITE_SEQUENCER_20 0x3014
+#define WM8996_WRITE_SEQUENCER_21 0x3015
+#define WM8996_WRITE_SEQUENCER_22 0x3016
+#define WM8996_WRITE_SEQUENCER_23 0x3017
+#define WM8996_WRITE_SEQUENCER_24 0x3018
+#define WM8996_WRITE_SEQUENCER_25 0x3019
+#define WM8996_WRITE_SEQUENCER_26 0x301A
+#define WM8996_WRITE_SEQUENCER_27 0x301B
+#define WM8996_WRITE_SEQUENCER_28 0x301C
+#define WM8996_WRITE_SEQUENCER_29 0x301D
+#define WM8996_WRITE_SEQUENCER_30 0x301E
+#define WM8996_WRITE_SEQUENCER_31 0x301F
+#define WM8996_WRITE_SEQUENCER_32 0x3020
+#define WM8996_WRITE_SEQUENCER_33 0x3021
+#define WM8996_WRITE_SEQUENCER_34 0x3022
+#define WM8996_WRITE_SEQUENCER_35 0x3023
+#define WM8996_WRITE_SEQUENCER_36 0x3024
+#define WM8996_WRITE_SEQUENCER_37 0x3025
+#define WM8996_WRITE_SEQUENCER_38 0x3026
+#define WM8996_WRITE_SEQUENCER_39 0x3027
+#define WM8996_WRITE_SEQUENCER_40 0x3028
+#define WM8996_WRITE_SEQUENCER_41 0x3029
+#define WM8996_WRITE_SEQUENCER_42 0x302A
+#define WM8996_WRITE_SEQUENCER_43 0x302B
+#define WM8996_WRITE_SEQUENCER_44 0x302C
+#define WM8996_WRITE_SEQUENCER_45 0x302D
+#define WM8996_WRITE_SEQUENCER_46 0x302E
+#define WM8996_WRITE_SEQUENCER_47 0x302F
+#define WM8996_WRITE_SEQUENCER_48 0x3030
+#define WM8996_WRITE_SEQUENCER_49 0x3031
+#define WM8996_WRITE_SEQUENCER_50 0x3032
+#define WM8996_WRITE_SEQUENCER_51 0x3033
+#define WM8996_WRITE_SEQUENCER_52 0x3034
+#define WM8996_WRITE_SEQUENCER_53 0x3035
+#define WM8996_WRITE_SEQUENCER_54 0x3036
+#define WM8996_WRITE_SEQUENCER_55 0x3037
+#define WM8996_WRITE_SEQUENCER_56 0x3038
+#define WM8996_WRITE_SEQUENCER_57 0x3039
+#define WM8996_WRITE_SEQUENCER_58 0x303A
+#define WM8996_WRITE_SEQUENCER_59 0x303B
+#define WM8996_WRITE_SEQUENCER_60 0x303C
+#define WM8996_WRITE_SEQUENCER_61 0x303D
+#define WM8996_WRITE_SEQUENCER_62 0x303E
+#define WM8996_WRITE_SEQUENCER_63 0x303F
+#define WM8996_WRITE_SEQUENCER_64 0x3040
+#define WM8996_WRITE_SEQUENCER_65 0x3041
+#define WM8996_WRITE_SEQUENCER_66 0x3042
+#define WM8996_WRITE_SEQUENCER_67 0x3043
+#define WM8996_WRITE_SEQUENCER_68 0x3044
+#define WM8996_WRITE_SEQUENCER_69 0x3045
+#define WM8996_WRITE_SEQUENCER_70 0x3046
+#define WM8996_WRITE_SEQUENCER_71 0x3047
+#define WM8996_WRITE_SEQUENCER_72 0x3048
+#define WM8996_WRITE_SEQUENCER_73 0x3049
+#define WM8996_WRITE_SEQUENCER_74 0x304A
+#define WM8996_WRITE_SEQUENCER_75 0x304B
+#define WM8996_WRITE_SEQUENCER_76 0x304C
+#define WM8996_WRITE_SEQUENCER_77 0x304D
+#define WM8996_WRITE_SEQUENCER_78 0x304E
+#define WM8996_WRITE_SEQUENCER_79 0x304F
+#define WM8996_WRITE_SEQUENCER_80 0x3050
+#define WM8996_WRITE_SEQUENCER_81 0x3051
+#define WM8996_WRITE_SEQUENCER_82 0x3052
+#define WM8996_WRITE_SEQUENCER_83 0x3053
+#define WM8996_WRITE_SEQUENCER_84 0x3054
+#define WM8996_WRITE_SEQUENCER_85 0x3055
+#define WM8996_WRITE_SEQUENCER_86 0x3056
+#define WM8996_WRITE_SEQUENCER_87 0x3057
+#define WM8996_WRITE_SEQUENCER_88 0x3058
+#define WM8996_WRITE_SEQUENCER_89 0x3059
+#define WM8996_WRITE_SEQUENCER_90 0x305A
+#define WM8996_WRITE_SEQUENCER_91 0x305B
+#define WM8996_WRITE_SEQUENCER_92 0x305C
+#define WM8996_WRITE_SEQUENCER_93 0x305D
+#define WM8996_WRITE_SEQUENCER_94 0x305E
+#define WM8996_WRITE_SEQUENCER_95 0x305F
+#define WM8996_WRITE_SEQUENCER_96 0x3060
+#define WM8996_WRITE_SEQUENCER_97 0x3061
+#define WM8996_WRITE_SEQUENCER_98 0x3062
+#define WM8996_WRITE_SEQUENCER_99 0x3063
+#define WM8996_WRITE_SEQUENCER_100 0x3064
+#define WM8996_WRITE_SEQUENCER_101 0x3065
+#define WM8996_WRITE_SEQUENCER_102 0x3066
+#define WM8996_WRITE_SEQUENCER_103 0x3067
+#define WM8996_WRITE_SEQUENCER_104 0x3068
+#define WM8996_WRITE_SEQUENCER_105 0x3069
+#define WM8996_WRITE_SEQUENCER_106 0x306A
+#define WM8996_WRITE_SEQUENCER_107 0x306B
+#define WM8996_WRITE_SEQUENCER_108 0x306C
+#define WM8996_WRITE_SEQUENCER_109 0x306D
+#define WM8996_WRITE_SEQUENCER_110 0x306E
+#define WM8996_WRITE_SEQUENCER_111 0x306F
+#define WM8996_WRITE_SEQUENCER_112 0x3070
+#define WM8996_WRITE_SEQUENCER_113 0x3071
+#define WM8996_WRITE_SEQUENCER_114 0x3072
+#define WM8996_WRITE_SEQUENCER_115 0x3073
+#define WM8996_WRITE_SEQUENCER_116 0x3074
+#define WM8996_WRITE_SEQUENCER_117 0x3075
+#define WM8996_WRITE_SEQUENCER_118 0x3076
+#define WM8996_WRITE_SEQUENCER_119 0x3077
+#define WM8996_WRITE_SEQUENCER_120 0x3078
+#define WM8996_WRITE_SEQUENCER_121 0x3079
+#define WM8996_WRITE_SEQUENCER_122 0x307A
+#define WM8996_WRITE_SEQUENCER_123 0x307B
+#define WM8996_WRITE_SEQUENCER_124 0x307C
+#define WM8996_WRITE_SEQUENCER_125 0x307D
+#define WM8996_WRITE_SEQUENCER_126 0x307E
+#define WM8996_WRITE_SEQUENCER_127 0x307F
+#define WM8996_WRITE_SEQUENCER_128 0x3080
+#define WM8996_WRITE_SEQUENCER_129 0x3081
+#define WM8996_WRITE_SEQUENCER_130 0x3082
+#define WM8996_WRITE_SEQUENCER_131 0x3083
+#define WM8996_WRITE_SEQUENCER_132 0x3084
+#define WM8996_WRITE_SEQUENCER_133 0x3085
+#define WM8996_WRITE_SEQUENCER_134 0x3086
+#define WM8996_WRITE_SEQUENCER_135 0x3087
+#define WM8996_WRITE_SEQUENCER_136 0x3088
+#define WM8996_WRITE_SEQUENCER_137 0x3089
+#define WM8996_WRITE_SEQUENCER_138 0x308A
+#define WM8996_WRITE_SEQUENCER_139 0x308B
+#define WM8996_WRITE_SEQUENCER_140 0x308C
+#define WM8996_WRITE_SEQUENCER_141 0x308D
+#define WM8996_WRITE_SEQUENCER_142 0x308E
+#define WM8996_WRITE_SEQUENCER_143 0x308F
+#define WM8996_WRITE_SEQUENCER_144 0x3090
+#define WM8996_WRITE_SEQUENCER_145 0x3091
+#define WM8996_WRITE_SEQUENCER_146 0x3092
+#define WM8996_WRITE_SEQUENCER_147 0x3093
+#define WM8996_WRITE_SEQUENCER_148 0x3094
+#define WM8996_WRITE_SEQUENCER_149 0x3095
+#define WM8996_WRITE_SEQUENCER_150 0x3096
+#define WM8996_WRITE_SEQUENCER_151 0x3097
+#define WM8996_WRITE_SEQUENCER_152 0x3098
+#define WM8996_WRITE_SEQUENCER_153 0x3099
+#define WM8996_WRITE_SEQUENCER_154 0x309A
+#define WM8996_WRITE_SEQUENCER_155 0x309B
+#define WM8996_WRITE_SEQUENCER_156 0x309C
+#define WM8996_WRITE_SEQUENCER_157 0x309D
+#define WM8996_WRITE_SEQUENCER_158 0x309E
+#define WM8996_WRITE_SEQUENCER_159 0x309F
+#define WM8996_WRITE_SEQUENCER_160 0x30A0
+#define WM8996_WRITE_SEQUENCER_161 0x30A1
+#define WM8996_WRITE_SEQUENCER_162 0x30A2
+#define WM8996_WRITE_SEQUENCER_163 0x30A3
+#define WM8996_WRITE_SEQUENCER_164 0x30A4
+#define WM8996_WRITE_SEQUENCER_165 0x30A5
+#define WM8996_WRITE_SEQUENCER_166 0x30A6
+#define WM8996_WRITE_SEQUENCER_167 0x30A7
+#define WM8996_WRITE_SEQUENCER_168 0x30A8
+#define WM8996_WRITE_SEQUENCER_169 0x30A9
+#define WM8996_WRITE_SEQUENCER_170 0x30AA
+#define WM8996_WRITE_SEQUENCER_171 0x30AB
+#define WM8996_WRITE_SEQUENCER_172 0x30AC
+#define WM8996_WRITE_SEQUENCER_173 0x30AD
+#define WM8996_WRITE_SEQUENCER_174 0x30AE
+#define WM8996_WRITE_SEQUENCER_175 0x30AF
+#define WM8996_WRITE_SEQUENCER_176 0x30B0
+#define WM8996_WRITE_SEQUENCER_177 0x30B1
+#define WM8996_WRITE_SEQUENCER_178 0x30B2
+#define WM8996_WRITE_SEQUENCER_179 0x30B3
+#define WM8996_WRITE_SEQUENCER_180 0x30B4
+#define WM8996_WRITE_SEQUENCER_181 0x30B5
+#define WM8996_WRITE_SEQUENCER_182 0x30B6
+#define WM8996_WRITE_SEQUENCER_183 0x30B7
+#define WM8996_WRITE_SEQUENCER_184 0x30B8
+#define WM8996_WRITE_SEQUENCER_185 0x30B9
+#define WM8996_WRITE_SEQUENCER_186 0x30BA
+#define WM8996_WRITE_SEQUENCER_187 0x30BB
+#define WM8996_WRITE_SEQUENCER_188 0x30BC
+#define WM8996_WRITE_SEQUENCER_189 0x30BD
+#define WM8996_WRITE_SEQUENCER_190 0x30BE
+#define WM8996_WRITE_SEQUENCER_191 0x30BF
+#define WM8996_WRITE_SEQUENCER_192 0x30C0
+#define WM8996_WRITE_SEQUENCER_193 0x30C1
+#define WM8996_WRITE_SEQUENCER_194 0x30C2
+#define WM8996_WRITE_SEQUENCER_195 0x30C3
+#define WM8996_WRITE_SEQUENCER_196 0x30C4
+#define WM8996_WRITE_SEQUENCER_197 0x30C5
+#define WM8996_WRITE_SEQUENCER_198 0x30C6
+#define WM8996_WRITE_SEQUENCER_199 0x30C7
+#define WM8996_WRITE_SEQUENCER_200 0x30C8
+#define WM8996_WRITE_SEQUENCER_201 0x30C9
+#define WM8996_WRITE_SEQUENCER_202 0x30CA
+#define WM8996_WRITE_SEQUENCER_203 0x30CB
+#define WM8996_WRITE_SEQUENCER_204 0x30CC
+#define WM8996_WRITE_SEQUENCER_205 0x30CD
+#define WM8996_WRITE_SEQUENCER_206 0x30CE
+#define WM8996_WRITE_SEQUENCER_207 0x30CF
+#define WM8996_WRITE_SEQUENCER_208 0x30D0
+#define WM8996_WRITE_SEQUENCER_209 0x30D1
+#define WM8996_WRITE_SEQUENCER_210 0x30D2
+#define WM8996_WRITE_SEQUENCER_211 0x30D3
+#define WM8996_WRITE_SEQUENCER_212 0x30D4
+#define WM8996_WRITE_SEQUENCER_213 0x30D5
+#define WM8996_WRITE_SEQUENCER_214 0x30D6
+#define WM8996_WRITE_SEQUENCER_215 0x30D7
+#define WM8996_WRITE_SEQUENCER_216 0x30D8
+#define WM8996_WRITE_SEQUENCER_217 0x30D9
+#define WM8996_WRITE_SEQUENCER_218 0x30DA
+#define WM8996_WRITE_SEQUENCER_219 0x30DB
+#define WM8996_WRITE_SEQUENCER_220 0x30DC
+#define WM8996_WRITE_SEQUENCER_221 0x30DD
+#define WM8996_WRITE_SEQUENCER_222 0x30DE
+#define WM8996_WRITE_SEQUENCER_223 0x30DF
+#define WM8996_WRITE_SEQUENCER_224 0x30E0
+#define WM8996_WRITE_SEQUENCER_225 0x30E1
+#define WM8996_WRITE_SEQUENCER_226 0x30E2
+#define WM8996_WRITE_SEQUENCER_227 0x30E3
+#define WM8996_WRITE_SEQUENCER_228 0x30E4
+#define WM8996_WRITE_SEQUENCER_229 0x30E5
+#define WM8996_WRITE_SEQUENCER_230 0x30E6
+#define WM8996_WRITE_SEQUENCER_231 0x30E7
+#define WM8996_WRITE_SEQUENCER_232 0x30E8
+#define WM8996_WRITE_SEQUENCER_233 0x30E9
+#define WM8996_WRITE_SEQUENCER_234 0x30EA
+#define WM8996_WRITE_SEQUENCER_235 0x30EB
+#define WM8996_WRITE_SEQUENCER_236 0x30EC
+#define WM8996_WRITE_SEQUENCER_237 0x30ED
+#define WM8996_WRITE_SEQUENCER_238 0x30EE
+#define WM8996_WRITE_SEQUENCER_239 0x30EF
+#define WM8996_WRITE_SEQUENCER_240 0x30F0
+#define WM8996_WRITE_SEQUENCER_241 0x30F1
+#define WM8996_WRITE_SEQUENCER_242 0x30F2
+#define WM8996_WRITE_SEQUENCER_243 0x30F3
+#define WM8996_WRITE_SEQUENCER_244 0x30F4
+#define WM8996_WRITE_SEQUENCER_245 0x30F5
+#define WM8996_WRITE_SEQUENCER_246 0x30F6
+#define WM8996_WRITE_SEQUENCER_247 0x30F7
+#define WM8996_WRITE_SEQUENCER_248 0x30F8
+#define WM8996_WRITE_SEQUENCER_249 0x30F9
+#define WM8996_WRITE_SEQUENCER_250 0x30FA
+#define WM8996_WRITE_SEQUENCER_251 0x30FB
+#define WM8996_WRITE_SEQUENCER_252 0x30FC
+#define WM8996_WRITE_SEQUENCER_253 0x30FD
+#define WM8996_WRITE_SEQUENCER_254 0x30FE
+#define WM8996_WRITE_SEQUENCER_255 0x30FF
+#define WM8996_WRITE_SEQUENCER_256 0x3100
+#define WM8996_WRITE_SEQUENCER_257 0x3101
+#define WM8996_WRITE_SEQUENCER_258 0x3102
+#define WM8996_WRITE_SEQUENCER_259 0x3103
+#define WM8996_WRITE_SEQUENCER_260 0x3104
+#define WM8996_WRITE_SEQUENCER_261 0x3105
+#define WM8996_WRITE_SEQUENCER_262 0x3106
+#define WM8996_WRITE_SEQUENCER_263 0x3107
+#define WM8996_WRITE_SEQUENCER_264 0x3108
+#define WM8996_WRITE_SEQUENCER_265 0x3109
+#define WM8996_WRITE_SEQUENCER_266 0x310A
+#define WM8996_WRITE_SEQUENCER_267 0x310B
+#define WM8996_WRITE_SEQUENCER_268 0x310C
+#define WM8996_WRITE_SEQUENCER_269 0x310D
+#define WM8996_WRITE_SEQUENCER_270 0x310E
+#define WM8996_WRITE_SEQUENCER_271 0x310F
+#define WM8996_WRITE_SEQUENCER_272 0x3110
+#define WM8996_WRITE_SEQUENCER_273 0x3111
+#define WM8996_WRITE_SEQUENCER_274 0x3112
+#define WM8996_WRITE_SEQUENCER_275 0x3113
+#define WM8996_WRITE_SEQUENCER_276 0x3114
+#define WM8996_WRITE_SEQUENCER_277 0x3115
+#define WM8996_WRITE_SEQUENCER_278 0x3116
+#define WM8996_WRITE_SEQUENCER_279 0x3117
+#define WM8996_WRITE_SEQUENCER_280 0x3118
+#define WM8996_WRITE_SEQUENCER_281 0x3119
+#define WM8996_WRITE_SEQUENCER_282 0x311A
+#define WM8996_WRITE_SEQUENCER_283 0x311B
+#define WM8996_WRITE_SEQUENCER_284 0x311C
+#define WM8996_WRITE_SEQUENCER_285 0x311D
+#define WM8996_WRITE_SEQUENCER_286 0x311E
+#define WM8996_WRITE_SEQUENCER_287 0x311F
+#define WM8996_WRITE_SEQUENCER_288 0x3120
+#define WM8996_WRITE_SEQUENCER_289 0x3121
+#define WM8996_WRITE_SEQUENCER_290 0x3122
+#define WM8996_WRITE_SEQUENCER_291 0x3123
+#define WM8996_WRITE_SEQUENCER_292 0x3124
+#define WM8996_WRITE_SEQUENCER_293 0x3125
+#define WM8996_WRITE_SEQUENCER_294 0x3126
+#define WM8996_WRITE_SEQUENCER_295 0x3127
+#define WM8996_WRITE_SEQUENCER_296 0x3128
+#define WM8996_WRITE_SEQUENCER_297 0x3129
+#define WM8996_WRITE_SEQUENCER_298 0x312A
+#define WM8996_WRITE_SEQUENCER_299 0x312B
+#define WM8996_WRITE_SEQUENCER_300 0x312C
+#define WM8996_WRITE_SEQUENCER_301 0x312D
+#define WM8996_WRITE_SEQUENCER_302 0x312E
+#define WM8996_WRITE_SEQUENCER_303 0x312F
+#define WM8996_WRITE_SEQUENCER_304 0x3130
+#define WM8996_WRITE_SEQUENCER_305 0x3131
+#define WM8996_WRITE_SEQUENCER_306 0x3132
+#define WM8996_WRITE_SEQUENCER_307 0x3133
+#define WM8996_WRITE_SEQUENCER_308 0x3134
+#define WM8996_WRITE_SEQUENCER_309 0x3135
+#define WM8996_WRITE_SEQUENCER_310 0x3136
+#define WM8996_WRITE_SEQUENCER_311 0x3137
+#define WM8996_WRITE_SEQUENCER_312 0x3138
+#define WM8996_WRITE_SEQUENCER_313 0x3139
+#define WM8996_WRITE_SEQUENCER_314 0x313A
+#define WM8996_WRITE_SEQUENCER_315 0x313B
+#define WM8996_WRITE_SEQUENCER_316 0x313C
+#define WM8996_WRITE_SEQUENCER_317 0x313D
+#define WM8996_WRITE_SEQUENCER_318 0x313E
+#define WM8996_WRITE_SEQUENCER_319 0x313F
+#define WM8996_WRITE_SEQUENCER_320 0x3140
+#define WM8996_WRITE_SEQUENCER_321 0x3141
+#define WM8996_WRITE_SEQUENCER_322 0x3142
+#define WM8996_WRITE_SEQUENCER_323 0x3143
+#define WM8996_WRITE_SEQUENCER_324 0x3144
+#define WM8996_WRITE_SEQUENCER_325 0x3145
+#define WM8996_WRITE_SEQUENCER_326 0x3146
+#define WM8996_WRITE_SEQUENCER_327 0x3147
+#define WM8996_WRITE_SEQUENCER_328 0x3148
+#define WM8996_WRITE_SEQUENCER_329 0x3149
+#define WM8996_WRITE_SEQUENCER_330 0x314A
+#define WM8996_WRITE_SEQUENCER_331 0x314B
+#define WM8996_WRITE_SEQUENCER_332 0x314C
+#define WM8996_WRITE_SEQUENCER_333 0x314D
+#define WM8996_WRITE_SEQUENCER_334 0x314E
+#define WM8996_WRITE_SEQUENCER_335 0x314F
+#define WM8996_WRITE_SEQUENCER_336 0x3150
+#define WM8996_WRITE_SEQUENCER_337 0x3151
+#define WM8996_WRITE_SEQUENCER_338 0x3152
+#define WM8996_WRITE_SEQUENCER_339 0x3153
+#define WM8996_WRITE_SEQUENCER_340 0x3154
+#define WM8996_WRITE_SEQUENCER_341 0x3155
+#define WM8996_WRITE_SEQUENCER_342 0x3156
+#define WM8996_WRITE_SEQUENCER_343 0x3157
+#define WM8996_WRITE_SEQUENCER_344 0x3158
+#define WM8996_WRITE_SEQUENCER_345 0x3159
+#define WM8996_WRITE_SEQUENCER_346 0x315A
+#define WM8996_WRITE_SEQUENCER_347 0x315B
+#define WM8996_WRITE_SEQUENCER_348 0x315C
+#define WM8996_WRITE_SEQUENCER_349 0x315D
+#define WM8996_WRITE_SEQUENCER_350 0x315E
+#define WM8996_WRITE_SEQUENCER_351 0x315F
+#define WM8996_WRITE_SEQUENCER_352 0x3160
+#define WM8996_WRITE_SEQUENCER_353 0x3161
+#define WM8996_WRITE_SEQUENCER_354 0x3162
+#define WM8996_WRITE_SEQUENCER_355 0x3163
+#define WM8996_WRITE_SEQUENCER_356 0x3164
+#define WM8996_WRITE_SEQUENCER_357 0x3165
+#define WM8996_WRITE_SEQUENCER_358 0x3166
+#define WM8996_WRITE_SEQUENCER_359 0x3167
+#define WM8996_WRITE_SEQUENCER_360 0x3168
+#define WM8996_WRITE_SEQUENCER_361 0x3169
+#define WM8996_WRITE_SEQUENCER_362 0x316A
+#define WM8996_WRITE_SEQUENCER_363 0x316B
+#define WM8996_WRITE_SEQUENCER_364 0x316C
+#define WM8996_WRITE_SEQUENCER_365 0x316D
+#define WM8996_WRITE_SEQUENCER_366 0x316E
+#define WM8996_WRITE_SEQUENCER_367 0x316F
+#define WM8996_WRITE_SEQUENCER_368 0x3170
+#define WM8996_WRITE_SEQUENCER_369 0x3171
+#define WM8996_WRITE_SEQUENCER_370 0x3172
+#define WM8996_WRITE_SEQUENCER_371 0x3173
+#define WM8996_WRITE_SEQUENCER_372 0x3174
+#define WM8996_WRITE_SEQUENCER_373 0x3175
+#define WM8996_WRITE_SEQUENCER_374 0x3176
+#define WM8996_WRITE_SEQUENCER_375 0x3177
+#define WM8996_WRITE_SEQUENCER_376 0x3178
+#define WM8996_WRITE_SEQUENCER_377 0x3179
+#define WM8996_WRITE_SEQUENCER_378 0x317A
+#define WM8996_WRITE_SEQUENCER_379 0x317B
+#define WM8996_WRITE_SEQUENCER_380 0x317C
+#define WM8996_WRITE_SEQUENCER_381 0x317D
+#define WM8996_WRITE_SEQUENCER_382 0x317E
+#define WM8996_WRITE_SEQUENCER_383 0x317F
+#define WM8996_WRITE_SEQUENCER_384 0x3180
+#define WM8996_WRITE_SEQUENCER_385 0x3181
+#define WM8996_WRITE_SEQUENCER_386 0x3182
+#define WM8996_WRITE_SEQUENCER_387 0x3183
+#define WM8996_WRITE_SEQUENCER_388 0x3184
+#define WM8996_WRITE_SEQUENCER_389 0x3185
+#define WM8996_WRITE_SEQUENCER_390 0x3186
+#define WM8996_WRITE_SEQUENCER_391 0x3187
+#define WM8996_WRITE_SEQUENCER_392 0x3188
+#define WM8996_WRITE_SEQUENCER_393 0x3189
+#define WM8996_WRITE_SEQUENCER_394 0x318A
+#define WM8996_WRITE_SEQUENCER_395 0x318B
+#define WM8996_WRITE_SEQUENCER_396 0x318C
+#define WM8996_WRITE_SEQUENCER_397 0x318D
+#define WM8996_WRITE_SEQUENCER_398 0x318E
+#define WM8996_WRITE_SEQUENCER_399 0x318F
+#define WM8996_WRITE_SEQUENCER_400 0x3190
+#define WM8996_WRITE_SEQUENCER_401 0x3191
+#define WM8996_WRITE_SEQUENCER_402 0x3192
+#define WM8996_WRITE_SEQUENCER_403 0x3193
+#define WM8996_WRITE_SEQUENCER_404 0x3194
+#define WM8996_WRITE_SEQUENCER_405 0x3195
+#define WM8996_WRITE_SEQUENCER_406 0x3196
+#define WM8996_WRITE_SEQUENCER_407 0x3197
+#define WM8996_WRITE_SEQUENCER_408 0x3198
+#define WM8996_WRITE_SEQUENCER_409 0x3199
+#define WM8996_WRITE_SEQUENCER_410 0x319A
+#define WM8996_WRITE_SEQUENCER_411 0x319B
+#define WM8996_WRITE_SEQUENCER_412 0x319C
+#define WM8996_WRITE_SEQUENCER_413 0x319D
+#define WM8996_WRITE_SEQUENCER_414 0x319E
+#define WM8996_WRITE_SEQUENCER_415 0x319F
+#define WM8996_WRITE_SEQUENCER_416 0x31A0
+#define WM8996_WRITE_SEQUENCER_417 0x31A1
+#define WM8996_WRITE_SEQUENCER_418 0x31A2
+#define WM8996_WRITE_SEQUENCER_419 0x31A3
+#define WM8996_WRITE_SEQUENCER_420 0x31A4
+#define WM8996_WRITE_SEQUENCER_421 0x31A5
+#define WM8996_WRITE_SEQUENCER_422 0x31A6
+#define WM8996_WRITE_SEQUENCER_423 0x31A7
+#define WM8996_WRITE_SEQUENCER_424 0x31A8
+#define WM8996_WRITE_SEQUENCER_425 0x31A9
+#define WM8996_WRITE_SEQUENCER_426 0x31AA
+#define WM8996_WRITE_SEQUENCER_427 0x31AB
+#define WM8996_WRITE_SEQUENCER_428 0x31AC
+#define WM8996_WRITE_SEQUENCER_429 0x31AD
+#define WM8996_WRITE_SEQUENCER_430 0x31AE
+#define WM8996_WRITE_SEQUENCER_431 0x31AF
+#define WM8996_WRITE_SEQUENCER_432 0x31B0
+#define WM8996_WRITE_SEQUENCER_433 0x31B1
+#define WM8996_WRITE_SEQUENCER_434 0x31B2
+#define WM8996_WRITE_SEQUENCER_435 0x31B3
+#define WM8996_WRITE_SEQUENCER_436 0x31B4
+#define WM8996_WRITE_SEQUENCER_437 0x31B5
+#define WM8996_WRITE_SEQUENCER_438 0x31B6
+#define WM8996_WRITE_SEQUENCER_439 0x31B7
+#define WM8996_WRITE_SEQUENCER_440 0x31B8
+#define WM8996_WRITE_SEQUENCER_441 0x31B9
+#define WM8996_WRITE_SEQUENCER_442 0x31BA
+#define WM8996_WRITE_SEQUENCER_443 0x31BB
+#define WM8996_WRITE_SEQUENCER_444 0x31BC
+#define WM8996_WRITE_SEQUENCER_445 0x31BD
+#define WM8996_WRITE_SEQUENCER_446 0x31BE
+#define WM8996_WRITE_SEQUENCER_447 0x31BF
+#define WM8996_WRITE_SEQUENCER_448 0x31C0
+#define WM8996_WRITE_SEQUENCER_449 0x31C1
+#define WM8996_WRITE_SEQUENCER_450 0x31C2
+#define WM8996_WRITE_SEQUENCER_451 0x31C3
+#define WM8996_WRITE_SEQUENCER_452 0x31C4
+#define WM8996_WRITE_SEQUENCER_453 0x31C5
+#define WM8996_WRITE_SEQUENCER_454 0x31C6
+#define WM8996_WRITE_SEQUENCER_455 0x31C7
+#define WM8996_WRITE_SEQUENCER_456 0x31C8
+#define WM8996_WRITE_SEQUENCER_457 0x31C9
+#define WM8996_WRITE_SEQUENCER_458 0x31CA
+#define WM8996_WRITE_SEQUENCER_459 0x31CB
+#define WM8996_WRITE_SEQUENCER_460 0x31CC
+#define WM8996_WRITE_SEQUENCER_461 0x31CD
+#define WM8996_WRITE_SEQUENCER_462 0x31CE
+#define WM8996_WRITE_SEQUENCER_463 0x31CF
+#define WM8996_WRITE_SEQUENCER_464 0x31D0
+#define WM8996_WRITE_SEQUENCER_465 0x31D1
+#define WM8996_WRITE_SEQUENCER_466 0x31D2
+#define WM8996_WRITE_SEQUENCER_467 0x31D3
+#define WM8996_WRITE_SEQUENCER_468 0x31D4
+#define WM8996_WRITE_SEQUENCER_469 0x31D5
+#define WM8996_WRITE_SEQUENCER_470 0x31D6
+#define WM8996_WRITE_SEQUENCER_471 0x31D7
+#define WM8996_WRITE_SEQUENCER_472 0x31D8
+#define WM8996_WRITE_SEQUENCER_473 0x31D9
+#define WM8996_WRITE_SEQUENCER_474 0x31DA
+#define WM8996_WRITE_SEQUENCER_475 0x31DB
+#define WM8996_WRITE_SEQUENCER_476 0x31DC
+#define WM8996_WRITE_SEQUENCER_477 0x31DD
+#define WM8996_WRITE_SEQUENCER_478 0x31DE
+#define WM8996_WRITE_SEQUENCER_479 0x31DF
+#define WM8996_WRITE_SEQUENCER_480 0x31E0
+#define WM8996_WRITE_SEQUENCER_481 0x31E1
+#define WM8996_WRITE_SEQUENCER_482 0x31E2
+#define WM8996_WRITE_SEQUENCER_483 0x31E3
+#define WM8996_WRITE_SEQUENCER_484 0x31E4
+#define WM8996_WRITE_SEQUENCER_485 0x31E5
+#define WM8996_WRITE_SEQUENCER_486 0x31E6
+#define WM8996_WRITE_SEQUENCER_487 0x31E7
+#define WM8996_WRITE_SEQUENCER_488 0x31E8
+#define WM8996_WRITE_SEQUENCER_489 0x31E9
+#define WM8996_WRITE_SEQUENCER_490 0x31EA
+#define WM8996_WRITE_SEQUENCER_491 0x31EB
+#define WM8996_WRITE_SEQUENCER_492 0x31EC
+#define WM8996_WRITE_SEQUENCER_493 0x31ED
+#define WM8996_WRITE_SEQUENCER_494 0x31EE
+#define WM8996_WRITE_SEQUENCER_495 0x31EF
+#define WM8996_WRITE_SEQUENCER_496 0x31F0
+#define WM8996_WRITE_SEQUENCER_497 0x31F1
+#define WM8996_WRITE_SEQUENCER_498 0x31F2
+#define WM8996_WRITE_SEQUENCER_499 0x31F3
+#define WM8996_WRITE_SEQUENCER_500 0x31F4
+#define WM8996_WRITE_SEQUENCER_501 0x31F5
+#define WM8996_WRITE_SEQUENCER_502 0x31F6
+#define WM8996_WRITE_SEQUENCER_503 0x31F7
+#define WM8996_WRITE_SEQUENCER_504 0x31F8
+#define WM8996_WRITE_SEQUENCER_505 0x31F9
+#define WM8996_WRITE_SEQUENCER_506 0x31FA
+#define WM8996_WRITE_SEQUENCER_507 0x31FB
+#define WM8996_WRITE_SEQUENCER_508 0x31FC
+#define WM8996_WRITE_SEQUENCER_509 0x31FD
+#define WM8996_WRITE_SEQUENCER_510 0x31FE
+#define WM8996_WRITE_SEQUENCER_511 0x31FF
+
+#define WM8996_REGISTER_COUNT 706
+#define WM8996_MAX_REGISTER 0x31FF
+
+/*
+ * Field Definitions.
+ */
+
+/*
+ * R0 (0x00) - Software Reset
+ */
+#define WM8996_SW_RESET_MASK 0xFFFF /* SW_RESET - [15:0] */
+#define WM8996_SW_RESET_SHIFT 0 /* SW_RESET - [15:0] */
+#define WM8996_SW_RESET_WIDTH 16 /* SW_RESET - [15:0] */
+
+/*
+ * R1 (0x01) - Power Management (1)
+ */
+#define WM8996_MICB2_ENA 0x0200 /* MICB2_ENA */
+#define WM8996_MICB2_ENA_MASK 0x0200 /* MICB2_ENA */
+#define WM8996_MICB2_ENA_SHIFT 9 /* MICB2_ENA */
+#define WM8996_MICB2_ENA_WIDTH 1 /* MICB2_ENA */
+#define WM8996_MICB1_ENA 0x0100 /* MICB1_ENA */
+#define WM8996_MICB1_ENA_MASK 0x0100 /* MICB1_ENA */
+#define WM8996_MICB1_ENA_SHIFT 8 /* MICB1_ENA */
+#define WM8996_MICB1_ENA_WIDTH 1 /* MICB1_ENA */
+#define WM8996_HPOUT2L_ENA 0x0080 /* HPOUT2L_ENA */
+#define WM8996_HPOUT2L_ENA_MASK 0x0080 /* HPOUT2L_ENA */
+#define WM8996_HPOUT2L_ENA_SHIFT 7 /* HPOUT2L_ENA */
+#define WM8996_HPOUT2L_ENA_WIDTH 1 /* HPOUT2L_ENA */
+#define WM8996_HPOUT2R_ENA 0x0040 /* HPOUT2R_ENA */
+#define WM8996_HPOUT2R_ENA_MASK 0x0040 /* HPOUT2R_ENA */
+#define WM8996_HPOUT2R_ENA_SHIFT 6 /* HPOUT2R_ENA */
+#define WM8996_HPOUT2R_ENA_WIDTH 1 /* HPOUT2R_ENA */
+#define WM8996_HPOUT1L_ENA 0x0020 /* HPOUT1L_ENA */
+#define WM8996_HPOUT1L_ENA_MASK 0x0020 /* HPOUT1L_ENA */
+#define WM8996_HPOUT1L_ENA_SHIFT 5 /* HPOUT1L_ENA */
+#define WM8996_HPOUT1L_ENA_WIDTH 1 /* HPOUT1L_ENA */
+#define WM8996_HPOUT1R_ENA 0x0010 /* HPOUT1R_ENA */
+#define WM8996_HPOUT1R_ENA_MASK 0x0010 /* HPOUT1R_ENA */
+#define WM8996_HPOUT1R_ENA_SHIFT 4 /* HPOUT1R_ENA */
+#define WM8996_HPOUT1R_ENA_WIDTH 1 /* HPOUT1R_ENA */
+#define WM8996_BG_ENA 0x0001 /* BG_ENA */
+#define WM8996_BG_ENA_MASK 0x0001 /* BG_ENA */
+#define WM8996_BG_ENA_SHIFT 0 /* BG_ENA */
+#define WM8996_BG_ENA_WIDTH 1 /* BG_ENA */
+
+/*
+ * R2 (0x02) - Power Management (2)
+ */
+#define WM8996_OPCLK_ENA 0x0800 /* OPCLK_ENA */
+#define WM8996_OPCLK_ENA_MASK 0x0800 /* OPCLK_ENA */
+#define WM8996_OPCLK_ENA_SHIFT 11 /* OPCLK_ENA */
+#define WM8996_OPCLK_ENA_WIDTH 1 /* OPCLK_ENA */
+#define WM8996_INL_ENA 0x0020 /* INL_ENA */
+#define WM8996_INL_ENA_MASK 0x0020 /* INL_ENA */
+#define WM8996_INL_ENA_SHIFT 5 /* INL_ENA */
+#define WM8996_INL_ENA_WIDTH 1 /* INL_ENA */
+#define WM8996_INR_ENA 0x0010 /* INR_ENA */
+#define WM8996_INR_ENA_MASK 0x0010 /* INR_ENA */
+#define WM8996_INR_ENA_SHIFT 4 /* INR_ENA */
+#define WM8996_INR_ENA_WIDTH 1 /* INR_ENA */
+#define WM8996_LDO2_ENA 0x0002 /* LDO2_ENA */
+#define WM8996_LDO2_ENA_MASK 0x0002 /* LDO2_ENA */
+#define WM8996_LDO2_ENA_SHIFT 1 /* LDO2_ENA */
+#define WM8996_LDO2_ENA_WIDTH 1 /* LDO2_ENA */
+
+/*
+ * R3 (0x03) - Power Management (3)
+ */
+#define WM8996_DSP2RXL_ENA 0x0800 /* DSP2RXL_ENA */
+#define WM8996_DSP2RXL_ENA_MASK 0x0800 /* DSP2RXL_ENA */
+#define WM8996_DSP2RXL_ENA_SHIFT 11 /* DSP2RXL_ENA */
+#define WM8996_DSP2RXL_ENA_WIDTH 1 /* DSP2RXL_ENA */
+#define WM8996_DSP2RXR_ENA 0x0400 /* DSP2RXR_ENA */
+#define WM8996_DSP2RXR_ENA_MASK 0x0400 /* DSP2RXR_ENA */
+#define WM8996_DSP2RXR_ENA_SHIFT 10 /* DSP2RXR_ENA */
+#define WM8996_DSP2RXR_ENA_WIDTH 1 /* DSP2RXR_ENA */
+#define WM8996_DSP1RXL_ENA 0x0200 /* DSP1RXL_ENA */
+#define WM8996_DSP1RXL_ENA_MASK 0x0200 /* DSP1RXL_ENA */
+#define WM8996_DSP1RXL_ENA_SHIFT 9 /* DSP1RXL_ENA */
+#define WM8996_DSP1RXL_ENA_WIDTH 1 /* DSP1RXL_ENA */
+#define WM8996_DSP1RXR_ENA 0x0100 /* DSP1RXR_ENA */
+#define WM8996_DSP1RXR_ENA_MASK 0x0100 /* DSP1RXR_ENA */
+#define WM8996_DSP1RXR_ENA_SHIFT 8 /* DSP1RXR_ENA */
+#define WM8996_DSP1RXR_ENA_WIDTH 1 /* DSP1RXR_ENA */
+#define WM8996_DMIC2L_ENA 0x0020 /* DMIC2L_ENA */
+#define WM8996_DMIC2L_ENA_MASK 0x0020 /* DMIC2L_ENA */
+#define WM8996_DMIC2L_ENA_SHIFT 5 /* DMIC2L_ENA */
+#define WM8996_DMIC2L_ENA_WIDTH 1 /* DMIC2L_ENA */
+#define WM8996_DMIC2R_ENA 0x0010 /* DMIC2R_ENA */
+#define WM8996_DMIC2R_ENA_MASK 0x0010 /* DMIC2R_ENA */
+#define WM8996_DMIC2R_ENA_SHIFT 4 /* DMIC2R_ENA */
+#define WM8996_DMIC2R_ENA_WIDTH 1 /* DMIC2R_ENA */
+#define WM8996_DMIC1L_ENA 0x0008 /* DMIC1L_ENA */
+#define WM8996_DMIC1L_ENA_MASK 0x0008 /* DMIC1L_ENA */
+#define WM8996_DMIC1L_ENA_SHIFT 3 /* DMIC1L_ENA */
+#define WM8996_DMIC1L_ENA_WIDTH 1 /* DMIC1L_ENA */
+#define WM8996_DMIC1R_ENA 0x0004 /* DMIC1R_ENA */
+#define WM8996_DMIC1R_ENA_MASK 0x0004 /* DMIC1R_ENA */
+#define WM8996_DMIC1R_ENA_SHIFT 2 /* DMIC1R_ENA */
+#define WM8996_DMIC1R_ENA_WIDTH 1 /* DMIC1R_ENA */
+#define WM8996_ADCL_ENA 0x0002 /* ADCL_ENA */
+#define WM8996_ADCL_ENA_MASK 0x0002 /* ADCL_ENA */
+#define WM8996_ADCL_ENA_SHIFT 1 /* ADCL_ENA */
+#define WM8996_ADCL_ENA_WIDTH 1 /* ADCL_ENA */
+#define WM8996_ADCR_ENA 0x0001 /* ADCR_ENA */
+#define WM8996_ADCR_ENA_MASK 0x0001 /* ADCR_ENA */
+#define WM8996_ADCR_ENA_SHIFT 0 /* ADCR_ENA */
+#define WM8996_ADCR_ENA_WIDTH 1 /* ADCR_ENA */
+
+/*
+ * R4 (0x04) - Power Management (4)
+ */
+#define WM8996_AIF2RX_CHAN1_ENA 0x0200 /* AIF2RX_CHAN1_ENA */
+#define WM8996_AIF2RX_CHAN1_ENA_MASK 0x0200 /* AIF2RX_CHAN1_ENA */
+#define WM8996_AIF2RX_CHAN1_ENA_SHIFT 9 /* AIF2RX_CHAN1_ENA */
+#define WM8996_AIF2RX_CHAN1_ENA_WIDTH 1 /* AIF2RX_CHAN1_ENA */
+#define WM8996_AIF2RX_CHAN0_ENA 0x0100 /* AIF2RX_CHAN0_ENA */
+#define WM8996_AIF2RX_CHAN0_ENA_MASK 0x0100 /* AIF2RX_CHAN0_ENA */
+#define WM8996_AIF2RX_CHAN0_ENA_SHIFT 8 /* AIF2RX_CHAN0_ENA */
+#define WM8996_AIF2RX_CHAN0_ENA_WIDTH 1 /* AIF2RX_CHAN0_ENA */
+#define WM8996_AIF1RX_CHAN5_ENA 0x0020 /* AIF1RX_CHAN5_ENA */
+#define WM8996_AIF1RX_CHAN5_ENA_MASK 0x0020 /* AIF1RX_CHAN5_ENA */
+#define WM8996_AIF1RX_CHAN5_ENA_SHIFT 5 /* AIF1RX_CHAN5_ENA */
+#define WM8996_AIF1RX_CHAN5_ENA_WIDTH 1 /* AIF1RX_CHAN5_ENA */
+#define WM8996_AIF1RX_CHAN4_ENA 0x0010 /* AIF1RX_CHAN4_ENA */
+#define WM8996_AIF1RX_CHAN4_ENA_MASK 0x0010 /* AIF1RX_CHAN4_ENA */
+#define WM8996_AIF1RX_CHAN4_ENA_SHIFT 4 /* AIF1RX_CHAN4_ENA */
+#define WM8996_AIF1RX_CHAN4_ENA_WIDTH 1 /* AIF1RX_CHAN4_ENA */
+#define WM8996_AIF1RX_CHAN3_ENA 0x0008 /* AIF1RX_CHAN3_ENA */
+#define WM8996_AIF1RX_CHAN3_ENA_MASK 0x0008 /* AIF1RX_CHAN3_ENA */
+#define WM8996_AIF1RX_CHAN3_ENA_SHIFT 3 /* AIF1RX_CHAN3_ENA */
+#define WM8996_AIF1RX_CHAN3_ENA_WIDTH 1 /* AIF1RX_CHAN3_ENA */
+#define WM8996_AIF1RX_CHAN2_ENA 0x0004 /* AIF1RX_CHAN2_ENA */
+#define WM8996_AIF1RX_CHAN2_ENA_MASK 0x0004 /* AIF1RX_CHAN2_ENA */
+#define WM8996_AIF1RX_CHAN2_ENA_SHIFT 2 /* AIF1RX_CHAN2_ENA */
+#define WM8996_AIF1RX_CHAN2_ENA_WIDTH 1 /* AIF1RX_CHAN2_ENA */
+#define WM8996_AIF1RX_CHAN1_ENA 0x0002 /* AIF1RX_CHAN1_ENA */
+#define WM8996_AIF1RX_CHAN1_ENA_MASK 0x0002 /* AIF1RX_CHAN1_ENA */
+#define WM8996_AIF1RX_CHAN1_ENA_SHIFT 1 /* AIF1RX_CHAN1_ENA */
+#define WM8996_AIF1RX_CHAN1_ENA_WIDTH 1 /* AIF1RX_CHAN1_ENA */
+#define WM8996_AIF1RX_CHAN0_ENA 0x0001 /* AIF1RX_CHAN0_ENA */
+#define WM8996_AIF1RX_CHAN0_ENA_MASK 0x0001 /* AIF1RX_CHAN0_ENA */
+#define WM8996_AIF1RX_CHAN0_ENA_SHIFT 0 /* AIF1RX_CHAN0_ENA */
+#define WM8996_AIF1RX_CHAN0_ENA_WIDTH 1 /* AIF1RX_CHAN0_ENA */
+
+/*
+ * R5 (0x05) - Power Management (5)
+ */
+#define WM8996_DSP2TXL_ENA 0x0800 /* DSP2TXL_ENA */
+#define WM8996_DSP2TXL_ENA_MASK 0x0800 /* DSP2TXL_ENA */
+#define WM8996_DSP2TXL_ENA_SHIFT 11 /* DSP2TXL_ENA */
+#define WM8996_DSP2TXL_ENA_WIDTH 1 /* DSP2TXL_ENA */
+#define WM8996_DSP2TXR_ENA 0x0400 /* DSP2TXR_ENA */
+#define WM8996_DSP2TXR_ENA_MASK 0x0400 /* DSP2TXR_ENA */
+#define WM8996_DSP2TXR_ENA_SHIFT 10 /* DSP2TXR_ENA */
+#define WM8996_DSP2TXR_ENA_WIDTH 1 /* DSP2TXR_ENA */
+#define WM8996_DSP1TXL_ENA 0x0200 /* DSP1TXL_ENA */
+#define WM8996_DSP1TXL_ENA_MASK 0x0200 /* DSP1TXL_ENA */
+#define WM8996_DSP1TXL_ENA_SHIFT 9 /* DSP1TXL_ENA */
+#define WM8996_DSP1TXL_ENA_WIDTH 1 /* DSP1TXL_ENA */
+#define WM8996_DSP1TXR_ENA 0x0100 /* DSP1TXR_ENA */
+#define WM8996_DSP1TXR_ENA_MASK 0x0100 /* DSP1TXR_ENA */
+#define WM8996_DSP1TXR_ENA_SHIFT 8 /* DSP1TXR_ENA */
+#define WM8996_DSP1TXR_ENA_WIDTH 1 /* DSP1TXR_ENA */
+#define WM8996_DAC2L_ENA 0x0008 /* DAC2L_ENA */
+#define WM8996_DAC2L_ENA_MASK 0x0008 /* DAC2L_ENA */
+#define WM8996_DAC2L_ENA_SHIFT 3 /* DAC2L_ENA */
+#define WM8996_DAC2L_ENA_WIDTH 1 /* DAC2L_ENA */
+#define WM8996_DAC2R_ENA 0x0004 /* DAC2R_ENA */
+#define WM8996_DAC2R_ENA_MASK 0x0004 /* DAC2R_ENA */
+#define WM8996_DAC2R_ENA_SHIFT 2 /* DAC2R_ENA */
+#define WM8996_DAC2R_ENA_WIDTH 1 /* DAC2R_ENA */
+#define WM8996_DAC1L_ENA 0x0002 /* DAC1L_ENA */
+#define WM8996_DAC1L_ENA_MASK 0x0002 /* DAC1L_ENA */
+#define WM8996_DAC1L_ENA_SHIFT 1 /* DAC1L_ENA */
+#define WM8996_DAC1L_ENA_WIDTH 1 /* DAC1L_ENA */
+#define WM8996_DAC1R_ENA 0x0001 /* DAC1R_ENA */
+#define WM8996_DAC1R_ENA_MASK 0x0001 /* DAC1R_ENA */
+#define WM8996_DAC1R_ENA_SHIFT 0 /* DAC1R_ENA */
+#define WM8996_DAC1R_ENA_WIDTH 1 /* DAC1R_ENA */
+
+/*
+ * R6 (0x06) - Power Management (6)
+ */
+#define WM8996_AIF2TX_CHAN1_ENA 0x0200 /* AIF2TX_CHAN1_ENA */
+#define WM8996_AIF2TX_CHAN1_ENA_MASK 0x0200 /* AIF2TX_CHAN1_ENA */
+#define WM8996_AIF2TX_CHAN1_ENA_SHIFT 9 /* AIF2TX_CHAN1_ENA */
+#define WM8996_AIF2TX_CHAN1_ENA_WIDTH 1 /* AIF2TX_CHAN1_ENA */
+#define WM8996_AIF2TX_CHAN0_ENA 0x0100 /* AIF2TX_CHAN0_ENA */
+#define WM8996_AIF2TX_CHAN0_ENA_MASK 0x0100 /* AIF2TX_CHAN0_ENA */
+#define WM8996_AIF2TX_CHAN0_ENA_SHIFT 8 /* AIF2TX_CHAN0_ENA */
+#define WM8996_AIF2TX_CHAN0_ENA_WIDTH 1 /* AIF2TX_CHAN0_ENA */
+#define WM8996_AIF1TX_CHAN5_ENA 0x0020 /* AIF1TX_CHAN5_ENA */
+#define WM8996_AIF1TX_CHAN5_ENA_MASK 0x0020 /* AIF1TX_CHAN5_ENA */
+#define WM8996_AIF1TX_CHAN5_ENA_SHIFT 5 /* AIF1TX_CHAN5_ENA */
+#define WM8996_AIF1TX_CHAN5_ENA_WIDTH 1 /* AIF1TX_CHAN5_ENA */
+#define WM8996_AIF1TX_CHAN4_ENA 0x0010 /* AIF1TX_CHAN4_ENA */
+#define WM8996_AIF1TX_CHAN4_ENA_MASK 0x0010 /* AIF1TX_CHAN4_ENA */
+#define WM8996_AIF1TX_CHAN4_ENA_SHIFT 4 /* AIF1TX_CHAN4_ENA */
+#define WM8996_AIF1TX_CHAN4_ENA_WIDTH 1 /* AIF1TX_CHAN4_ENA */
+#define WM8996_AIF1TX_CHAN3_ENA 0x0008 /* AIF1TX_CHAN3_ENA */
+#define WM8996_AIF1TX_CHAN3_ENA_MASK 0x0008 /* AIF1TX_CHAN3_ENA */
+#define WM8996_AIF1TX_CHAN3_ENA_SHIFT 3 /* AIF1TX_CHAN3_ENA */
+#define WM8996_AIF1TX_CHAN3_ENA_WIDTH 1 /* AIF1TX_CHAN3_ENA */
+#define WM8996_AIF1TX_CHAN2_ENA 0x0004 /* AIF1TX_CHAN2_ENA */
+#define WM8996_AIF1TX_CHAN2_ENA_MASK 0x0004 /* AIF1TX_CHAN2_ENA */
+#define WM8996_AIF1TX_CHAN2_ENA_SHIFT 2 /* AIF1TX_CHAN2_ENA */
+#define WM8996_AIF1TX_CHAN2_ENA_WIDTH 1 /* AIF1TX_CHAN2_ENA */
+#define WM8996_AIF1TX_CHAN1_ENA 0x0002 /* AIF1TX_CHAN1_ENA */
+#define WM8996_AIF1TX_CHAN1_ENA_MASK 0x0002 /* AIF1TX_CHAN1_ENA */
+#define WM8996_AIF1TX_CHAN1_ENA_SHIFT 1 /* AIF1TX_CHAN1_ENA */
+#define WM8996_AIF1TX_CHAN1_ENA_WIDTH 1 /* AIF1TX_CHAN1_ENA */
+#define WM8996_AIF1TX_CHAN0_ENA 0x0001 /* AIF1TX_CHAN0_ENA */
+#define WM8996_AIF1TX_CHAN0_ENA_MASK 0x0001 /* AIF1TX_CHAN0_ENA */
+#define WM8996_AIF1TX_CHAN0_ENA_SHIFT 0 /* AIF1TX_CHAN0_ENA */
+#define WM8996_AIF1TX_CHAN0_ENA_WIDTH 1 /* AIF1TX_CHAN0_ENA */
+
+/*
+ * R7 (0x07) - Power Management (7)
+ */
+#define WM8996_DMIC2_FN 0x0200 /* DMIC2_FN */
+#define WM8996_DMIC2_FN_MASK 0x0200 /* DMIC2_FN */
+#define WM8996_DMIC2_FN_SHIFT 9 /* DMIC2_FN */
+#define WM8996_DMIC2_FN_WIDTH 1 /* DMIC2_FN */
+#define WM8996_DMIC1_FN 0x0100 /* DMIC1_FN */
+#define WM8996_DMIC1_FN_MASK 0x0100 /* DMIC1_FN */
+#define WM8996_DMIC1_FN_SHIFT 8 /* DMIC1_FN */
+#define WM8996_DMIC1_FN_WIDTH 1 /* DMIC1_FN */
+#define WM8996_ADC_DMIC_DSP2R_ENA 0x0080 /* ADC_DMIC_DSP2R_ENA */
+#define WM8996_ADC_DMIC_DSP2R_ENA_MASK 0x0080 /* ADC_DMIC_DSP2R_ENA */
+#define WM8996_ADC_DMIC_DSP2R_ENA_SHIFT 7 /* ADC_DMIC_DSP2R_ENA */
+#define WM8996_ADC_DMIC_DSP2R_ENA_WIDTH 1 /* ADC_DMIC_DSP2R_ENA */
+#define WM8996_ADC_DMIC_DSP2L_ENA 0x0040 /* ADC_DMIC_DSP2L_ENA */
+#define WM8996_ADC_DMIC_DSP2L_ENA_MASK 0x0040 /* ADC_DMIC_DSP2L_ENA */
+#define WM8996_ADC_DMIC_DSP2L_ENA_SHIFT 6 /* ADC_DMIC_DSP2L_ENA */
+#define WM8996_ADC_DMIC_DSP2L_ENA_WIDTH 1 /* ADC_DMIC_DSP2L_ENA */
+#define WM8996_ADC_DMIC_SRC2_MASK 0x0030 /* ADC_DMIC_SRC2 - [5:4] */
+#define WM8996_ADC_DMIC_SRC2_SHIFT 4 /* ADC_DMIC_SRC2 - [5:4] */
+#define WM8996_ADC_DMIC_SRC2_WIDTH 2 /* ADC_DMIC_SRC2 - [5:4] */
+#define WM8996_ADC_DMIC_DSP1R_ENA 0x0008 /* ADC_DMIC_DSP1R_ENA */
+#define WM8996_ADC_DMIC_DSP1R_ENA_MASK 0x0008 /* ADC_DMIC_DSP1R_ENA */
+#define WM8996_ADC_DMIC_DSP1R_ENA_SHIFT 3 /* ADC_DMIC_DSP1R_ENA */
+#define WM8996_ADC_DMIC_DSP1R_ENA_WIDTH 1 /* ADC_DMIC_DSP1R_ENA */
+#define WM8996_ADC_DMIC_DSP1L_ENA 0x0004 /* ADC_DMIC_DSP1L_ENA */
+#define WM8996_ADC_DMIC_DSP1L_ENA_MASK 0x0004 /* ADC_DMIC_DSP1L_ENA */
+#define WM8996_ADC_DMIC_DSP1L_ENA_SHIFT 2 /* ADC_DMIC_DSP1L_ENA */
+#define WM8996_ADC_DMIC_DSP1L_ENA_WIDTH 1 /* ADC_DMIC_DSP1L_ENA */
+#define WM8996_ADC_DMIC_SRC1_MASK 0x0003 /* ADC_DMIC_SRC1 - [1:0] */
+#define WM8996_ADC_DMIC_SRC1_SHIFT 0 /* ADC_DMIC_SRC1 - [1:0] */
+#define WM8996_ADC_DMIC_SRC1_WIDTH 2 /* ADC_DMIC_SRC1 - [1:0] */
+
+/*
+ * R8 (0x08) - Power Management (8)
+ */
+#define WM8996_AIF2TX_SRC_MASK 0x00C0 /* AIF2TX_SRC - [7:6] */
+#define WM8996_AIF2TX_SRC_SHIFT 6 /* AIF2TX_SRC - [7:6] */
+#define WM8996_AIF2TX_SRC_WIDTH 2 /* AIF2TX_SRC - [7:6] */
+#define WM8996_DSP2RX_SRC 0x0010 /* DSP2RX_SRC */
+#define WM8996_DSP2RX_SRC_MASK 0x0010 /* DSP2RX_SRC */
+#define WM8996_DSP2RX_SRC_SHIFT 4 /* DSP2RX_SRC */
+#define WM8996_DSP2RX_SRC_WIDTH 1 /* DSP2RX_SRC */
+#define WM8996_DSP1RX_SRC 0x0001 /* DSP1RX_SRC */
+#define WM8996_DSP1RX_SRC_MASK 0x0001 /* DSP1RX_SRC */
+#define WM8996_DSP1RX_SRC_SHIFT 0 /* DSP1RX_SRC */
+#define WM8996_DSP1RX_SRC_WIDTH 1 /* DSP1RX_SRC */
+
+/*
+ * R16 (0x10) - Left Line Input Volume
+ */
+#define WM8996_IN1_VU 0x0080 /* IN1_VU */
+#define WM8996_IN1_VU_MASK 0x0080 /* IN1_VU */
+#define WM8996_IN1_VU_SHIFT 7 /* IN1_VU */
+#define WM8996_IN1_VU_WIDTH 1 /* IN1_VU */
+#define WM8996_IN1L_ZC 0x0020 /* IN1L_ZC */
+#define WM8996_IN1L_ZC_MASK 0x0020 /* IN1L_ZC */
+#define WM8996_IN1L_ZC_SHIFT 5 /* IN1L_ZC */
+#define WM8996_IN1L_ZC_WIDTH 1 /* IN1L_ZC */
+#define WM8996_IN1L_VOL_MASK 0x001F /* IN1L_VOL - [4:0] */
+#define WM8996_IN1L_VOL_SHIFT 0 /* IN1L_VOL - [4:0] */
+#define WM8996_IN1L_VOL_WIDTH 5 /* IN1L_VOL - [4:0] */
+
+/*
+ * R17 (0x11) - Right Line Input Volume
+ */
+#define WM8996_IN1_VU 0x0080 /* IN1_VU */
+#define WM8996_IN1_VU_MASK 0x0080 /* IN1_VU */
+#define WM8996_IN1_VU_SHIFT 7 /* IN1_VU */
+#define WM8996_IN1_VU_WIDTH 1 /* IN1_VU */
+#define WM8996_IN1R_ZC 0x0020 /* IN1R_ZC */
+#define WM8996_IN1R_ZC_MASK 0x0020 /* IN1R_ZC */
+#define WM8996_IN1R_ZC_SHIFT 5 /* IN1R_ZC */
+#define WM8996_IN1R_ZC_WIDTH 1 /* IN1R_ZC */
+#define WM8996_IN1R_VOL_MASK 0x001F /* IN1R_VOL - [4:0] */
+#define WM8996_IN1R_VOL_SHIFT 0 /* IN1R_VOL - [4:0] */
+#define WM8996_IN1R_VOL_WIDTH 5 /* IN1R_VOL - [4:0] */
+
+/*
+ * R18 (0x12) - Line Input Control
+ */
+#define WM8996_INL_MODE_MASK 0x000C /* INL_MODE - [3:2] */
+#define WM8996_INL_MODE_SHIFT 2 /* INL_MODE - [3:2] */
+#define WM8996_INL_MODE_WIDTH 2 /* INL_MODE - [3:2] */
+#define WM8996_INR_MODE_MASK 0x0003 /* INR_MODE - [1:0] */
+#define WM8996_INR_MODE_SHIFT 0 /* INR_MODE - [1:0] */
+#define WM8996_INR_MODE_WIDTH 2 /* INR_MODE - [1:0] */
+
+/*
+ * R21 (0x15) - DAC1 HPOUT1 Volume
+ */
+#define WM8996_DAC1R_HPOUT1R_VOL_MASK 0x00F0 /* DAC1R_HPOUT1R_VOL - [7:4] */
+#define WM8996_DAC1R_HPOUT1R_VOL_SHIFT 4 /* DAC1R_HPOUT1R_VOL - [7:4] */
+#define WM8996_DAC1R_HPOUT1R_VOL_WIDTH 4 /* DAC1R_HPOUT1R_VOL - [7:4] */
+#define WM8996_DAC1L_HPOUT1L_VOL_MASK 0x000F /* DAC1L_HPOUT1L_VOL - [3:0] */
+#define WM8996_DAC1L_HPOUT1L_VOL_SHIFT 0 /* DAC1L_HPOUT1L_VOL - [3:0] */
+#define WM8996_DAC1L_HPOUT1L_VOL_WIDTH 4 /* DAC1L_HPOUT1L_VOL - [3:0] */
+
+/*
+ * R22 (0x16) - DAC2 HPOUT2 Volume
+ */
+#define WM8996_DAC2R_HPOUT2R_VOL_MASK 0x00F0 /* DAC2R_HPOUT2R_VOL - [7:4] */
+#define WM8996_DAC2R_HPOUT2R_VOL_SHIFT 4 /* DAC2R_HPOUT2R_VOL - [7:4] */
+#define WM8996_DAC2R_HPOUT2R_VOL_WIDTH 4 /* DAC2R_HPOUT2R_VOL - [7:4] */
+#define WM8996_DAC2L_HPOUT2L_VOL_MASK 0x000F /* DAC2L_HPOUT2L_VOL - [3:0] */
+#define WM8996_DAC2L_HPOUT2L_VOL_SHIFT 0 /* DAC2L_HPOUT2L_VOL - [3:0] */
+#define WM8996_DAC2L_HPOUT2L_VOL_WIDTH 4 /* DAC2L_HPOUT2L_VOL - [3:0] */
+
+/*
+ * R24 (0x18) - DAC1 Left Volume
+ */
+#define WM8996_DAC1L_MUTE 0x0200 /* DAC1L_MUTE */
+#define WM8996_DAC1L_MUTE_MASK 0x0200 /* DAC1L_MUTE */
+#define WM8996_DAC1L_MUTE_SHIFT 9 /* DAC1L_MUTE */
+#define WM8996_DAC1L_MUTE_WIDTH 1 /* DAC1L_MUTE */
+#define WM8996_DAC1_VU 0x0100 /* DAC1_VU */
+#define WM8996_DAC1_VU_MASK 0x0100 /* DAC1_VU */
+#define WM8996_DAC1_VU_SHIFT 8 /* DAC1_VU */
+#define WM8996_DAC1_VU_WIDTH 1 /* DAC1_VU */
+#define WM8996_DAC1L_VOL_MASK 0x00FF /* DAC1L_VOL - [7:0] */
+#define WM8996_DAC1L_VOL_SHIFT 0 /* DAC1L_VOL - [7:0] */
+#define WM8996_DAC1L_VOL_WIDTH 8 /* DAC1L_VOL - [7:0] */
+
+/*
+ * R25 (0x19) - DAC1 Right Volume
+ */
+#define WM8996_DAC1R_MUTE 0x0200 /* DAC1R_MUTE */
+#define WM8996_DAC1R_MUTE_MASK 0x0200 /* DAC1R_MUTE */
+#define WM8996_DAC1R_MUTE_SHIFT 9 /* DAC1R_MUTE */
+#define WM8996_DAC1R_MUTE_WIDTH 1 /* DAC1R_MUTE */
+#define WM8996_DAC1_VU 0x0100 /* DAC1_VU */
+#define WM8996_DAC1_VU_MASK 0x0100 /* DAC1_VU */
+#define WM8996_DAC1_VU_SHIFT 8 /* DAC1_VU */
+#define WM8996_DAC1_VU_WIDTH 1 /* DAC1_VU */
+#define WM8996_DAC1R_VOL_MASK 0x00FF /* DAC1R_VOL - [7:0] */
+#define WM8996_DAC1R_VOL_SHIFT 0 /* DAC1R_VOL - [7:0] */
+#define WM8996_DAC1R_VOL_WIDTH 8 /* DAC1R_VOL - [7:0] */
+
+/*
+ * R26 (0x1A) - DAC2 Left Volume
+ */
+#define WM8996_DAC2L_MUTE 0x0200 /* DAC2L_MUTE */
+#define WM8996_DAC2L_MUTE_MASK 0x0200 /* DAC2L_MUTE */
+#define WM8996_DAC2L_MUTE_SHIFT 9 /* DAC2L_MUTE */
+#define WM8996_DAC2L_MUTE_WIDTH 1 /* DAC2L_MUTE */
+#define WM8996_DAC2_VU 0x0100 /* DAC2_VU */
+#define WM8996_DAC2_VU_MASK 0x0100 /* DAC2_VU */
+#define WM8996_DAC2_VU_SHIFT 8 /* DAC2_VU */
+#define WM8996_DAC2_VU_WIDTH 1 /* DAC2_VU */
+#define WM8996_DAC2L_VOL_MASK 0x00FF /* DAC2L_VOL - [7:0] */
+#define WM8996_DAC2L_VOL_SHIFT 0 /* DAC2L_VOL - [7:0] */
+#define WM8996_DAC2L_VOL_WIDTH 8 /* DAC2L_VOL - [7:0] */
+
+/*
+ * R27 (0x1B) - DAC2 Right Volume
+ */
+#define WM8996_DAC2R_MUTE 0x0200 /* DAC2R_MUTE */
+#define WM8996_DAC2R_MUTE_MASK 0x0200 /* DAC2R_MUTE */
+#define WM8996_DAC2R_MUTE_SHIFT 9 /* DAC2R_MUTE */
+#define WM8996_DAC2R_MUTE_WIDTH 1 /* DAC2R_MUTE */
+#define WM8996_DAC2_VU 0x0100 /* DAC2_VU */
+#define WM8996_DAC2_VU_MASK 0x0100 /* DAC2_VU */
+#define WM8996_DAC2_VU_SHIFT 8 /* DAC2_VU */
+#define WM8996_DAC2_VU_WIDTH 1 /* DAC2_VU */
+#define WM8996_DAC2R_VOL_MASK 0x00FF /* DAC2R_VOL - [7:0] */
+#define WM8996_DAC2R_VOL_SHIFT 0 /* DAC2R_VOL - [7:0] */
+#define WM8996_DAC2R_VOL_WIDTH 8 /* DAC2R_VOL - [7:0] */
+
+/*
+ * R28 (0x1C) - Output1 Left Volume
+ */
+#define WM8996_DAC1_VU 0x0100 /* DAC1_VU */
+#define WM8996_DAC1_VU_MASK 0x0100 /* DAC1_VU */
+#define WM8996_DAC1_VU_SHIFT 8 /* DAC1_VU */
+#define WM8996_DAC1_VU_WIDTH 1 /* DAC1_VU */
+#define WM8996_HPOUT1L_ZC 0x0080 /* HPOUT1L_ZC */
+#define WM8996_HPOUT1L_ZC_MASK 0x0080 /* HPOUT1L_ZC */
+#define WM8996_HPOUT1L_ZC_SHIFT 7 /* HPOUT1L_ZC */
+#define WM8996_HPOUT1L_ZC_WIDTH 1 /* HPOUT1L_ZC */
+#define WM8996_HPOUT1L_VOL_MASK 0x000F /* HPOUT1L_VOL - [3:0] */
+#define WM8996_HPOUT1L_VOL_SHIFT 0 /* HPOUT1L_VOL - [3:0] */
+#define WM8996_HPOUT1L_VOL_WIDTH 4 /* HPOUT1L_VOL - [3:0] */
+
+/*
+ * R29 (0x1D) - Output1 Right Volume
+ */
+#define WM8996_DAC1_VU 0x0100 /* DAC1_VU */
+#define WM8996_DAC1_VU_MASK 0x0100 /* DAC1_VU */
+#define WM8996_DAC1_VU_SHIFT 8 /* DAC1_VU */
+#define WM8996_DAC1_VU_WIDTH 1 /* DAC1_VU */
+#define WM8996_HPOUT1R_ZC 0x0080 /* HPOUT1R_ZC */
+#define WM8996_HPOUT1R_ZC_MASK 0x0080 /* HPOUT1R_ZC */
+#define WM8996_HPOUT1R_ZC_SHIFT 7 /* HPOUT1R_ZC */
+#define WM8996_HPOUT1R_ZC_WIDTH 1 /* HPOUT1R_ZC */
+#define WM8996_HPOUT1R_VOL_MASK 0x000F /* HPOUT1R_VOL - [3:0] */
+#define WM8996_HPOUT1R_VOL_SHIFT 0 /* HPOUT1R_VOL - [3:0] */
+#define WM8996_HPOUT1R_VOL_WIDTH 4 /* HPOUT1R_VOL - [3:0] */
+
+/*
+ * R30 (0x1E) - Output2 Left Volume
+ */
+#define WM8996_DAC2_VU 0x0100 /* DAC2_VU */
+#define WM8996_DAC2_VU_MASK 0x0100 /* DAC2_VU */
+#define WM8996_DAC2_VU_SHIFT 8 /* DAC2_VU */
+#define WM8996_DAC2_VU_WIDTH 1 /* DAC2_VU */
+#define WM8996_HPOUT2L_ZC 0x0080 /* HPOUT2L_ZC */
+#define WM8996_HPOUT2L_ZC_MASK 0x0080 /* HPOUT2L_ZC */
+#define WM8996_HPOUT2L_ZC_SHIFT 7 /* HPOUT2L_ZC */
+#define WM8996_HPOUT2L_ZC_WIDTH 1 /* HPOUT2L_ZC */
+#define WM8996_HPOUT2L_VOL_MASK 0x000F /* HPOUT2L_VOL - [3:0] */
+#define WM8996_HPOUT2L_VOL_SHIFT 0 /* HPOUT2L_VOL - [3:0] */
+#define WM8996_HPOUT2L_VOL_WIDTH 4 /* HPOUT2L_VOL - [3:0] */
+
+/*
+ * R31 (0x1F) - Output2 Right Volume
+ */
+#define WM8996_DAC2_VU 0x0100 /* DAC2_VU */
+#define WM8996_DAC2_VU_MASK 0x0100 /* DAC2_VU */
+#define WM8996_DAC2_VU_SHIFT 8 /* DAC2_VU */
+#define WM8996_DAC2_VU_WIDTH 1 /* DAC2_VU */
+#define WM8996_HPOUT2R_ZC 0x0080 /* HPOUT2R_ZC */
+#define WM8996_HPOUT2R_ZC_MASK 0x0080 /* HPOUT2R_ZC */
+#define WM8996_HPOUT2R_ZC_SHIFT 7 /* HPOUT2R_ZC */
+#define WM8996_HPOUT2R_ZC_WIDTH 1 /* HPOUT2R_ZC */
+#define WM8996_HPOUT2R_VOL_MASK 0x000F /* HPOUT2R_VOL - [3:0] */
+#define WM8996_HPOUT2R_VOL_SHIFT 0 /* HPOUT2R_VOL - [3:0] */
+#define WM8996_HPOUT2R_VOL_WIDTH 4 /* HPOUT2R_VOL - [3:0] */
+
+/*
+ * R32 (0x20) - MICBIAS (1)
+ */
+#define WM8996_MICB1_RATE 0x0020 /* MICB1_RATE */
+#define WM8996_MICB1_RATE_MASK 0x0020 /* MICB1_RATE */
+#define WM8996_MICB1_RATE_SHIFT 5 /* MICB1_RATE */
+#define WM8996_MICB1_RATE_WIDTH 1 /* MICB1_RATE */
+#define WM8996_MICB1_MODE 0x0010 /* MICB1_MODE */
+#define WM8996_MICB1_MODE_MASK 0x0010 /* MICB1_MODE */
+#define WM8996_MICB1_MODE_SHIFT 4 /* MICB1_MODE */
+#define WM8996_MICB1_MODE_WIDTH 1 /* MICB1_MODE */
+#define WM8996_MICB1_LVL_MASK 0x000E /* MICB1_LVL - [3:1] */
+#define WM8996_MICB1_LVL_SHIFT 1 /* MICB1_LVL - [3:1] */
+#define WM8996_MICB1_LVL_WIDTH 3 /* MICB1_LVL - [3:1] */
+#define WM8996_MICB1_DISCH 0x0001 /* MICB1_DISCH */
+#define WM8996_MICB1_DISCH_MASK 0x0001 /* MICB1_DISCH */
+#define WM8996_MICB1_DISCH_SHIFT 0 /* MICB1_DISCH */
+#define WM8996_MICB1_DISCH_WIDTH 1 /* MICB1_DISCH */
+
+/*
+ * R33 (0x21) - MICBIAS (2)
+ */
+#define WM8996_MICB2_RATE 0x0020 /* MICB2_RATE */
+#define WM8996_MICB2_RATE_MASK 0x0020 /* MICB2_RATE */
+#define WM8996_MICB2_RATE_SHIFT 5 /* MICB2_RATE */
+#define WM8996_MICB2_RATE_WIDTH 1 /* MICB2_RATE */
+#define WM8996_MICB2_MODE 0x0010 /* MICB2_MODE */
+#define WM8996_MICB2_MODE_MASK 0x0010 /* MICB2_MODE */
+#define WM8996_MICB2_MODE_SHIFT 4 /* MICB2_MODE */
+#define WM8996_MICB2_MODE_WIDTH 1 /* MICB2_MODE */
+#define WM8996_MICB2_LVL_MASK 0x000E /* MICB2_LVL - [3:1] */
+#define WM8996_MICB2_LVL_SHIFT 1 /* MICB2_LVL - [3:1] */
+#define WM8996_MICB2_LVL_WIDTH 3 /* MICB2_LVL - [3:1] */
+#define WM8996_MICB2_DISCH 0x0001 /* MICB2_DISCH */
+#define WM8996_MICB2_DISCH_MASK 0x0001 /* MICB2_DISCH */
+#define WM8996_MICB2_DISCH_SHIFT 0 /* MICB2_DISCH */
+#define WM8996_MICB2_DISCH_WIDTH 1 /* MICB2_DISCH */
+
+/*
+ * R40 (0x28) - LDO 1
+ */
+#define WM8996_LDO1_MODE 0x0020 /* LDO1_MODE */
+#define WM8996_LDO1_MODE_MASK 0x0020 /* LDO1_MODE */
+#define WM8996_LDO1_MODE_SHIFT 5 /* LDO1_MODE */
+#define WM8996_LDO1_MODE_WIDTH 1 /* LDO1_MODE */
+#define WM8996_LDO1_VSEL_MASK 0x0006 /* LDO1_VSEL - [2:1] */
+#define WM8996_LDO1_VSEL_SHIFT 1 /* LDO1_VSEL - [2:1] */
+#define WM8996_LDO1_VSEL_WIDTH 2 /* LDO1_VSEL - [2:1] */
+#define WM8996_LDO1_DISCH 0x0001 /* LDO1_DISCH */
+#define WM8996_LDO1_DISCH_MASK 0x0001 /* LDO1_DISCH */
+#define WM8996_LDO1_DISCH_SHIFT 0 /* LDO1_DISCH */
+#define WM8996_LDO1_DISCH_WIDTH 1 /* LDO1_DISCH */
+
+/*
+ * R41 (0x29) - LDO 2
+ */
+#define WM8996_LDO2_MODE 0x0020 /* LDO2_MODE */
+#define WM8996_LDO2_MODE_MASK 0x0020 /* LDO2_MODE */
+#define WM8996_LDO2_MODE_SHIFT 5 /* LDO2_MODE */
+#define WM8996_LDO2_MODE_WIDTH 1 /* LDO2_MODE */
+#define WM8996_LDO2_VSEL_MASK 0x001E /* LDO2_VSEL - [4:1] */
+#define WM8996_LDO2_VSEL_SHIFT 1 /* LDO2_VSEL - [4:1] */
+#define WM8996_LDO2_VSEL_WIDTH 4 /* LDO2_VSEL - [4:1] */
+#define WM8996_LDO2_DISCH 0x0001 /* LDO2_DISCH */
+#define WM8996_LDO2_DISCH_MASK 0x0001 /* LDO2_DISCH */
+#define WM8996_LDO2_DISCH_SHIFT 0 /* LDO2_DISCH */
+#define WM8996_LDO2_DISCH_WIDTH 1 /* LDO2_DISCH */
+
+/*
+ * R48 (0x30) - Accessory Detect Mode 1
+ */
+#define WM8996_JD_MODE_MASK 0x0003 /* JD_MODE - [1:0] */
+#define WM8996_JD_MODE_SHIFT 0 /* JD_MODE - [1:0] */
+#define WM8996_JD_MODE_WIDTH 2 /* JD_MODE - [1:0] */
+
+/*
+ * R49 (0x31) - Accessory Detect Mode 2
+ */
+#define WM8996_HPOUT1FB_SRC 0x0004 /* HPOUT1FB_SRC */
+#define WM8996_HPOUT1FB_SRC_MASK 0x0004 /* HPOUT1FB_SRC */
+#define WM8996_HPOUT1FB_SRC_SHIFT 2 /* HPOUT1FB_SRC */
+#define WM8996_HPOUT1FB_SRC_WIDTH 1 /* HPOUT1FB_SRC */
+#define WM8996_MICD_SRC 0x0002 /* MICD_SRC */
+#define WM8996_MICD_SRC_MASK 0x0002 /* MICD_SRC */
+#define WM8996_MICD_SRC_SHIFT 1 /* MICD_SRC */
+#define WM8996_MICD_SRC_WIDTH 1 /* MICD_SRC */
+#define WM8996_MICD_BIAS_SRC 0x0001 /* MICD_BIAS_SRC */
+#define WM8996_MICD_BIAS_SRC_MASK 0x0001 /* MICD_BIAS_SRC */
+#define WM8996_MICD_BIAS_SRC_SHIFT 0 /* MICD_BIAS_SRC */
+#define WM8996_MICD_BIAS_SRC_WIDTH 1 /* MICD_BIAS_SRC */
+
+/*
+ * R52 (0x34) - Headphone Detect 1
+ */
+#define WM8996_HP_HOLDTIME_MASK 0x00E0 /* HP_HOLDTIME - [7:5] */
+#define WM8996_HP_HOLDTIME_SHIFT 5 /* HP_HOLDTIME - [7:5] */
+#define WM8996_HP_HOLDTIME_WIDTH 3 /* HP_HOLDTIME - [7:5] */
+#define WM8996_HP_CLK_DIV_MASK 0x0018 /* HP_CLK_DIV - [4:3] */
+#define WM8996_HP_CLK_DIV_SHIFT 3 /* HP_CLK_DIV - [4:3] */
+#define WM8996_HP_CLK_DIV_WIDTH 2 /* HP_CLK_DIV - [4:3] */
+#define WM8996_HP_STEP_SIZE 0x0002 /* HP_STEP_SIZE */
+#define WM8996_HP_STEP_SIZE_MASK 0x0002 /* HP_STEP_SIZE */
+#define WM8996_HP_STEP_SIZE_SHIFT 1 /* HP_STEP_SIZE */
+#define WM8996_HP_STEP_SIZE_WIDTH 1 /* HP_STEP_SIZE */
+#define WM8996_HP_POLL 0x0001 /* HP_POLL */
+#define WM8996_HP_POLL_MASK 0x0001 /* HP_POLL */
+#define WM8996_HP_POLL_SHIFT 0 /* HP_POLL */
+#define WM8996_HP_POLL_WIDTH 1 /* HP_POLL */
+
+/*
+ * R53 (0x35) - Headphone Detect 2
+ */
+#define WM8996_HP_DONE 0x0080 /* HP_DONE */
+#define WM8996_HP_DONE_MASK 0x0080 /* HP_DONE */
+#define WM8996_HP_DONE_SHIFT 7 /* HP_DONE */
+#define WM8996_HP_DONE_WIDTH 1 /* HP_DONE */
+#define WM8996_HP_LVL_MASK 0x007F /* HP_LVL - [6:0] */
+#define WM8996_HP_LVL_SHIFT 0 /* HP_LVL - [6:0] */
+#define WM8996_HP_LVL_WIDTH 7 /* HP_LVL - [6:0] */
+
+/*
+ * R56 (0x38) - Mic Detect 1
+ */
+#define WM8996_MICD_BIAS_STARTTIME_MASK 0xF000 /* MICD_BIAS_STARTTIME - [15:12] */
+#define WM8996_MICD_BIAS_STARTTIME_SHIFT 12 /* MICD_BIAS_STARTTIME - [15:12] */
+#define WM8996_MICD_BIAS_STARTTIME_WIDTH 4 /* MICD_BIAS_STARTTIME - [15:12] */
+#define WM8996_MICD_RATE_MASK 0x0F00 /* MICD_RATE - [11:8] */
+#define WM8996_MICD_RATE_SHIFT 8 /* MICD_RATE - [11:8] */
+#define WM8996_MICD_RATE_WIDTH 4 /* MICD_RATE - [11:8] */
+#define WM8996_MICD_DBTIME 0x0002 /* MICD_DBTIME */
+#define WM8996_MICD_DBTIME_MASK 0x0002 /* MICD_DBTIME */
+#define WM8996_MICD_DBTIME_SHIFT 1 /* MICD_DBTIME */
+#define WM8996_MICD_DBTIME_WIDTH 1 /* MICD_DBTIME */
+#define WM8996_MICD_ENA 0x0001 /* MICD_ENA */
+#define WM8996_MICD_ENA_MASK 0x0001 /* MICD_ENA */
+#define WM8996_MICD_ENA_SHIFT 0 /* MICD_ENA */
+#define WM8996_MICD_ENA_WIDTH 1 /* MICD_ENA */
+
+/*
+ * R57 (0x39) - Mic Detect 2
+ */
+#define WM8996_MICD_LVL_SEL_MASK 0x00FF /* MICD_LVL_SEL - [7:0] */
+#define WM8996_MICD_LVL_SEL_SHIFT 0 /* MICD_LVL_SEL - [7:0] */
+#define WM8996_MICD_LVL_SEL_WIDTH 8 /* MICD_LVL_SEL - [7:0] */
+
+/*
+ * R58 (0x3A) - Mic Detect 3
+ */
+#define WM8996_MICD_LVL_MASK 0x07FC /* MICD_LVL - [10:2] */
+#define WM8996_MICD_LVL_SHIFT 2 /* MICD_LVL - [10:2] */
+#define WM8996_MICD_LVL_WIDTH 9 /* MICD_LVL - [10:2] */
+#define WM8996_MICD_VALID 0x0002 /* MICD_VALID */
+#define WM8996_MICD_VALID_MASK 0x0002 /* MICD_VALID */
+#define WM8996_MICD_VALID_SHIFT 1 /* MICD_VALID */
+#define WM8996_MICD_VALID_WIDTH 1 /* MICD_VALID */
+#define WM8996_MICD_STS 0x0001 /* MICD_STS */
+#define WM8996_MICD_STS_MASK 0x0001 /* MICD_STS */
+#define WM8996_MICD_STS_SHIFT 0 /* MICD_STS */
+#define WM8996_MICD_STS_WIDTH 1 /* MICD_STS */
+
+/*
+ * R64 (0x40) - Charge Pump (1)
+ */
+#define WM8996_CP_ENA 0x8000 /* CP_ENA */
+#define WM8996_CP_ENA_MASK 0x8000 /* CP_ENA */
+#define WM8996_CP_ENA_SHIFT 15 /* CP_ENA */
+#define WM8996_CP_ENA_WIDTH 1 /* CP_ENA */
+
+/*
+ * R65 (0x41) - Charge Pump (2)
+ */
+#define WM8996_CP_DISCH 0x8000 /* CP_DISCH */
+#define WM8996_CP_DISCH_MASK 0x8000 /* CP_DISCH */
+#define WM8996_CP_DISCH_SHIFT 15 /* CP_DISCH */
+#define WM8996_CP_DISCH_WIDTH 1 /* CP_DISCH */
+
+/*
+ * R80 (0x50) - DC Servo (1)
+ */
+#define WM8996_DCS_ENA_CHAN_3 0x0008 /* DCS_ENA_CHAN_3 */
+#define WM8996_DCS_ENA_CHAN_3_MASK 0x0008 /* DCS_ENA_CHAN_3 */
+#define WM8996_DCS_ENA_CHAN_3_SHIFT 3 /* DCS_ENA_CHAN_3 */
+#define WM8996_DCS_ENA_CHAN_3_WIDTH 1 /* DCS_ENA_CHAN_3 */
+#define WM8996_DCS_ENA_CHAN_2 0x0004 /* DCS_ENA_CHAN_2 */
+#define WM8996_DCS_ENA_CHAN_2_MASK 0x0004 /* DCS_ENA_CHAN_2 */
+#define WM8996_DCS_ENA_CHAN_2_SHIFT 2 /* DCS_ENA_CHAN_2 */
+#define WM8996_DCS_ENA_CHAN_2_WIDTH 1 /* DCS_ENA_CHAN_2 */
+#define WM8996_DCS_ENA_CHAN_1 0x0002 /* DCS_ENA_CHAN_1 */
+#define WM8996_DCS_ENA_CHAN_1_MASK 0x0002 /* DCS_ENA_CHAN_1 */
+#define WM8996_DCS_ENA_CHAN_1_SHIFT 1 /* DCS_ENA_CHAN_1 */
+#define WM8996_DCS_ENA_CHAN_1_WIDTH 1 /* DCS_ENA_CHAN_1 */
+#define WM8996_DCS_ENA_CHAN_0 0x0001 /* DCS_ENA_CHAN_0 */
+#define WM8996_DCS_ENA_CHAN_0_MASK 0x0001 /* DCS_ENA_CHAN_0 */
+#define WM8996_DCS_ENA_CHAN_0_SHIFT 0 /* DCS_ENA_CHAN_0 */
+#define WM8996_DCS_ENA_CHAN_0_WIDTH 1 /* DCS_ENA_CHAN_0 */
+
+/*
+ * R81 (0x51) - DC Servo (2)
+ */
+#define WM8996_DCS_TRIG_SINGLE_3 0x8000 /* DCS_TRIG_SINGLE_3 */
+#define WM8996_DCS_TRIG_SINGLE_3_MASK 0x8000 /* DCS_TRIG_SINGLE_3 */
+#define WM8996_DCS_TRIG_SINGLE_3_SHIFT 15 /* DCS_TRIG_SINGLE_3 */
+#define WM8996_DCS_TRIG_SINGLE_3_WIDTH 1 /* DCS_TRIG_SINGLE_3 */
+#define WM8996_DCS_TRIG_SINGLE_2 0x4000 /* DCS_TRIG_SINGLE_2 */
+#define WM8996_DCS_TRIG_SINGLE_2_MASK 0x4000 /* DCS_TRIG_SINGLE_2 */
+#define WM8996_DCS_TRIG_SINGLE_2_SHIFT 14 /* DCS_TRIG_SINGLE_2 */
+#define WM8996_DCS_TRIG_SINGLE_2_WIDTH 1 /* DCS_TRIG_SINGLE_2 */
+#define WM8996_DCS_TRIG_SINGLE_1 0x2000 /* DCS_TRIG_SINGLE_1 */
+#define WM8996_DCS_TRIG_SINGLE_1_MASK 0x2000 /* DCS_TRIG_SINGLE_1 */
+#define WM8996_DCS_TRIG_SINGLE_1_SHIFT 13 /* DCS_TRIG_SINGLE_1 */
+#define WM8996_DCS_TRIG_SINGLE_1_WIDTH 1 /* DCS_TRIG_SINGLE_1 */
+#define WM8996_DCS_TRIG_SINGLE_0 0x1000 /* DCS_TRIG_SINGLE_0 */
+#define WM8996_DCS_TRIG_SINGLE_0_MASK 0x1000 /* DCS_TRIG_SINGLE_0 */
+#define WM8996_DCS_TRIG_SINGLE_0_SHIFT 12 /* DCS_TRIG_SINGLE_0 */
+#define WM8996_DCS_TRIG_SINGLE_0_WIDTH 1 /* DCS_TRIG_SINGLE_0 */
+#define WM8996_DCS_TRIG_SERIES_3 0x0800 /* DCS_TRIG_SERIES_3 */
+#define WM8996_DCS_TRIG_SERIES_3_MASK 0x0800 /* DCS_TRIG_SERIES_3 */
+#define WM8996_DCS_TRIG_SERIES_3_SHIFT 11 /* DCS_TRIG_SERIES_3 */
+#define WM8996_DCS_TRIG_SERIES_3_WIDTH 1 /* DCS_TRIG_SERIES_3 */
+#define WM8996_DCS_TRIG_SERIES_2 0x0400 /* DCS_TRIG_SERIES_2 */
+#define WM8996_DCS_TRIG_SERIES_2_MASK 0x0400 /* DCS_TRIG_SERIES_2 */
+#define WM8996_DCS_TRIG_SERIES_2_SHIFT 10 /* DCS_TRIG_SERIES_2 */
+#define WM8996_DCS_TRIG_SERIES_2_WIDTH 1 /* DCS_TRIG_SERIES_2 */
+#define WM8996_DCS_TRIG_SERIES_1 0x0200 /* DCS_TRIG_SERIES_1 */
+#define WM8996_DCS_TRIG_SERIES_1_MASK 0x0200 /* DCS_TRIG_SERIES_1 */
+#define WM8996_DCS_TRIG_SERIES_1_SHIFT 9 /* DCS_TRIG_SERIES_1 */
+#define WM8996_DCS_TRIG_SERIES_1_WIDTH 1 /* DCS_TRIG_SERIES_1 */
+#define WM8996_DCS_TRIG_SERIES_0 0x0100 /* DCS_TRIG_SERIES_0 */
+#define WM8996_DCS_TRIG_SERIES_0_MASK 0x0100 /* DCS_TRIG_SERIES_0 */
+#define WM8996_DCS_TRIG_SERIES_0_SHIFT 8 /* DCS_TRIG_SERIES_0 */
+#define WM8996_DCS_TRIG_SERIES_0_WIDTH 1 /* DCS_TRIG_SERIES_0 */
+#define WM8996_DCS_TRIG_STARTUP_3 0x0080 /* DCS_TRIG_STARTUP_3 */
+#define WM8996_DCS_TRIG_STARTUP_3_MASK 0x0080 /* DCS_TRIG_STARTUP_3 */
+#define WM8996_DCS_TRIG_STARTUP_3_SHIFT 7 /* DCS_TRIG_STARTUP_3 */
+#define WM8996_DCS_TRIG_STARTUP_3_WIDTH 1 /* DCS_TRIG_STARTUP_3 */
+#define WM8996_DCS_TRIG_STARTUP_2 0x0040 /* DCS_TRIG_STARTUP_2 */
+#define WM8996_DCS_TRIG_STARTUP_2_MASK 0x0040 /* DCS_TRIG_STARTUP_2 */
+#define WM8996_DCS_TRIG_STARTUP_2_SHIFT 6 /* DCS_TRIG_STARTUP_2 */
+#define WM8996_DCS_TRIG_STARTUP_2_WIDTH 1 /* DCS_TRIG_STARTUP_2 */
+#define WM8996_DCS_TRIG_STARTUP_1 0x0020 /* DCS_TRIG_STARTUP_1 */
+#define WM8996_DCS_TRIG_STARTUP_1_MASK 0x0020 /* DCS_TRIG_STARTUP_1 */
+#define WM8996_DCS_TRIG_STARTUP_1_SHIFT 5 /* DCS_TRIG_STARTUP_1 */
+#define WM8996_DCS_TRIG_STARTUP_1_WIDTH 1 /* DCS_TRIG_STARTUP_1 */
+#define WM8996_DCS_TRIG_STARTUP_0 0x0010 /* DCS_TRIG_STARTUP_0 */
+#define WM8996_DCS_TRIG_STARTUP_0_MASK 0x0010 /* DCS_TRIG_STARTUP_0 */
+#define WM8996_DCS_TRIG_STARTUP_0_SHIFT 4 /* DCS_TRIG_STARTUP_0 */
+#define WM8996_DCS_TRIG_STARTUP_0_WIDTH 1 /* DCS_TRIG_STARTUP_0 */
+#define WM8996_DCS_TRIG_DAC_WR_3 0x0008 /* DCS_TRIG_DAC_WR_3 */
+#define WM8996_DCS_TRIG_DAC_WR_3_MASK 0x0008 /* DCS_TRIG_DAC_WR_3 */
+#define WM8996_DCS_TRIG_DAC_WR_3_SHIFT 3 /* DCS_TRIG_DAC_WR_3 */
+#define WM8996_DCS_TRIG_DAC_WR_3_WIDTH 1 /* DCS_TRIG_DAC_WR_3 */
+#define WM8996_DCS_TRIG_DAC_WR_2 0x0004 /* DCS_TRIG_DAC_WR_2 */
+#define WM8996_DCS_TRIG_DAC_WR_2_MASK 0x0004 /* DCS_TRIG_DAC_WR_2 */
+#define WM8996_DCS_TRIG_DAC_WR_2_SHIFT 2 /* DCS_TRIG_DAC_WR_2 */
+#define WM8996_DCS_TRIG_DAC_WR_2_WIDTH 1 /* DCS_TRIG_DAC_WR_2 */
+#define WM8996_DCS_TRIG_DAC_WR_1 0x0002 /* DCS_TRIG_DAC_WR_1 */
+#define WM8996_DCS_TRIG_DAC_WR_1_MASK 0x0002 /* DCS_TRIG_DAC_WR_1 */
+#define WM8996_DCS_TRIG_DAC_WR_1_SHIFT 1 /* DCS_TRIG_DAC_WR_1 */
+#define WM8996_DCS_TRIG_DAC_WR_1_WIDTH 1 /* DCS_TRIG_DAC_WR_1 */
+#define WM8996_DCS_TRIG_DAC_WR_0 0x0001 /* DCS_TRIG_DAC_WR_0 */
+#define WM8996_DCS_TRIG_DAC_WR_0_MASK 0x0001 /* DCS_TRIG_DAC_WR_0 */
+#define WM8996_DCS_TRIG_DAC_WR_0_SHIFT 0 /* DCS_TRIG_DAC_WR_0 */
+#define WM8996_DCS_TRIG_DAC_WR_0_WIDTH 1 /* DCS_TRIG_DAC_WR_0 */
+
+/*
+ * R82 (0x52) - DC Servo (3)
+ */
+#define WM8996_DCS_TIMER_PERIOD_23_MASK 0x0F00 /* DCS_TIMER_PERIOD_23 - [11:8] */
+#define WM8996_DCS_TIMER_PERIOD_23_SHIFT 8 /* DCS_TIMER_PERIOD_23 - [11:8] */
+#define WM8996_DCS_TIMER_PERIOD_23_WIDTH 4 /* DCS_TIMER_PERIOD_23 - [11:8] */
+#define WM8996_DCS_TIMER_PERIOD_01_MASK 0x000F /* DCS_TIMER_PERIOD_01 - [3:0] */
+#define WM8996_DCS_TIMER_PERIOD_01_SHIFT 0 /* DCS_TIMER_PERIOD_01 - [3:0] */
+#define WM8996_DCS_TIMER_PERIOD_01_WIDTH 4 /* DCS_TIMER_PERIOD_01 - [3:0] */
+
+/*
+ * R84 (0x54) - DC Servo (5)
+ */
+#define WM8996_DCS_SERIES_NO_23_MASK 0x7F00 /* DCS_SERIES_NO_23 - [14:8] */
+#define WM8996_DCS_SERIES_NO_23_SHIFT 8 /* DCS_SERIES_NO_23 - [14:8] */
+#define WM8996_DCS_SERIES_NO_23_WIDTH 7 /* DCS_SERIES_NO_23 - [14:8] */
+#define WM8996_DCS_SERIES_NO_01_MASK 0x007F /* DCS_SERIES_NO_01 - [6:0] */
+#define WM8996_DCS_SERIES_NO_01_SHIFT 0 /* DCS_SERIES_NO_01 - [6:0] */
+#define WM8996_DCS_SERIES_NO_01_WIDTH 7 /* DCS_SERIES_NO_01 - [6:0] */
+
+/*
+ * R85 (0x55) - DC Servo (6)
+ */
+#define WM8996_DCS_DAC_WR_VAL_3_MASK 0xFF00 /* DCS_DAC_WR_VAL_3 - [15:8] */
+#define WM8996_DCS_DAC_WR_VAL_3_SHIFT 8 /* DCS_DAC_WR_VAL_3 - [15:8] */
+#define WM8996_DCS_DAC_WR_VAL_3_WIDTH 8 /* DCS_DAC_WR_VAL_3 - [15:8] */
+#define WM8996_DCS_DAC_WR_VAL_2_MASK 0x00FF /* DCS_DAC_WR_VAL_2 - [7:0] */
+#define WM8996_DCS_DAC_WR_VAL_2_SHIFT 0 /* DCS_DAC_WR_VAL_2 - [7:0] */
+#define WM8996_DCS_DAC_WR_VAL_2_WIDTH 8 /* DCS_DAC_WR_VAL_2 - [7:0] */
+
+/*
+ * R86 (0x56) - DC Servo (7)
+ */
+#define WM8996_DCS_DAC_WR_VAL_1_MASK 0xFF00 /* DCS_DAC_WR_VAL_1 - [15:8] */
+#define WM8996_DCS_DAC_WR_VAL_1_SHIFT 8 /* DCS_DAC_WR_VAL_1 - [15:8] */
+#define WM8996_DCS_DAC_WR_VAL_1_WIDTH 8 /* DCS_DAC_WR_VAL_1 - [15:8] */
+#define WM8996_DCS_DAC_WR_VAL_0_MASK 0x00FF /* DCS_DAC_WR_VAL_0 - [7:0] */
+#define WM8996_DCS_DAC_WR_VAL_0_SHIFT 0 /* DCS_DAC_WR_VAL_0 - [7:0] */
+#define WM8996_DCS_DAC_WR_VAL_0_WIDTH 8 /* DCS_DAC_WR_VAL_0 - [7:0] */
+
+/*
+ * R87 (0x57) - DC Servo Readback 0
+ */
+#define WM8996_DCS_CAL_COMPLETE_MASK 0x0F00 /* DCS_CAL_COMPLETE - [11:8] */
+#define WM8996_DCS_CAL_COMPLETE_SHIFT 8 /* DCS_CAL_COMPLETE - [11:8] */
+#define WM8996_DCS_CAL_COMPLETE_WIDTH 4 /* DCS_CAL_COMPLETE - [11:8] */
+#define WM8996_DCS_DAC_WR_COMPLETE_MASK 0x00F0 /* DCS_DAC_WR_COMPLETE - [7:4] */
+#define WM8996_DCS_DAC_WR_COMPLETE_SHIFT 4 /* DCS_DAC_WR_COMPLETE - [7:4] */
+#define WM8996_DCS_DAC_WR_COMPLETE_WIDTH 4 /* DCS_DAC_WR_COMPLETE - [7:4] */
+#define WM8996_DCS_STARTUP_COMPLETE_MASK 0x000F /* DCS_STARTUP_COMPLETE - [3:0] */
+#define WM8996_DCS_STARTUP_COMPLETE_SHIFT 0 /* DCS_STARTUP_COMPLETE - [3:0] */
+#define WM8996_DCS_STARTUP_COMPLETE_WIDTH 4 /* DCS_STARTUP_COMPLETE - [3:0] */
+
+/*
+ * R96 (0x60) - Analogue HP (1)
+ */
+#define WM8996_HPOUT1L_RMV_SHORT 0x0080 /* HPOUT1L_RMV_SHORT */
+#define WM8996_HPOUT1L_RMV_SHORT_MASK 0x0080 /* HPOUT1L_RMV_SHORT */
+#define WM8996_HPOUT1L_RMV_SHORT_SHIFT 7 /* HPOUT1L_RMV_SHORT */
+#define WM8996_HPOUT1L_RMV_SHORT_WIDTH 1 /* HPOUT1L_RMV_SHORT */
+#define WM8996_HPOUT1L_OUTP 0x0040 /* HPOUT1L_OUTP */
+#define WM8996_HPOUT1L_OUTP_MASK 0x0040 /* HPOUT1L_OUTP */
+#define WM8996_HPOUT1L_OUTP_SHIFT 6 /* HPOUT1L_OUTP */
+#define WM8996_HPOUT1L_OUTP_WIDTH 1 /* HPOUT1L_OUTP */
+#define WM8996_HPOUT1L_DLY 0x0020 /* HPOUT1L_DLY */
+#define WM8996_HPOUT1L_DLY_MASK 0x0020 /* HPOUT1L_DLY */
+#define WM8996_HPOUT1L_DLY_SHIFT 5 /* HPOUT1L_DLY */
+#define WM8996_HPOUT1L_DLY_WIDTH 1 /* HPOUT1L_DLY */
+#define WM8996_HPOUT1R_RMV_SHORT 0x0008 /* HPOUT1R_RMV_SHORT */
+#define WM8996_HPOUT1R_RMV_SHORT_MASK 0x0008 /* HPOUT1R_RMV_SHORT */
+#define WM8996_HPOUT1R_RMV_SHORT_SHIFT 3 /* HPOUT1R_RMV_SHORT */
+#define WM8996_HPOUT1R_RMV_SHORT_WIDTH 1 /* HPOUT1R_RMV_SHORT */
+#define WM8996_HPOUT1R_OUTP 0x0004 /* HPOUT1R_OUTP */
+#define WM8996_HPOUT1R_OUTP_MASK 0x0004 /* HPOUT1R_OUTP */
+#define WM8996_HPOUT1R_OUTP_SHIFT 2 /* HPOUT1R_OUTP */
+#define WM8996_HPOUT1R_OUTP_WIDTH 1 /* HPOUT1R_OUTP */
+#define WM8996_HPOUT1R_DLY 0x0002 /* HPOUT1R_DLY */
+#define WM8996_HPOUT1R_DLY_MASK 0x0002 /* HPOUT1R_DLY */
+#define WM8996_HPOUT1R_DLY_SHIFT 1 /* HPOUT1R_DLY */
+#define WM8996_HPOUT1R_DLY_WIDTH 1 /* HPOUT1R_DLY */
+
+/*
+ * R97 (0x61) - Analogue HP (2)
+ */
+#define WM8996_HPOUT2L_RMV_SHORT 0x0080 /* HPOUT2L_RMV_SHORT */
+#define WM8996_HPOUT2L_RMV_SHORT_MASK 0x0080 /* HPOUT2L_RMV_SHORT */
+#define WM8996_HPOUT2L_RMV_SHORT_SHIFT 7 /* HPOUT2L_RMV_SHORT */
+#define WM8996_HPOUT2L_RMV_SHORT_WIDTH 1 /* HPOUT2L_RMV_SHORT */
+#define WM8996_HPOUT2L_OUTP 0x0040 /* HPOUT2L_OUTP */
+#define WM8996_HPOUT2L_OUTP_MASK 0x0040 /* HPOUT2L_OUTP */
+#define WM8996_HPOUT2L_OUTP_SHIFT 6 /* HPOUT2L_OUTP */
+#define WM8996_HPOUT2L_OUTP_WIDTH 1 /* HPOUT2L_OUTP */
+#define WM8996_HPOUT2L_DLY 0x0020 /* HPOUT2L_DLY */
+#define WM8996_HPOUT2L_DLY_MASK 0x0020 /* HPOUT2L_DLY */
+#define WM8996_HPOUT2L_DLY_SHIFT 5 /* HPOUT2L_DLY */
+#define WM8996_HPOUT2L_DLY_WIDTH 1 /* HPOUT2L_DLY */
+#define WM8996_HPOUT2R_RMV_SHORT 0x0008 /* HPOUT2R_RMV_SHORT */
+#define WM8996_HPOUT2R_RMV_SHORT_MASK 0x0008 /* HPOUT2R_RMV_SHORT */
+#define WM8996_HPOUT2R_RMV_SHORT_SHIFT 3 /* HPOUT2R_RMV_SHORT */
+#define WM8996_HPOUT2R_RMV_SHORT_WIDTH 1 /* HPOUT2R_RMV_SHORT */
+#define WM8996_HPOUT2R_OUTP 0x0004 /* HPOUT2R_OUTP */
+#define WM8996_HPOUT2R_OUTP_MASK 0x0004 /* HPOUT2R_OUTP */
+#define WM8996_HPOUT2R_OUTP_SHIFT 2 /* HPOUT2R_OUTP */
+#define WM8996_HPOUT2R_OUTP_WIDTH 1 /* HPOUT2R_OUTP */
+#define WM8996_HPOUT2R_DLY 0x0002 /* HPOUT2R_DLY */
+#define WM8996_HPOUT2R_DLY_MASK 0x0002 /* HPOUT2R_DLY */
+#define WM8996_HPOUT2R_DLY_SHIFT 1 /* HPOUT2R_DLY */
+#define WM8996_HPOUT2R_DLY_WIDTH 1 /* HPOUT2R_DLY */
+
+/*
+ * R256 (0x100) - Chip Revision
+ */
+#define WM8996_CHIP_REV_MASK 0x000F /* CHIP_REV - [3:0] */
+#define WM8996_CHIP_REV_SHIFT 0 /* CHIP_REV - [3:0] */
+#define WM8996_CHIP_REV_WIDTH 4 /* CHIP_REV - [3:0] */
+
+/*
+ * R257 (0x101) - Control Interface (1)
+ */
+#define WM8996_AUTO_INC 0x0004 /* AUTO_INC */
+#define WM8996_AUTO_INC_MASK 0x0004 /* AUTO_INC */
+#define WM8996_AUTO_INC_SHIFT 2 /* AUTO_INC */
+#define WM8996_AUTO_INC_WIDTH 1 /* AUTO_INC */
+
+/*
+ * R272 (0x110) - Write Sequencer Ctrl (1)
+ */
+#define WM8996_WSEQ_ENA 0x8000 /* WSEQ_ENA */
+#define WM8996_WSEQ_ENA_MASK 0x8000 /* WSEQ_ENA */
+#define WM8996_WSEQ_ENA_SHIFT 15 /* WSEQ_ENA */
+#define WM8996_WSEQ_ENA_WIDTH 1 /* WSEQ_ENA */
+#define WM8996_WSEQ_ABORT 0x0200 /* WSEQ_ABORT */
+#define WM8996_WSEQ_ABORT_MASK 0x0200 /* WSEQ_ABORT */
+#define WM8996_WSEQ_ABORT_SHIFT 9 /* WSEQ_ABORT */
+#define WM8996_WSEQ_ABORT_WIDTH 1 /* WSEQ_ABORT */
+#define WM8996_WSEQ_START 0x0100 /* WSEQ_START */
+#define WM8996_WSEQ_START_MASK 0x0100 /* WSEQ_START */
+#define WM8996_WSEQ_START_SHIFT 8 /* WSEQ_START */
+#define WM8996_WSEQ_START_WIDTH 1 /* WSEQ_START */
+#define WM8996_WSEQ_START_INDEX_MASK 0x007F /* WSEQ_START_INDEX - [6:0] */
+#define WM8996_WSEQ_START_INDEX_SHIFT 0 /* WSEQ_START_INDEX - [6:0] */
+#define WM8996_WSEQ_START_INDEX_WIDTH 7 /* WSEQ_START_INDEX - [6:0] */
+
+/*
+ * R273 (0x111) - Write Sequencer Ctrl (2)
+ */
+#define WM8996_WSEQ_BUSY 0x0100 /* WSEQ_BUSY */
+#define WM8996_WSEQ_BUSY_MASK 0x0100 /* WSEQ_BUSY */
+#define WM8996_WSEQ_BUSY_SHIFT 8 /* WSEQ_BUSY */
+#define WM8996_WSEQ_BUSY_WIDTH 1 /* WSEQ_BUSY */
+#define WM8996_WSEQ_CURRENT_INDEX_MASK 0x007F /* WSEQ_CURRENT_INDEX - [6:0] */
+#define WM8996_WSEQ_CURRENT_INDEX_SHIFT 0 /* WSEQ_CURRENT_INDEX - [6:0] */
+#define WM8996_WSEQ_CURRENT_INDEX_WIDTH 7 /* WSEQ_CURRENT_INDEX - [6:0] */
+
+/*
+ * R512 (0x200) - AIF Clocking (1)
+ */
+#define WM8996_SYSCLK_SRC_MASK 0x0018 /* SYSCLK_SRC - [4:3] */
+#define WM8996_SYSCLK_SRC_SHIFT 3 /* SYSCLK_SRC - [4:3] */
+#define WM8996_SYSCLK_SRC_WIDTH 2 /* SYSCLK_SRC - [4:3] */
+#define WM8996_SYSCLK_INV 0x0004 /* SYSCLK_INV */
+#define WM8996_SYSCLK_INV_MASK 0x0004 /* SYSCLK_INV */
+#define WM8996_SYSCLK_INV_SHIFT 2 /* SYSCLK_INV */
+#define WM8996_SYSCLK_INV_WIDTH 1 /* SYSCLK_INV */
+#define WM8996_SYSCLK_DIV 0x0002 /* SYSCLK_DIV */
+#define WM8996_SYSCLK_DIV_MASK 0x0002 /* SYSCLK_DIV */
+#define WM8996_SYSCLK_DIV_SHIFT 1 /* SYSCLK_DIV */
+#define WM8996_SYSCLK_DIV_WIDTH 1 /* SYSCLK_DIV */
+#define WM8996_SYSCLK_ENA 0x0001 /* SYSCLK_ENA */
+#define WM8996_SYSCLK_ENA_MASK 0x0001 /* SYSCLK_ENA */
+#define WM8996_SYSCLK_ENA_SHIFT 0 /* SYSCLK_ENA */
+#define WM8996_SYSCLK_ENA_WIDTH 1 /* SYSCLK_ENA */
+
+/*
+ * R513 (0x201) - AIF Clocking (2)
+ */
+#define WM8996_DSP2_DIV_MASK 0x0018 /* DSP2_DIV - [4:3] */
+#define WM8996_DSP2_DIV_SHIFT 3 /* DSP2_DIV - [4:3] */
+#define WM8996_DSP2_DIV_WIDTH 2 /* DSP2_DIV - [4:3] */
+#define WM8996_DSP1_DIV_MASK 0x0003 /* DSP1_DIV - [1:0] */
+#define WM8996_DSP1_DIV_SHIFT 0 /* DSP1_DIV - [1:0] */
+#define WM8996_DSP1_DIV_WIDTH 2 /* DSP1_DIV - [1:0] */
+
+/*
+ * R520 (0x208) - Clocking (1)
+ */
+#define WM8996_LFCLK_ENA 0x0020 /* LFCLK_ENA */
+#define WM8996_LFCLK_ENA_MASK 0x0020 /* LFCLK_ENA */
+#define WM8996_LFCLK_ENA_SHIFT 5 /* LFCLK_ENA */
+#define WM8996_LFCLK_ENA_WIDTH 1 /* LFCLK_ENA */
+#define WM8996_TOCLK_ENA 0x0010 /* TOCLK_ENA */
+#define WM8996_TOCLK_ENA_MASK 0x0010 /* TOCLK_ENA */
+#define WM8996_TOCLK_ENA_SHIFT 4 /* TOCLK_ENA */
+#define WM8996_TOCLK_ENA_WIDTH 1 /* TOCLK_ENA */
+#define WM8996_AIFCLK_ENA 0x0004 /* AIFCLK_ENA */
+#define WM8996_AIFCLK_ENA_MASK 0x0004 /* AIFCLK_ENA */
+#define WM8996_AIFCLK_ENA_SHIFT 2 /* AIFCLK_ENA */
+#define WM8996_AIFCLK_ENA_WIDTH 1 /* AIFCLK_ENA */
+#define WM8996_SYSDSPCLK_ENA 0x0002 /* SYSDSPCLK_ENA */
+#define WM8996_SYSDSPCLK_ENA_MASK 0x0002 /* SYSDSPCLK_ENA */
+#define WM8996_SYSDSPCLK_ENA_SHIFT 1 /* SYSDSPCLK_ENA */
+#define WM8996_SYSDSPCLK_ENA_WIDTH 1 /* SYSDSPCLK_ENA */
+
+/*
+ * R521 (0x209) - Clocking (2)
+ */
+#define WM8996_TOCLK_DIV_MASK 0x0700 /* TOCLK_DIV - [10:8] */
+#define WM8996_TOCLK_DIV_SHIFT 8 /* TOCLK_DIV - [10:8] */
+#define WM8996_TOCLK_DIV_WIDTH 3 /* TOCLK_DIV - [10:8] */
+#define WM8996_DBCLK_DIV_MASK 0x00F0 /* DBCLK_DIV - [7:4] */
+#define WM8996_DBCLK_DIV_SHIFT 4 /* DBCLK_DIV - [7:4] */
+#define WM8996_DBCLK_DIV_WIDTH 4 /* DBCLK_DIV - [7:4] */
+#define WM8996_OPCLK_DIV_MASK 0x0007 /* OPCLK_DIV - [2:0] */
+#define WM8996_OPCLK_DIV_SHIFT 0 /* OPCLK_DIV - [2:0] */
+#define WM8996_OPCLK_DIV_WIDTH 3 /* OPCLK_DIV - [2:0] */
+
+/*
+ * R528 (0x210) - AIF Rate
+ */
+#define WM8996_SYSCLK_RATE 0x0001 /* SYSCLK_RATE */
+#define WM8996_SYSCLK_RATE_MASK 0x0001 /* SYSCLK_RATE */
+#define WM8996_SYSCLK_RATE_SHIFT 0 /* SYSCLK_RATE */
+#define WM8996_SYSCLK_RATE_WIDTH 1 /* SYSCLK_RATE */
+
+/*
+ * R544 (0x220) - FLL Control (1)
+ */
+#define WM8996_FLL_OSC_ENA 0x0002 /* FLL_OSC_ENA */
+#define WM8996_FLL_OSC_ENA_MASK 0x0002 /* FLL_OSC_ENA */
+#define WM8996_FLL_OSC_ENA_SHIFT 1 /* FLL_OSC_ENA */
+#define WM8996_FLL_OSC_ENA_WIDTH 1 /* FLL_OSC_ENA */
+#define WM8996_FLL_ENA 0x0001 /* FLL_ENA */
+#define WM8996_FLL_ENA_MASK 0x0001 /* FLL_ENA */
+#define WM8996_FLL_ENA_SHIFT 0 /* FLL_ENA */
+#define WM8996_FLL_ENA_WIDTH 1 /* FLL_ENA */
+
+/*
+ * R545 (0x221) - FLL Control (2)
+ */
+#define WM8996_FLL_OUTDIV_MASK 0x3F00 /* FLL_OUTDIV - [13:8] */
+#define WM8996_FLL_OUTDIV_SHIFT 8 /* FLL_OUTDIV - [13:8] */
+#define WM8996_FLL_OUTDIV_WIDTH 6 /* FLL_OUTDIV - [13:8] */
+#define WM8996_FLL_FRATIO_MASK 0x0007 /* FLL_FRATIO - [2:0] */
+#define WM8996_FLL_FRATIO_SHIFT 0 /* FLL_FRATIO - [2:0] */
+#define WM8996_FLL_FRATIO_WIDTH 3 /* FLL_FRATIO - [2:0] */
+
+/*
+ * R546 (0x222) - FLL Control (3)
+ */
+#define WM8996_FLL_THETA_MASK 0xFFFF /* FLL_THETA - [15:0] */
+#define WM8996_FLL_THETA_SHIFT 0 /* FLL_THETA - [15:0] */
+#define WM8996_FLL_THETA_WIDTH 16 /* FLL_THETA - [15:0] */
+
+/*
+ * R547 (0x223) - FLL Control (4)
+ */
+#define WM8996_FLL_N_MASK 0x7FE0 /* FLL_N - [14:5] */
+#define WM8996_FLL_N_SHIFT 5 /* FLL_N - [14:5] */
+#define WM8996_FLL_N_WIDTH 10 /* FLL_N - [14:5] */
+#define WM8996_FLL_LOOP_GAIN_MASK 0x000F /* FLL_LOOP_GAIN - [3:0] */
+#define WM8996_FLL_LOOP_GAIN_SHIFT 0 /* FLL_LOOP_GAIN - [3:0] */
+#define WM8996_FLL_LOOP_GAIN_WIDTH 4 /* FLL_LOOP_GAIN - [3:0] */
+
+/*
+ * R548 (0x224) - FLL Control (5)
+ */
+#define WM8996_FLL_FRC_NCO_VAL_MASK 0x1F80 /* FLL_FRC_NCO_VAL - [12:7] */
+#define WM8996_FLL_FRC_NCO_VAL_SHIFT 7 /* FLL_FRC_NCO_VAL - [12:7] */
+#define WM8996_FLL_FRC_NCO_VAL_WIDTH 6 /* FLL_FRC_NCO_VAL - [12:7] */
+#define WM8996_FLL_FRC_NCO 0x0040 /* FLL_FRC_NCO */
+#define WM8996_FLL_FRC_NCO_MASK 0x0040 /* FLL_FRC_NCO */
+#define WM8996_FLL_FRC_NCO_SHIFT 6 /* FLL_FRC_NCO */
+#define WM8996_FLL_FRC_NCO_WIDTH 1 /* FLL_FRC_NCO */
+#define WM8996_FLL_REFCLK_DIV_MASK 0x0018 /* FLL_REFCLK_DIV - [4:3] */
+#define WM8996_FLL_REFCLK_DIV_SHIFT 3 /* FLL_REFCLK_DIV - [4:3] */
+#define WM8996_FLL_REFCLK_DIV_WIDTH 2 /* FLL_REFCLK_DIV - [4:3] */
+#define WM8996_FLL_REF_FREQ 0x0004 /* FLL_REF_FREQ */
+#define WM8996_FLL_REF_FREQ_MASK 0x0004 /* FLL_REF_FREQ */
+#define WM8996_FLL_REF_FREQ_SHIFT 2 /* FLL_REF_FREQ */
+#define WM8996_FLL_REF_FREQ_WIDTH 1 /* FLL_REF_FREQ */
+#define WM8996_FLL_REFCLK_SRC_MASK 0x0003 /* FLL_REFCLK_SRC - [1:0] */
+#define WM8996_FLL_REFCLK_SRC_SHIFT 0 /* FLL_REFCLK_SRC - [1:0] */
+#define WM8996_FLL_REFCLK_SRC_WIDTH 2 /* FLL_REFCLK_SRC - [1:0] */
+
+/*
+ * R549 (0x225) - FLL Control (6)
+ */
+#define WM8996_FLL_REFCLK_SRC_STS_MASK 0x000C /* FLL_REFCLK_SRC_STS - [3:2] */
+#define WM8996_FLL_REFCLK_SRC_STS_SHIFT 2 /* FLL_REFCLK_SRC_STS - [3:2] */
+#define WM8996_FLL_REFCLK_SRC_STS_WIDTH 2 /* FLL_REFCLK_SRC_STS - [3:2] */
+#define WM8996_FLL_SWITCH_CLK 0x0001 /* FLL_SWITCH_CLK */
+#define WM8996_FLL_SWITCH_CLK_MASK 0x0001 /* FLL_SWITCH_CLK */
+#define WM8996_FLL_SWITCH_CLK_SHIFT 0 /* FLL_SWITCH_CLK */
+#define WM8996_FLL_SWITCH_CLK_WIDTH 1 /* FLL_SWITCH_CLK */
+
+/*
+ * R550 (0x226) - FLL EFS 1
+ */
+#define WM8996_FLL_LAMBDA_MASK 0xFFFF /* FLL_LAMBDA - [15:0] */
+#define WM8996_FLL_LAMBDA_SHIFT 0 /* FLL_LAMBDA - [15:0] */
+#define WM8996_FLL_LAMBDA_WIDTH 16 /* FLL_LAMBDA - [15:0] */
+
+/*
+ * R551 (0x227) - FLL EFS 2
+ */
+#define WM8996_FLL_LFSR_SEL_MASK 0x0006 /* FLL_LFSR_SEL - [2:1] */
+#define WM8996_FLL_LFSR_SEL_SHIFT 1 /* FLL_LFSR_SEL - [2:1] */
+#define WM8996_FLL_LFSR_SEL_WIDTH 2 /* FLL_LFSR_SEL - [2:1] */
+#define WM8996_FLL_EFS_ENA 0x0001 /* FLL_EFS_ENA */
+#define WM8996_FLL_EFS_ENA_MASK 0x0001 /* FLL_EFS_ENA */
+#define WM8996_FLL_EFS_ENA_SHIFT 0 /* FLL_EFS_ENA */
+#define WM8996_FLL_EFS_ENA_WIDTH 1 /* FLL_EFS_ENA */
+
+/*
+ * R768 (0x300) - AIF1 Control
+ */
+#define WM8996_AIF1_TRI 0x0004 /* AIF1_TRI */
+#define WM8996_AIF1_TRI_MASK 0x0004 /* AIF1_TRI */
+#define WM8996_AIF1_TRI_SHIFT 2 /* AIF1_TRI */
+#define WM8996_AIF1_TRI_WIDTH 1 /* AIF1_TRI */
+#define WM8996_AIF1_FMT_MASK 0x0003 /* AIF1_FMT - [1:0] */
+#define WM8996_AIF1_FMT_SHIFT 0 /* AIF1_FMT - [1:0] */
+#define WM8996_AIF1_FMT_WIDTH 2 /* AIF1_FMT - [1:0] */
+
+/*
+ * R769 (0x301) - AIF1 BCLK
+ */
+#define WM8996_AIF1_BCLK_INV 0x0400 /* AIF1_BCLK_INV */
+#define WM8996_AIF1_BCLK_INV_MASK 0x0400 /* AIF1_BCLK_INV */
+#define WM8996_AIF1_BCLK_INV_SHIFT 10 /* AIF1_BCLK_INV */
+#define WM8996_AIF1_BCLK_INV_WIDTH 1 /* AIF1_BCLK_INV */
+#define WM8996_AIF1_BCLK_FRC 0x0200 /* AIF1_BCLK_FRC */
+#define WM8996_AIF1_BCLK_FRC_MASK 0x0200 /* AIF1_BCLK_FRC */
+#define WM8996_AIF1_BCLK_FRC_SHIFT 9 /* AIF1_BCLK_FRC */
+#define WM8996_AIF1_BCLK_FRC_WIDTH 1 /* AIF1_BCLK_FRC */
+#define WM8996_AIF1_BCLK_MSTR 0x0100 /* AIF1_BCLK_MSTR */
+#define WM8996_AIF1_BCLK_MSTR_MASK 0x0100 /* AIF1_BCLK_MSTR */
+#define WM8996_AIF1_BCLK_MSTR_SHIFT 8 /* AIF1_BCLK_MSTR */
+#define WM8996_AIF1_BCLK_MSTR_WIDTH 1 /* AIF1_BCLK_MSTR */
+#define WM8996_AIF1_BCLK_DIV_MASK 0x000F /* AIF1_BCLK_DIV - [3:0] */
+#define WM8996_AIF1_BCLK_DIV_SHIFT 0 /* AIF1_BCLK_DIV - [3:0] */
+#define WM8996_AIF1_BCLK_DIV_WIDTH 4 /* AIF1_BCLK_DIV - [3:0] */
+
+/*
+ * R770 (0x302) - AIF1 TX LRCLK(1)
+ */
+#define WM8996_AIF1TX_RATE_MASK 0x07FF /* AIF1TX_RATE - [10:0] */
+#define WM8996_AIF1TX_RATE_SHIFT 0 /* AIF1TX_RATE - [10:0] */
+#define WM8996_AIF1TX_RATE_WIDTH 11 /* AIF1TX_RATE - [10:0] */
+
+/*
+ * R771 (0x303) - AIF1 TX LRCLK(2)
+ */
+#define WM8996_AIF1TX_LRCLK_MODE 0x0008 /* AIF1TX_LRCLK_MODE */
+#define WM8996_AIF1TX_LRCLK_MODE_MASK 0x0008 /* AIF1TX_LRCLK_MODE */
+#define WM8996_AIF1TX_LRCLK_MODE_SHIFT 3 /* AIF1TX_LRCLK_MODE */
+#define WM8996_AIF1TX_LRCLK_MODE_WIDTH 1 /* AIF1TX_LRCLK_MODE */
+#define WM8996_AIF1TX_LRCLK_INV 0x0004 /* AIF1TX_LRCLK_INV */
+#define WM8996_AIF1TX_LRCLK_INV_MASK 0x0004 /* AIF1TX_LRCLK_INV */
+#define WM8996_AIF1TX_LRCLK_INV_SHIFT 2 /* AIF1TX_LRCLK_INV */
+#define WM8996_AIF1TX_LRCLK_INV_WIDTH 1 /* AIF1TX_LRCLK_INV */
+#define WM8996_AIF1TX_LRCLK_FRC 0x0002 /* AIF1TX_LRCLK_FRC */
+#define WM8996_AIF1TX_LRCLK_FRC_MASK 0x0002 /* AIF1TX_LRCLK_FRC */
+#define WM8996_AIF1TX_LRCLK_FRC_SHIFT 1 /* AIF1TX_LRCLK_FRC */
+#define WM8996_AIF1TX_LRCLK_FRC_WIDTH 1 /* AIF1TX_LRCLK_FRC */
+#define WM8996_AIF1TX_LRCLK_MSTR 0x0001 /* AIF1TX_LRCLK_MSTR */
+#define WM8996_AIF1TX_LRCLK_MSTR_MASK 0x0001 /* AIF1TX_LRCLK_MSTR */
+#define WM8996_AIF1TX_LRCLK_MSTR_SHIFT 0 /* AIF1TX_LRCLK_MSTR */
+#define WM8996_AIF1TX_LRCLK_MSTR_WIDTH 1 /* AIF1TX_LRCLK_MSTR */
+
+/*
+ * R772 (0x304) - AIF1 RX LRCLK(1)
+ */
+#define WM8996_AIF1RX_RATE_MASK 0x07FF /* AIF1RX_RATE - [10:0] */
+#define WM8996_AIF1RX_RATE_SHIFT 0 /* AIF1RX_RATE - [10:0] */
+#define WM8996_AIF1RX_RATE_WIDTH 11 /* AIF1RX_RATE - [10:0] */
+
+/*
+ * R773 (0x305) - AIF1 RX LRCLK(2)
+ */
+#define WM8996_AIF1RX_LRCLK_INV 0x0004 /* AIF1RX_LRCLK_INV */
+#define WM8996_AIF1RX_LRCLK_INV_MASK 0x0004 /* AIF1RX_LRCLK_INV */
+#define WM8996_AIF1RX_LRCLK_INV_SHIFT 2 /* AIF1RX_LRCLK_INV */
+#define WM8996_AIF1RX_LRCLK_INV_WIDTH 1 /* AIF1RX_LRCLK_INV */
+#define WM8996_AIF1RX_LRCLK_FRC 0x0002 /* AIF1RX_LRCLK_FRC */
+#define WM8996_AIF1RX_LRCLK_FRC_MASK 0x0002 /* AIF1RX_LRCLK_FRC */
+#define WM8996_AIF1RX_LRCLK_FRC_SHIFT 1 /* AIF1RX_LRCLK_FRC */
+#define WM8996_AIF1RX_LRCLK_FRC_WIDTH 1 /* AIF1RX_LRCLK_FRC */
+#define WM8996_AIF1RX_LRCLK_MSTR 0x0001 /* AIF1RX_LRCLK_MSTR */
+#define WM8996_AIF1RX_LRCLK_MSTR_MASK 0x0001 /* AIF1RX_LRCLK_MSTR */
+#define WM8996_AIF1RX_LRCLK_MSTR_SHIFT 0 /* AIF1RX_LRCLK_MSTR */
+#define WM8996_AIF1RX_LRCLK_MSTR_WIDTH 1 /* AIF1RX_LRCLK_MSTR */
+
+/*
+ * R774 (0x306) - AIF1TX Data Configuration (1)
+ */
+#define WM8996_AIF1TX_WL_MASK 0xFF00 /* AIF1TX_WL - [15:8] */
+#define WM8996_AIF1TX_WL_SHIFT 8 /* AIF1TX_WL - [15:8] */
+#define WM8996_AIF1TX_WL_WIDTH 8 /* AIF1TX_WL - [15:8] */
+#define WM8996_AIF1TX_SLOT_LEN_MASK 0x00FF /* AIF1TX_SLOT_LEN - [7:0] */
+#define WM8996_AIF1TX_SLOT_LEN_SHIFT 0 /* AIF1TX_SLOT_LEN - [7:0] */
+#define WM8996_AIF1TX_SLOT_LEN_WIDTH 8 /* AIF1TX_SLOT_LEN - [7:0] */
+
+/*
+ * R775 (0x307) - AIF1TX Data Configuration (2)
+ */
+#define WM8996_AIF1TX_DAT_TRI 0x0001 /* AIF1TX_DAT_TRI */
+#define WM8996_AIF1TX_DAT_TRI_MASK 0x0001 /* AIF1TX_DAT_TRI */
+#define WM8996_AIF1TX_DAT_TRI_SHIFT 0 /* AIF1TX_DAT_TRI */
+#define WM8996_AIF1TX_DAT_TRI_WIDTH 1 /* AIF1TX_DAT_TRI */
+
+/*
+ * R776 (0x308) - AIF1RX Data Configuration
+ */
+#define WM8996_AIF1RX_WL_MASK 0xFF00 /* AIF1RX_WL - [15:8] */
+#define WM8996_AIF1RX_WL_SHIFT 8 /* AIF1RX_WL - [15:8] */
+#define WM8996_AIF1RX_WL_WIDTH 8 /* AIF1RX_WL - [15:8] */
+#define WM8996_AIF1RX_SLOT_LEN_MASK 0x00FF /* AIF1RX_SLOT_LEN - [7:0] */
+#define WM8996_AIF1RX_SLOT_LEN_SHIFT 0 /* AIF1RX_SLOT_LEN - [7:0] */
+#define WM8996_AIF1RX_SLOT_LEN_WIDTH 8 /* AIF1RX_SLOT_LEN - [7:0] */
+
+/*
+ * R777 (0x309) - AIF1TX Channel 0 Configuration
+ */
+#define WM8996_AIF1TX_CHAN0_DAT_INV 0x8000 /* AIF1TX_CHAN0_DAT_INV */
+#define WM8996_AIF1TX_CHAN0_DAT_INV_MASK 0x8000 /* AIF1TX_CHAN0_DAT_INV */
+#define WM8996_AIF1TX_CHAN0_DAT_INV_SHIFT 15 /* AIF1TX_CHAN0_DAT_INV */
+#define WM8996_AIF1TX_CHAN0_DAT_INV_WIDTH 1 /* AIF1TX_CHAN0_DAT_INV */
+#define WM8996_AIF1TX_CHAN0_SPACING_MASK 0x7E00 /* AIF1TX_CHAN0_SPACING - [14:9] */
+#define WM8996_AIF1TX_CHAN0_SPACING_SHIFT 9 /* AIF1TX_CHAN0_SPACING - [14:9] */
+#define WM8996_AIF1TX_CHAN0_SPACING_WIDTH 6 /* AIF1TX_CHAN0_SPACING - [14:9] */
+#define WM8996_AIF1TX_CHAN0_SLOTS_MASK 0x01C0 /* AIF1TX_CHAN0_SLOTS - [8:6] */
+#define WM8996_AIF1TX_CHAN0_SLOTS_SHIFT 6 /* AIF1TX_CHAN0_SLOTS - [8:6] */
+#define WM8996_AIF1TX_CHAN0_SLOTS_WIDTH 3 /* AIF1TX_CHAN0_SLOTS - [8:6] */
+#define WM8996_AIF1TX_CHAN0_START_SLOT_MASK 0x003F /* AIF1TX_CHAN0_START_SLOT - [5:0] */
+#define WM8996_AIF1TX_CHAN0_START_SLOT_SHIFT 0 /* AIF1TX_CHAN0_START_SLOT - [5:0] */
+#define WM8996_AIF1TX_CHAN0_START_SLOT_WIDTH 6 /* AIF1TX_CHAN0_START_SLOT - [5:0] */
+
+/*
+ * R778 (0x30A) - AIF1TX Channel 1 Configuration
+ */
+#define WM8996_AIF1TX_CHAN1_DAT_INV 0x8000 /* AIF1TX_CHAN1_DAT_INV */
+#define WM8996_AIF1TX_CHAN1_DAT_INV_MASK 0x8000 /* AIF1TX_CHAN1_DAT_INV */
+#define WM8996_AIF1TX_CHAN1_DAT_INV_SHIFT 15 /* AIF1TX_CHAN1_DAT_INV */
+#define WM8996_AIF1TX_CHAN1_DAT_INV_WIDTH 1 /* AIF1TX_CHAN1_DAT_INV */
+#define WM8996_AIF1TX_CHAN1_SPACING_MASK 0x7E00 /* AIF1TX_CHAN1_SPACING - [14:9] */
+#define WM8996_AIF1TX_CHAN1_SPACING_SHIFT 9 /* AIF1TX_CHAN1_SPACING - [14:9] */
+#define WM8996_AIF1TX_CHAN1_SPACING_WIDTH 6 /* AIF1TX_CHAN1_SPACING - [14:9] */
+#define WM8996_AIF1TX_CHAN1_SLOTS_MASK 0x01C0 /* AIF1TX_CHAN1_SLOTS - [8:6] */
+#define WM8996_AIF1TX_CHAN1_SLOTS_SHIFT 6 /* AIF1TX_CHAN1_SLOTS - [8:6] */
+#define WM8996_AIF1TX_CHAN1_SLOTS_WIDTH 3 /* AIF1TX_CHAN1_SLOTS - [8:6] */
+#define WM8996_AIF1TX_CHAN1_START_SLOT_MASK 0x003F /* AIF1TX_CHAN1_START_SLOT - [5:0] */
+#define WM8996_AIF1TX_CHAN1_START_SLOT_SHIFT 0 /* AIF1TX_CHAN1_START_SLOT - [5:0] */
+#define WM8996_AIF1TX_CHAN1_START_SLOT_WIDTH 6 /* AIF1TX_CHAN1_START_SLOT - [5:0] */
+
+/*
+ * R779 (0x30B) - AIF1TX Channel 2 Configuration
+ */
+#define WM8996_AIF1TX_CHAN2_DAT_INV 0x8000 /* AIF1TX_CHAN2_DAT_INV */
+#define WM8996_AIF1TX_CHAN2_DAT_INV_MASK 0x8000 /* AIF1TX_CHAN2_DAT_INV */
+#define WM8996_AIF1TX_CHAN2_DAT_INV_SHIFT 15 /* AIF1TX_CHAN2_DAT_INV */
+#define WM8996_AIF1TX_CHAN2_DAT_INV_WIDTH 1 /* AIF1TX_CHAN2_DAT_INV */
+#define WM8996_AIF1TX_CHAN2_SPACING_MASK 0x7E00 /* AIF1TX_CHAN2_SPACING - [14:9] */
+#define WM8996_AIF1TX_CHAN2_SPACING_SHIFT 9 /* AIF1TX_CHAN2_SPACING - [14:9] */
+#define WM8996_AIF1TX_CHAN2_SPACING_WIDTH 6 /* AIF1TX_CHAN2_SPACING - [14:9] */
+#define WM8996_AIF1TX_CHAN2_SLOTS_MASK 0x01C0 /* AIF1TX_CHAN2_SLOTS - [8:6] */
+#define WM8996_AIF1TX_CHAN2_SLOTS_SHIFT 6 /* AIF1TX_CHAN2_SLOTS - [8:6] */
+#define WM8996_AIF1TX_CHAN2_SLOTS_WIDTH 3 /* AIF1TX_CHAN2_SLOTS - [8:6] */
+#define WM8996_AIF1TX_CHAN2_START_SLOT_MASK 0x003F /* AIF1TX_CHAN2_START_SLOT - [5:0] */
+#define WM8996_AIF1TX_CHAN2_START_SLOT_SHIFT 0 /* AIF1TX_CHAN2_START_SLOT - [5:0] */
+#define WM8996_AIF1TX_CHAN2_START_SLOT_WIDTH 6 /* AIF1TX_CHAN2_START_SLOT - [5:0] */
+
+/*
+ * R780 (0x30C) - AIF1TX Channel 3 Configuration
+ */
+#define WM8996_AIF1TX_CHAN3_DAT_INV 0x8000 /* AIF1TX_CHAN3_DAT_INV */
+#define WM8996_AIF1TX_CHAN3_DAT_INV_MASK 0x8000 /* AIF1TX_CHAN3_DAT_INV */
+#define WM8996_AIF1TX_CHAN3_DAT_INV_SHIFT 15 /* AIF1TX_CHAN3_DAT_INV */
+#define WM8996_AIF1TX_CHAN3_DAT_INV_WIDTH 1 /* AIF1TX_CHAN3_DAT_INV */
+#define WM8996_AIF1TX_CHAN3_SPACING_MASK 0x7E00 /* AIF1TX_CHAN3_SPACING - [14:9] */
+#define WM8996_AIF1TX_CHAN3_SPACING_SHIFT 9 /* AIF1TX_CHAN3_SPACING - [14:9] */
+#define WM8996_AIF1TX_CHAN3_SPACING_WIDTH 6 /* AIF1TX_CHAN3_SPACING - [14:9] */
+#define WM8996_AIF1TX_CHAN3_SLOTS_MASK 0x01C0 /* AIF1TX_CHAN3_SLOTS - [8:6] */
+#define WM8996_AIF1TX_CHAN3_SLOTS_SHIFT 6 /* AIF1TX_CHAN3_SLOTS - [8:6] */
+#define WM8996_AIF1TX_CHAN3_SLOTS_WIDTH 3 /* AIF1TX_CHAN3_SLOTS - [8:6] */
+#define WM8996_AIF1TX_CHAN3_START_SLOT_MASK 0x003F /* AIF1TX_CHAN3_START_SLOT - [5:0] */
+#define WM8996_AIF1TX_CHAN3_START_SLOT_SHIFT 0 /* AIF1TX_CHAN3_START_SLOT - [5:0] */
+#define WM8996_AIF1TX_CHAN3_START_SLOT_WIDTH 6 /* AIF1TX_CHAN3_START_SLOT - [5:0] */
+
+/*
+ * R781 (0x30D) - AIF1TX Channel 4 Configuration
+ */
+#define WM8996_AIF1TX_CHAN4_DAT_INV 0x8000 /* AIF1TX_CHAN4_DAT_INV */
+#define WM8996_AIF1TX_CHAN4_DAT_INV_MASK 0x8000 /* AIF1TX_CHAN4_DAT_INV */
+#define WM8996_AIF1TX_CHAN4_DAT_INV_SHIFT 15 /* AIF1TX_CHAN4_DAT_INV */
+#define WM8996_AIF1TX_CHAN4_DAT_INV_WIDTH 1 /* AIF1TX_CHAN4_DAT_INV */
+#define WM8996_AIF1TX_CHAN4_SPACING_MASK 0x7E00 /* AIF1TX_CHAN4_SPACING - [14:9] */
+#define WM8996_AIF1TX_CHAN4_SPACING_SHIFT 9 /* AIF1TX_CHAN4_SPACING - [14:9] */
+#define WM8996_AIF1TX_CHAN4_SPACING_WIDTH 6 /* AIF1TX_CHAN4_SPACING - [14:9] */
+#define WM8996_AIF1TX_CHAN4_SLOTS_MASK 0x01C0 /* AIF1TX_CHAN4_SLOTS - [8:6] */
+#define WM8996_AIF1TX_CHAN4_SLOTS_SHIFT 6 /* AIF1TX_CHAN4_SLOTS - [8:6] */
+#define WM8996_AIF1TX_CHAN4_SLOTS_WIDTH 3 /* AIF1TX_CHAN4_SLOTS - [8:6] */
+#define WM8996_AIF1TX_CHAN4_START_SLOT_MASK 0x003F /* AIF1TX_CHAN4_START_SLOT - [5:0] */
+#define WM8996_AIF1TX_CHAN4_START_SLOT_SHIFT 0 /* AIF1TX_CHAN4_START_SLOT - [5:0] */
+#define WM8996_AIF1TX_CHAN4_START_SLOT_WIDTH 6 /* AIF1TX_CHAN4_START_SLOT - [5:0] */
+
+/*
+ * R782 (0x30E) - AIF1TX Channel 5 Configuration
+ */
+#define WM8996_AIF1TX_CHAN5_DAT_INV 0x8000 /* AIF1TX_CHAN5_DAT_INV */
+#define WM8996_AIF1TX_CHAN5_DAT_INV_MASK 0x8000 /* AIF1TX_CHAN5_DAT_INV */
+#define WM8996_AIF1TX_CHAN5_DAT_INV_SHIFT 15 /* AIF1TX_CHAN5_DAT_INV */
+#define WM8996_AIF1TX_CHAN5_DAT_INV_WIDTH 1 /* AIF1TX_CHAN5_DAT_INV */
+#define WM8996_AIF1TX_CHAN5_SPACING_MASK 0x7E00 /* AIF1TX_CHAN5_SPACING - [14:9] */
+#define WM8996_AIF1TX_CHAN5_SPACING_SHIFT 9 /* AIF1TX_CHAN5_SPACING - [14:9] */
+#define WM8996_AIF1TX_CHAN5_SPACING_WIDTH 6 /* AIF1TX_CHAN5_SPACING - [14:9] */
+#define WM8996_AIF1TX_CHAN5_SLOTS_MASK 0x01C0 /* AIF1TX_CHAN5_SLOTS - [8:6] */
+#define WM8996_AIF1TX_CHAN5_SLOTS_SHIFT 6 /* AIF1TX_CHAN5_SLOTS - [8:6] */
+#define WM8996_AIF1TX_CHAN5_SLOTS_WIDTH 3 /* AIF1TX_CHAN5_SLOTS - [8:6] */
+#define WM8996_AIF1TX_CHAN5_START_SLOT_MASK 0x003F /* AIF1TX_CHAN5_START_SLOT - [5:0] */
+#define WM8996_AIF1TX_CHAN5_START_SLOT_SHIFT 0 /* AIF1TX_CHAN5_START_SLOT - [5:0] */
+#define WM8996_AIF1TX_CHAN5_START_SLOT_WIDTH 6 /* AIF1TX_CHAN5_START_SLOT - [5:0] */
+
+/*
+ * R783 (0x30F) - AIF1RX Channel 0 Configuration
+ */
+#define WM8996_AIF1RX_CHAN0_DAT_INV 0x8000 /* AIF1RX_CHAN0_DAT_INV */
+#define WM8996_AIF1RX_CHAN0_DAT_INV_MASK 0x8000 /* AIF1RX_CHAN0_DAT_INV */
+#define WM8996_AIF1RX_CHAN0_DAT_INV_SHIFT 15 /* AIF1RX_CHAN0_DAT_INV */
+#define WM8996_AIF1RX_CHAN0_DAT_INV_WIDTH 1 /* AIF1RX_CHAN0_DAT_INV */
+#define WM8996_AIF1RX_CHAN0_SPACING_MASK 0x7E00 /* AIF1RX_CHAN0_SPACING - [14:9] */
+#define WM8996_AIF1RX_CHAN0_SPACING_SHIFT 9 /* AIF1RX_CHAN0_SPACING - [14:9] */
+#define WM8996_AIF1RX_CHAN0_SPACING_WIDTH 6 /* AIF1RX_CHAN0_SPACING - [14:9] */
+#define WM8996_AIF1RX_CHAN0_SLOTS_MASK 0x01C0 /* AIF1RX_CHAN0_SLOTS - [8:6] */
+#define WM8996_AIF1RX_CHAN0_SLOTS_SHIFT 6 /* AIF1RX_CHAN0_SLOTS - [8:6] */
+#define WM8996_AIF1RX_CHAN0_SLOTS_WIDTH 3 /* AIF1RX_CHAN0_SLOTS - [8:6] */
+#define WM8996_AIF1RX_CHAN0_START_SLOT_MASK 0x003F /* AIF1RX_CHAN0_START_SLOT - [5:0] */
+#define WM8996_AIF1RX_CHAN0_START_SLOT_SHIFT 0 /* AIF1RX_CHAN0_START_SLOT - [5:0] */
+#define WM8996_AIF1RX_CHAN0_START_SLOT_WIDTH 6 /* AIF1RX_CHAN0_START_SLOT - [5:0] */
+
+/*
+ * R784 (0x310) - AIF1RX Channel 1 Configuration
+ */
+#define WM8996_AIF1RX_CHAN1_DAT_INV 0x8000 /* AIF1RX_CHAN1_DAT_INV */
+#define WM8996_AIF1RX_CHAN1_DAT_INV_MASK 0x8000 /* AIF1RX_CHAN1_DAT_INV */
+#define WM8996_AIF1RX_CHAN1_DAT_INV_SHIFT 15 /* AIF1RX_CHAN1_DAT_INV */
+#define WM8996_AIF1RX_CHAN1_DAT_INV_WIDTH 1 /* AIF1RX_CHAN1_DAT_INV */
+#define WM8996_AIF1RX_CHAN1_SPACING_MASK 0x7E00 /* AIF1RX_CHAN1_SPACING - [14:9] */
+#define WM8996_AIF1RX_CHAN1_SPACING_SHIFT 9 /* AIF1RX_CHAN1_SPACING - [14:9] */
+#define WM8996_AIF1RX_CHAN1_SPACING_WIDTH 6 /* AIF1RX_CHAN1_SPACING - [14:9] */
+#define WM8996_AIF1RX_CHAN1_SLOTS_MASK 0x01C0 /* AIF1RX_CHAN1_SLOTS - [8:6] */
+#define WM8996_AIF1RX_CHAN1_SLOTS_SHIFT 6 /* AIF1RX_CHAN1_SLOTS - [8:6] */
+#define WM8996_AIF1RX_CHAN1_SLOTS_WIDTH 3 /* AIF1RX_CHAN1_SLOTS - [8:6] */
+#define WM8996_AIF1RX_CHAN1_START_SLOT_MASK 0x003F /* AIF1RX_CHAN1_START_SLOT - [5:0] */
+#define WM8996_AIF1RX_CHAN1_START_SLOT_SHIFT 0 /* AIF1RX_CHAN1_START_SLOT - [5:0] */
+#define WM8996_AIF1RX_CHAN1_START_SLOT_WIDTH 6 /* AIF1RX_CHAN1_START_SLOT - [5:0] */
+
+/*
+ * R785 (0x311) - AIF1RX Channel 2 Configuration
+ */
+#define WM8996_AIF1RX_CHAN2_DAT_INV 0x8000 /* AIF1RX_CHAN2_DAT_INV */
+#define WM8996_AIF1RX_CHAN2_DAT_INV_MASK 0x8000 /* AIF1RX_CHAN2_DAT_INV */
+#define WM8996_AIF1RX_CHAN2_DAT_INV_SHIFT 15 /* AIF1RX_CHAN2_DAT_INV */
+#define WM8996_AIF1RX_CHAN2_DAT_INV_WIDTH 1 /* AIF1RX_CHAN2_DAT_INV */
+#define WM8996_AIF1RX_CHAN2_SPACING_MASK 0x7E00 /* AIF1RX_CHAN2_SPACING - [14:9] */
+#define WM8996_AIF1RX_CHAN2_SPACING_SHIFT 9 /* AIF1RX_CHAN2_SPACING - [14:9] */
+#define WM8996_AIF1RX_CHAN2_SPACING_WIDTH 6 /* AIF1RX_CHAN2_SPACING - [14:9] */
+#define WM8996_AIF1RX_CHAN2_SLOTS_MASK 0x01C0 /* AIF1RX_CHAN2_SLOTS - [8:6] */
+#define WM8996_AIF1RX_CHAN2_SLOTS_SHIFT 6 /* AIF1RX_CHAN2_SLOTS - [8:6] */
+#define WM8996_AIF1RX_CHAN2_SLOTS_WIDTH 3 /* AIF1RX_CHAN2_SLOTS - [8:6] */
+#define WM8996_AIF1RX_CHAN2_START_SLOT_MASK 0x003F /* AIF1RX_CHAN2_START_SLOT - [5:0] */
+#define WM8996_AIF1RX_CHAN2_START_SLOT_SHIFT 0 /* AIF1RX_CHAN2_START_SLOT - [5:0] */
+#define WM8996_AIF1RX_CHAN2_START_SLOT_WIDTH 6 /* AIF1RX_CHAN2_START_SLOT - [5:0] */
+
+/*
+ * R786 (0x312) - AIF1RX Channel 3 Configuration
+ */
+#define WM8996_AIF1RX_CHAN3_DAT_INV 0x8000 /* AIF1RX_CHAN3_DAT_INV */
+#define WM8996_AIF1RX_CHAN3_DAT_INV_MASK 0x8000 /* AIF1RX_CHAN3_DAT_INV */
+#define WM8996_AIF1RX_CHAN3_DAT_INV_SHIFT 15 /* AIF1RX_CHAN3_DAT_INV */
+#define WM8996_AIF1RX_CHAN3_DAT_INV_WIDTH 1 /* AIF1RX_CHAN3_DAT_INV */
+#define WM8996_AIF1RX_CHAN3_SPACING_MASK 0x7E00 /* AIF1RX_CHAN3_SPACING - [14:9] */
+#define WM8996_AIF1RX_CHAN3_SPACING_SHIFT 9 /* AIF1RX_CHAN3_SPACING - [14:9] */
+#define WM8996_AIF1RX_CHAN3_SPACING_WIDTH 6 /* AIF1RX_CHAN3_SPACING - [14:9] */
+#define WM8996_AIF1RX_CHAN3_SLOTS_MASK 0x01C0 /* AIF1RX_CHAN3_SLOTS - [8:6] */
+#define WM8996_AIF1RX_CHAN3_SLOTS_SHIFT 6 /* AIF1RX_CHAN3_SLOTS - [8:6] */
+#define WM8996_AIF1RX_CHAN3_SLOTS_WIDTH 3 /* AIF1RX_CHAN3_SLOTS - [8:6] */
+#define WM8996_AIF1RX_CHAN3_START_SLOT_MASK 0x003F /* AIF1RX_CHAN3_START_SLOT - [5:0] */
+#define WM8996_AIF1RX_CHAN3_START_SLOT_SHIFT 0 /* AIF1RX_CHAN3_START_SLOT - [5:0] */
+#define WM8996_AIF1RX_CHAN3_START_SLOT_WIDTH 6 /* AIF1RX_CHAN3_START_SLOT - [5:0] */
+
+/*
+ * R787 (0x313) - AIF1RX Channel 4 Configuration
+ */
+#define WM8996_AIF1RX_CHAN4_DAT_INV 0x8000 /* AIF1RX_CHAN4_DAT_INV */
+#define WM8996_AIF1RX_CHAN4_DAT_INV_MASK 0x8000 /* AIF1RX_CHAN4_DAT_INV */
+#define WM8996_AIF1RX_CHAN4_DAT_INV_SHIFT 15 /* AIF1RX_CHAN4_DAT_INV */
+#define WM8996_AIF1RX_CHAN4_DAT_INV_WIDTH 1 /* AIF1RX_CHAN4_DAT_INV */
+#define WM8996_AIF1RX_CHAN4_SPACING_MASK 0x7E00 /* AIF1RX_CHAN4_SPACING - [14:9] */
+#define WM8996_AIF1RX_CHAN4_SPACING_SHIFT 9 /* AIF1RX_CHAN4_SPACING - [14:9] */
+#define WM8996_AIF1RX_CHAN4_SPACING_WIDTH 6 /* AIF1RX_CHAN4_SPACING - [14:9] */
+#define WM8996_AIF1RX_CHAN4_SLOTS_MASK 0x01C0 /* AIF1RX_CHAN4_SLOTS - [8:6] */
+#define WM8996_AIF1RX_CHAN4_SLOTS_SHIFT 6 /* AIF1RX_CHAN4_SLOTS - [8:6] */
+#define WM8996_AIF1RX_CHAN4_SLOTS_WIDTH 3 /* AIF1RX_CHAN4_SLOTS - [8:6] */
+#define WM8996_AIF1RX_CHAN4_START_SLOT_MASK 0x003F /* AIF1RX_CHAN4_START_SLOT - [5:0] */
+#define WM8996_AIF1RX_CHAN4_START_SLOT_SHIFT 0 /* AIF1RX_CHAN4_START_SLOT - [5:0] */
+#define WM8996_AIF1RX_CHAN4_START_SLOT_WIDTH 6 /* AIF1RX_CHAN4_START_SLOT - [5:0] */
+
+/*
+ * R788 (0x314) - AIF1RX Channel 5 Configuration
+ */
+#define WM8996_AIF1RX_CHAN5_DAT_INV 0x8000 /* AIF1RX_CHAN5_DAT_INV */
+#define WM8996_AIF1RX_CHAN5_DAT_INV_MASK 0x8000 /* AIF1RX_CHAN5_DAT_INV */
+#define WM8996_AIF1RX_CHAN5_DAT_INV_SHIFT 15 /* AIF1RX_CHAN5_DAT_INV */
+#define WM8996_AIF1RX_CHAN5_DAT_INV_WIDTH 1 /* AIF1RX_CHAN5_DAT_INV */
+#define WM8996_AIF1RX_CHAN5_SPACING_MASK 0x7E00 /* AIF1RX_CHAN5_SPACING - [14:9] */
+#define WM8996_AIF1RX_CHAN5_SPACING_SHIFT 9 /* AIF1RX_CHAN5_SPACING - [14:9] */
+#define WM8996_AIF1RX_CHAN5_SPACING_WIDTH 6 /* AIF1RX_CHAN5_SPACING - [14:9] */
+#define WM8996_AIF1RX_CHAN5_SLOTS_MASK 0x01C0 /* AIF1RX_CHAN5_SLOTS - [8:6] */
+#define WM8996_AIF1RX_CHAN5_SLOTS_SHIFT 6 /* AIF1RX_CHAN5_SLOTS - [8:6] */
+#define WM8996_AIF1RX_CHAN5_SLOTS_WIDTH 3 /* AIF1RX_CHAN5_SLOTS - [8:6] */
+#define WM8996_AIF1RX_CHAN5_START_SLOT_MASK 0x003F /* AIF1RX_CHAN5_START_SLOT - [5:0] */
+#define WM8996_AIF1RX_CHAN5_START_SLOT_SHIFT 0 /* AIF1RX_CHAN5_START_SLOT - [5:0] */
+#define WM8996_AIF1RX_CHAN5_START_SLOT_WIDTH 6 /* AIF1RX_CHAN5_START_SLOT - [5:0] */
+
+/*
+ * R789 (0x315) - AIF1RX Mono Configuration
+ */
+#define WM8996_AIF1RX_CHAN4_MONO_MODE 0x0004 /* AIF1RX_CHAN4_MONO_MODE */
+#define WM8996_AIF1RX_CHAN4_MONO_MODE_MASK 0x0004 /* AIF1RX_CHAN4_MONO_MODE */
+#define WM8996_AIF1RX_CHAN4_MONO_MODE_SHIFT 2 /* AIF1RX_CHAN4_MONO_MODE */
+#define WM8996_AIF1RX_CHAN4_MONO_MODE_WIDTH 1 /* AIF1RX_CHAN4_MONO_MODE */
+#define WM8996_AIF1RX_CHAN2_MONO_MODE 0x0002 /* AIF1RX_CHAN2_MONO_MODE */
+#define WM8996_AIF1RX_CHAN2_MONO_MODE_MASK 0x0002 /* AIF1RX_CHAN2_MONO_MODE */
+#define WM8996_AIF1RX_CHAN2_MONO_MODE_SHIFT 1 /* AIF1RX_CHAN2_MONO_MODE */
+#define WM8996_AIF1RX_CHAN2_MONO_MODE_WIDTH 1 /* AIF1RX_CHAN2_MONO_MODE */
+#define WM8996_AIF1RX_CHAN0_MONO_MODE 0x0001 /* AIF1RX_CHAN0_MONO_MODE */
+#define WM8996_AIF1RX_CHAN0_MONO_MODE_MASK 0x0001 /* AIF1RX_CHAN0_MONO_MODE */
+#define WM8996_AIF1RX_CHAN0_MONO_MODE_SHIFT 0 /* AIF1RX_CHAN0_MONO_MODE */
+#define WM8996_AIF1RX_CHAN0_MONO_MODE_WIDTH 1 /* AIF1RX_CHAN0_MONO_MODE */
+
+/*
+ * R794 (0x31A) - AIF1TX Test
+ */
+#define WM8996_AIF1TX45_DITHER_ENA 0x0004 /* AIF1TX45_DITHER_ENA */
+#define WM8996_AIF1TX45_DITHER_ENA_MASK 0x0004 /* AIF1TX45_DITHER_ENA */
+#define WM8996_AIF1TX45_DITHER_ENA_SHIFT 2 /* AIF1TX45_DITHER_ENA */
+#define WM8996_AIF1TX45_DITHER_ENA_WIDTH 1 /* AIF1TX45_DITHER_ENA */
+#define WM8996_AIF1TX23_DITHER_ENA 0x0002 /* AIF1TX23_DITHER_ENA */
+#define WM8996_AIF1TX23_DITHER_ENA_MASK 0x0002 /* AIF1TX23_DITHER_ENA */
+#define WM8996_AIF1TX23_DITHER_ENA_SHIFT 1 /* AIF1TX23_DITHER_ENA */
+#define WM8996_AIF1TX23_DITHER_ENA_WIDTH 1 /* AIF1TX23_DITHER_ENA */
+#define WM8996_AIF1TX01_DITHER_ENA 0x0001 /* AIF1TX01_DITHER_ENA */
+#define WM8996_AIF1TX01_DITHER_ENA_MASK 0x0001 /* AIF1TX01_DITHER_ENA */
+#define WM8996_AIF1TX01_DITHER_ENA_SHIFT 0 /* AIF1TX01_DITHER_ENA */
+#define WM8996_AIF1TX01_DITHER_ENA_WIDTH 1 /* AIF1TX01_DITHER_ENA */
+
+/*
+ * R800 (0x320) - AIF2 Control
+ */
+#define WM8996_AIF2_TRI 0x0004 /* AIF2_TRI */
+#define WM8996_AIF2_TRI_MASK 0x0004 /* AIF2_TRI */
+#define WM8996_AIF2_TRI_SHIFT 2 /* AIF2_TRI */
+#define WM8996_AIF2_TRI_WIDTH 1 /* AIF2_TRI */
+#define WM8996_AIF2_FMT_MASK 0x0003 /* AIF2_FMT - [1:0] */
+#define WM8996_AIF2_FMT_SHIFT 0 /* AIF2_FMT - [1:0] */
+#define WM8996_AIF2_FMT_WIDTH 2 /* AIF2_FMT - [1:0] */
+
+/*
+ * R801 (0x321) - AIF2 BCLK
+ */
+#define WM8996_AIF2_BCLK_INV 0x0400 /* AIF2_BCLK_INV */
+#define WM8996_AIF2_BCLK_INV_MASK 0x0400 /* AIF2_BCLK_INV */
+#define WM8996_AIF2_BCLK_INV_SHIFT 10 /* AIF2_BCLK_INV */
+#define WM8996_AIF2_BCLK_INV_WIDTH 1 /* AIF2_BCLK_INV */
+#define WM8996_AIF2_BCLK_FRC 0x0200 /* AIF2_BCLK_FRC */
+#define WM8996_AIF2_BCLK_FRC_MASK 0x0200 /* AIF2_BCLK_FRC */
+#define WM8996_AIF2_BCLK_FRC_SHIFT 9 /* AIF2_BCLK_FRC */
+#define WM8996_AIF2_BCLK_FRC_WIDTH 1 /* AIF2_BCLK_FRC */
+#define WM8996_AIF2_BCLK_MSTR 0x0100 /* AIF2_BCLK_MSTR */
+#define WM8996_AIF2_BCLK_MSTR_MASK 0x0100 /* AIF2_BCLK_MSTR */
+#define WM8996_AIF2_BCLK_MSTR_SHIFT 8 /* AIF2_BCLK_MSTR */
+#define WM8996_AIF2_BCLK_MSTR_WIDTH 1 /* AIF2_BCLK_MSTR */
+#define WM8996_AIF2_BCLK_DIV_MASK 0x000F /* AIF2_BCLK_DIV - [3:0] */
+#define WM8996_AIF2_BCLK_DIV_SHIFT 0 /* AIF2_BCLK_DIV - [3:0] */
+#define WM8996_AIF2_BCLK_DIV_WIDTH 4 /* AIF2_BCLK_DIV - [3:0] */
+
+/*
+ * R802 (0x322) - AIF2 TX LRCLK(1)
+ */
+#define WM8996_AIF2TX_RATE_MASK 0x07FF /* AIF2TX_RATE - [10:0] */
+#define WM8996_AIF2TX_RATE_SHIFT 0 /* AIF2TX_RATE - [10:0] */
+#define WM8996_AIF2TX_RATE_WIDTH 11 /* AIF2TX_RATE - [10:0] */
+
+/*
+ * R803 (0x323) - AIF2 TX LRCLK(2)
+ */
+#define WM8996_AIF2TX_LRCLK_MODE 0x0008 /* AIF2TX_LRCLK_MODE */
+#define WM8996_AIF2TX_LRCLK_MODE_MASK 0x0008 /* AIF2TX_LRCLK_MODE */
+#define WM8996_AIF2TX_LRCLK_MODE_SHIFT 3 /* AIF2TX_LRCLK_MODE */
+#define WM8996_AIF2TX_LRCLK_MODE_WIDTH 1 /* AIF2TX_LRCLK_MODE */
+#define WM8996_AIF2TX_LRCLK_INV 0x0004 /* AIF2TX_LRCLK_INV */
+#define WM8996_AIF2TX_LRCLK_INV_MASK 0x0004 /* AIF2TX_LRCLK_INV */
+#define WM8996_AIF2TX_LRCLK_INV_SHIFT 2 /* AIF2TX_LRCLK_INV */
+#define WM8996_AIF2TX_LRCLK_INV_WIDTH 1 /* AIF2TX_LRCLK_INV */
+#define WM8996_AIF2TX_LRCLK_FRC 0x0002 /* AIF2TX_LRCLK_FRC */
+#define WM8996_AIF2TX_LRCLK_FRC_MASK 0x0002 /* AIF2TX_LRCLK_FRC */
+#define WM8996_AIF2TX_LRCLK_FRC_SHIFT 1 /* AIF2TX_LRCLK_FRC */
+#define WM8996_AIF2TX_LRCLK_FRC_WIDTH 1 /* AIF2TX_LRCLK_FRC */
+#define WM8996_AIF2TX_LRCLK_MSTR 0x0001 /* AIF2TX_LRCLK_MSTR */
+#define WM8996_AIF2TX_LRCLK_MSTR_MASK 0x0001 /* AIF2TX_LRCLK_MSTR */
+#define WM8996_AIF2TX_LRCLK_MSTR_SHIFT 0 /* AIF2TX_LRCLK_MSTR */
+#define WM8996_AIF2TX_LRCLK_MSTR_WIDTH 1 /* AIF2TX_LRCLK_MSTR */
+
+/*
+ * R804 (0x324) - AIF2 RX LRCLK(1)
+ */
+#define WM8996_AIF2RX_RATE_MASK 0x07FF /* AIF2RX_RATE - [10:0] */
+#define WM8996_AIF2RX_RATE_SHIFT 0 /* AIF2RX_RATE - [10:0] */
+#define WM8996_AIF2RX_RATE_WIDTH 11 /* AIF2RX_RATE - [10:0] */
+
+/*
+ * R805 (0x325) - AIF2 RX LRCLK(2)
+ */
+#define WM8996_AIF2RX_LRCLK_INV 0x0004 /* AIF2RX_LRCLK_INV */
+#define WM8996_AIF2RX_LRCLK_INV_MASK 0x0004 /* AIF2RX_LRCLK_INV */
+#define WM8996_AIF2RX_LRCLK_INV_SHIFT 2 /* AIF2RX_LRCLK_INV */
+#define WM8996_AIF2RX_LRCLK_INV_WIDTH 1 /* AIF2RX_LRCLK_INV */
+#define WM8996_AIF2RX_LRCLK_FRC 0x0002 /* AIF2RX_LRCLK_FRC */
+#define WM8996_AIF2RX_LRCLK_FRC_MASK 0x0002 /* AIF2RX_LRCLK_FRC */
+#define WM8996_AIF2RX_LRCLK_FRC_SHIFT 1 /* AIF2RX_LRCLK_FRC */
+#define WM8996_AIF2RX_LRCLK_FRC_WIDTH 1 /* AIF2RX_LRCLK_FRC */
+#define WM8996_AIF2RX_LRCLK_MSTR 0x0001 /* AIF2RX_LRCLK_MSTR */
+#define WM8996_AIF2RX_LRCLK_MSTR_MASK 0x0001 /* AIF2RX_LRCLK_MSTR */
+#define WM8996_AIF2RX_LRCLK_MSTR_SHIFT 0 /* AIF2RX_LRCLK_MSTR */
+#define WM8996_AIF2RX_LRCLK_MSTR_WIDTH 1 /* AIF2RX_LRCLK_MSTR */
+
+/*
+ * R806 (0x326) - AIF2TX Data Configuration (1)
+ */
+#define WM8996_AIF2TX_WL_MASK 0xFF00 /* AIF2TX_WL - [15:8] */
+#define WM8996_AIF2TX_WL_SHIFT 8 /* AIF2TX_WL - [15:8] */
+#define WM8996_AIF2TX_WL_WIDTH 8 /* AIF2TX_WL - [15:8] */
+#define WM8996_AIF2TX_SLOT_LEN_MASK 0x00FF /* AIF2TX_SLOT_LEN - [7:0] */
+#define WM8996_AIF2TX_SLOT_LEN_SHIFT 0 /* AIF2TX_SLOT_LEN - [7:0] */
+#define WM8996_AIF2TX_SLOT_LEN_WIDTH 8 /* AIF2TX_SLOT_LEN - [7:0] */
+
+/*
+ * R807 (0x327) - AIF2TX Data Configuration (2)
+ */
+#define WM8996_AIF2TX_DAT_TRI 0x0001 /* AIF2TX_DAT_TRI */
+#define WM8996_AIF2TX_DAT_TRI_MASK 0x0001 /* AIF2TX_DAT_TRI */
+#define WM8996_AIF2TX_DAT_TRI_SHIFT 0 /* AIF2TX_DAT_TRI */
+#define WM8996_AIF2TX_DAT_TRI_WIDTH 1 /* AIF2TX_DAT_TRI */
+
+/*
+ * R808 (0x328) - AIF2RX Data Configuration
+ */
+#define WM8996_AIF2RX_WL_MASK 0xFF00 /* AIF2RX_WL - [15:8] */
+#define WM8996_AIF2RX_WL_SHIFT 8 /* AIF2RX_WL - [15:8] */
+#define WM8996_AIF2RX_WL_WIDTH 8 /* AIF2RX_WL - [15:8] */
+#define WM8996_AIF2RX_SLOT_LEN_MASK 0x00FF /* AIF2RX_SLOT_LEN - [7:0] */
+#define WM8996_AIF2RX_SLOT_LEN_SHIFT 0 /* AIF2RX_SLOT_LEN - [7:0] */
+#define WM8996_AIF2RX_SLOT_LEN_WIDTH 8 /* AIF2RX_SLOT_LEN - [7:0] */
+
+/*
+ * R809 (0x329) - AIF2TX Channel 0 Configuration
+ */
+#define WM8996_AIF2TX_CHAN0_DAT_INV 0x8000 /* AIF2TX_CHAN0_DAT_INV */
+#define WM8996_AIF2TX_CHAN0_DAT_INV_MASK 0x8000 /* AIF2TX_CHAN0_DAT_INV */
+#define WM8996_AIF2TX_CHAN0_DAT_INV_SHIFT 15 /* AIF2TX_CHAN0_DAT_INV */
+#define WM8996_AIF2TX_CHAN0_DAT_INV_WIDTH 1 /* AIF2TX_CHAN0_DAT_INV */
+#define WM8996_AIF2TX_CHAN0_SPACING_MASK 0x7E00 /* AIF2TX_CHAN0_SPACING - [14:9] */
+#define WM8996_AIF2TX_CHAN0_SPACING_SHIFT 9 /* AIF2TX_CHAN0_SPACING - [14:9] */
+#define WM8996_AIF2TX_CHAN0_SPACING_WIDTH 6 /* AIF2TX_CHAN0_SPACING - [14:9] */
+#define WM8996_AIF2TX_CHAN0_SLOTS_MASK 0x01C0 /* AIF2TX_CHAN0_SLOTS - [8:6] */
+#define WM8996_AIF2TX_CHAN0_SLOTS_SHIFT 6 /* AIF2TX_CHAN0_SLOTS - [8:6] */
+#define WM8996_AIF2TX_CHAN0_SLOTS_WIDTH 3 /* AIF2TX_CHAN0_SLOTS - [8:6] */
+#define WM8996_AIF2TX_CHAN0_START_SLOT_MASK 0x003F /* AIF2TX_CHAN0_START_SLOT - [5:0] */
+#define WM8996_AIF2TX_CHAN0_START_SLOT_SHIFT 0 /* AIF2TX_CHAN0_START_SLOT - [5:0] */
+#define WM8996_AIF2TX_CHAN0_START_SLOT_WIDTH 6 /* AIF2TX_CHAN0_START_SLOT - [5:0] */
+
+/*
+ * R810 (0x32A) - AIF2TX Channel 1 Configuration
+ */
+#define WM8996_AIF2TX_CHAN1_DAT_INV 0x8000 /* AIF2TX_CHAN1_DAT_INV */
+#define WM8996_AIF2TX_CHAN1_DAT_INV_MASK 0x8000 /* AIF2TX_CHAN1_DAT_INV */
+#define WM8996_AIF2TX_CHAN1_DAT_INV_SHIFT 15 /* AIF2TX_CHAN1_DAT_INV */
+#define WM8996_AIF2TX_CHAN1_DAT_INV_WIDTH 1 /* AIF2TX_CHAN1_DAT_INV */
+#define WM8996_AIF2TX_CHAN1_SPACING_MASK 0x7E00 /* AIF2TX_CHAN1_SPACING - [14:9] */
+#define WM8996_AIF2TX_CHAN1_SPACING_SHIFT 9 /* AIF2TX_CHAN1_SPACING - [14:9] */
+#define WM8996_AIF2TX_CHAN1_SPACING_WIDTH 6 /* AIF2TX_CHAN1_SPACING - [14:9] */
+#define WM8996_AIF2TX_CHAN1_SLOTS_MASK 0x01C0 /* AIF2TX_CHAN1_SLOTS - [8:6] */
+#define WM8996_AIF2TX_CHAN1_SLOTS_SHIFT 6 /* AIF2TX_CHAN1_SLOTS - [8:6] */
+#define WM8996_AIF2TX_CHAN1_SLOTS_WIDTH 3 /* AIF2TX_CHAN1_SLOTS - [8:6] */
+#define WM8996_AIF2TX_CHAN1_START_SLOT_MASK 0x003F /* AIF2TX_CHAN1_START_SLOT - [5:0] */
+#define WM8996_AIF2TX_CHAN1_START_SLOT_SHIFT 0 /* AIF2TX_CHAN1_START_SLOT - [5:0] */
+#define WM8996_AIF2TX_CHAN1_START_SLOT_WIDTH 6 /* AIF2TX_CHAN1_START_SLOT - [5:0] */
+
+/*
+ * R811 (0x32B) - AIF2RX Channel 0 Configuration
+ */
+#define WM8996_AIF2RX_CHAN0_DAT_INV 0x8000 /* AIF2RX_CHAN0_DAT_INV */
+#define WM8996_AIF2RX_CHAN0_DAT_INV_MASK 0x8000 /* AIF2RX_CHAN0_DAT_INV */
+#define WM8996_AIF2RX_CHAN0_DAT_INV_SHIFT 15 /* AIF2RX_CHAN0_DAT_INV */
+#define WM8996_AIF2RX_CHAN0_DAT_INV_WIDTH 1 /* AIF2RX_CHAN0_DAT_INV */
+#define WM8996_AIF2RX_CHAN0_SPACING_MASK 0x7E00 /* AIF2RX_CHAN0_SPACING - [14:9] */
+#define WM8996_AIF2RX_CHAN0_SPACING_SHIFT 9 /* AIF2RX_CHAN0_SPACING - [14:9] */
+#define WM8996_AIF2RX_CHAN0_SPACING_WIDTH 6 /* AIF2RX_CHAN0_SPACING - [14:9] */
+#define WM8996_AIF2RX_CHAN0_SLOTS_MASK 0x01C0 /* AIF2RX_CHAN0_SLOTS - [8:6] */
+#define WM8996_AIF2RX_CHAN0_SLOTS_SHIFT 6 /* AIF2RX_CHAN0_SLOTS - [8:6] */
+#define WM8996_AIF2RX_CHAN0_SLOTS_WIDTH 3 /* AIF2RX_CHAN0_SLOTS - [8:6] */
+#define WM8996_AIF2RX_CHAN0_START_SLOT_MASK 0x003F /* AIF2RX_CHAN0_START_SLOT - [5:0] */
+#define WM8996_AIF2RX_CHAN0_START_SLOT_SHIFT 0 /* AIF2RX_CHAN0_START_SLOT - [5:0] */
+#define WM8996_AIF2RX_CHAN0_START_SLOT_WIDTH 6 /* AIF2RX_CHAN0_START_SLOT - [5:0] */
+
+/*
+ * R812 (0x32C) - AIF2RX Channel 1 Configuration
+ */
+#define WM8996_AIF2RX_CHAN1_DAT_INV 0x8000 /* AIF2RX_CHAN1_DAT_INV */
+#define WM8996_AIF2RX_CHAN1_DAT_INV_MASK 0x8000 /* AIF2RX_CHAN1_DAT_INV */
+#define WM8996_AIF2RX_CHAN1_DAT_INV_SHIFT 15 /* AIF2RX_CHAN1_DAT_INV */
+#define WM8996_AIF2RX_CHAN1_DAT_INV_WIDTH 1 /* AIF2RX_CHAN1_DAT_INV */
+#define WM8996_AIF2RX_CHAN1_SPACING_MASK 0x7E00 /* AIF2RX_CHAN1_SPACING - [14:9] */
+#define WM8996_AIF2RX_CHAN1_SPACING_SHIFT 9 /* AIF2RX_CHAN1_SPACING - [14:9] */
+#define WM8996_AIF2RX_CHAN1_SPACING_WIDTH 6 /* AIF2RX_CHAN1_SPACING - [14:9] */
+#define WM8996_AIF2RX_CHAN1_SLOTS_MASK 0x01C0 /* AIF2RX_CHAN1_SLOTS - [8:6] */
+#define WM8996_AIF2RX_CHAN1_SLOTS_SHIFT 6 /* AIF2RX_CHAN1_SLOTS - [8:6] */
+#define WM8996_AIF2RX_CHAN1_SLOTS_WIDTH 3 /* AIF2RX_CHAN1_SLOTS - [8:6] */
+#define WM8996_AIF2RX_CHAN1_START_SLOT_MASK 0x003F /* AIF2RX_CHAN1_START_SLOT - [5:0] */
+#define WM8996_AIF2RX_CHAN1_START_SLOT_SHIFT 0 /* AIF2RX_CHAN1_START_SLOT - [5:0] */
+#define WM8996_AIF2RX_CHAN1_START_SLOT_WIDTH 6 /* AIF2RX_CHAN1_START_SLOT - [5:0] */
+
+/*
+ * R813 (0x32D) - AIF2RX Mono Configuration
+ */
+#define WM8996_AIF2RX_CHAN0_MONO_MODE 0x0001 /* AIF2RX_CHAN0_MONO_MODE */
+#define WM8996_AIF2RX_CHAN0_MONO_MODE_MASK 0x0001 /* AIF2RX_CHAN0_MONO_MODE */
+#define WM8996_AIF2RX_CHAN0_MONO_MODE_SHIFT 0 /* AIF2RX_CHAN0_MONO_MODE */
+#define WM8996_AIF2RX_CHAN0_MONO_MODE_WIDTH 1 /* AIF2RX_CHAN0_MONO_MODE */
+
+/*
+ * R815 (0x32F) - AIF2TX Test
+ */
+#define WM8996_AIF2TX_DITHER_ENA 0x0001 /* AIF2TX_DITHER_ENA */
+#define WM8996_AIF2TX_DITHER_ENA_MASK 0x0001 /* AIF2TX_DITHER_ENA */
+#define WM8996_AIF2TX_DITHER_ENA_SHIFT 0 /* AIF2TX_DITHER_ENA */
+#define WM8996_AIF2TX_DITHER_ENA_WIDTH 1 /* AIF2TX_DITHER_ENA */
+
+/*
+ * R1024 (0x400) - DSP1 TX Left Volume
+ */
+#define WM8996_DSP1TX_VU 0x0100 /* DSP1TX_VU */
+#define WM8996_DSP1TX_VU_MASK 0x0100 /* DSP1TX_VU */
+#define WM8996_DSP1TX_VU_SHIFT 8 /* DSP1TX_VU */
+#define WM8996_DSP1TX_VU_WIDTH 1 /* DSP1TX_VU */
+#define WM8996_DSP1TXL_VOL_MASK 0x00FF /* DSP1TXL_VOL - [7:0] */
+#define WM8996_DSP1TXL_VOL_SHIFT 0 /* DSP1TXL_VOL - [7:0] */
+#define WM8996_DSP1TXL_VOL_WIDTH 8 /* DSP1TXL_VOL - [7:0] */
+
+/*
+ * R1025 (0x401) - DSP1 TX Right Volume
+ */
+#define WM8996_DSP1TX_VU 0x0100 /* DSP1TX_VU */
+#define WM8996_DSP1TX_VU_MASK 0x0100 /* DSP1TX_VU */
+#define WM8996_DSP1TX_VU_SHIFT 8 /* DSP1TX_VU */
+#define WM8996_DSP1TX_VU_WIDTH 1 /* DSP1TX_VU */
+#define WM8996_DSP1TXR_VOL_MASK 0x00FF /* DSP1TXR_VOL - [7:0] */
+#define WM8996_DSP1TXR_VOL_SHIFT 0 /* DSP1TXR_VOL - [7:0] */
+#define WM8996_DSP1TXR_VOL_WIDTH 8 /* DSP1TXR_VOL - [7:0] */
+
+/*
+ * R1026 (0x402) - DSP1 RX Left Volume
+ */
+#define WM8996_DSP1RX_VU 0x0100 /* DSP1RX_VU */
+#define WM8996_DSP1RX_VU_MASK 0x0100 /* DSP1RX_VU */
+#define WM8996_DSP1RX_VU_SHIFT 8 /* DSP1RX_VU */
+#define WM8996_DSP1RX_VU_WIDTH 1 /* DSP1RX_VU */
+#define WM8996_DSP1RXL_VOL_MASK 0x00FF /* DSP1RXL_VOL - [7:0] */
+#define WM8996_DSP1RXL_VOL_SHIFT 0 /* DSP1RXL_VOL - [7:0] */
+#define WM8996_DSP1RXL_VOL_WIDTH 8 /* DSP1RXL_VOL - [7:0] */
+
+/*
+ * R1027 (0x403) - DSP1 RX Right Volume
+ */
+#define WM8996_DSP1RX_VU 0x0100 /* DSP1RX_VU */
+#define WM8996_DSP1RX_VU_MASK 0x0100 /* DSP1RX_VU */
+#define WM8996_DSP1RX_VU_SHIFT 8 /* DSP1RX_VU */
+#define WM8996_DSP1RX_VU_WIDTH 1 /* DSP1RX_VU */
+#define WM8996_DSP1RXR_VOL_MASK 0x00FF /* DSP1RXR_VOL - [7:0] */
+#define WM8996_DSP1RXR_VOL_SHIFT 0 /* DSP1RXR_VOL - [7:0] */
+#define WM8996_DSP1RXR_VOL_WIDTH 8 /* DSP1RXR_VOL - [7:0] */
+
+/*
+ * R1040 (0x410) - DSP1 TX Filters
+ */
+#define WM8996_DSP1TX_NF 0x2000 /* DSP1TX_NF */
+#define WM8996_DSP1TX_NF_MASK 0x2000 /* DSP1TX_NF */
+#define WM8996_DSP1TX_NF_SHIFT 13 /* DSP1TX_NF */
+#define WM8996_DSP1TX_NF_WIDTH 1 /* DSP1TX_NF */
+#define WM8996_DSP1TXL_HPF 0x1000 /* DSP1TXL_HPF */
+#define WM8996_DSP1TXL_HPF_MASK 0x1000 /* DSP1TXL_HPF */
+#define WM8996_DSP1TXL_HPF_SHIFT 12 /* DSP1TXL_HPF */
+#define WM8996_DSP1TXL_HPF_WIDTH 1 /* DSP1TXL_HPF */
+#define WM8996_DSP1TXR_HPF 0x0800 /* DSP1TXR_HPF */
+#define WM8996_DSP1TXR_HPF_MASK 0x0800 /* DSP1TXR_HPF */
+#define WM8996_DSP1TXR_HPF_SHIFT 11 /* DSP1TXR_HPF */
+#define WM8996_DSP1TXR_HPF_WIDTH 1 /* DSP1TXR_HPF */
+#define WM8996_DSP1TX_HPF_MODE_MASK 0x0018 /* DSP1TX_HPF_MODE - [4:3] */
+#define WM8996_DSP1TX_HPF_MODE_SHIFT 3 /* DSP1TX_HPF_MODE - [4:3] */
+#define WM8996_DSP1TX_HPF_MODE_WIDTH 2 /* DSP1TX_HPF_MODE - [4:3] */
+#define WM8996_DSP1TX_HPF_CUT_MASK 0x0007 /* DSP1TX_HPF_CUT - [2:0] */
+#define WM8996_DSP1TX_HPF_CUT_SHIFT 0 /* DSP1TX_HPF_CUT - [2:0] */
+#define WM8996_DSP1TX_HPF_CUT_WIDTH 3 /* DSP1TX_HPF_CUT - [2:0] */
+
+/*
+ * R1056 (0x420) - DSP1 RX Filters (1)
+ */
+#define WM8996_DSP1RX_MUTE 0x0200 /* DSP1RX_MUTE */
+#define WM8996_DSP1RX_MUTE_MASK 0x0200 /* DSP1RX_MUTE */
+#define WM8996_DSP1RX_MUTE_SHIFT 9 /* DSP1RX_MUTE */
+#define WM8996_DSP1RX_MUTE_WIDTH 1 /* DSP1RX_MUTE */
+#define WM8996_DSP1RX_MONO 0x0080 /* DSP1RX_MONO */
+#define WM8996_DSP1RX_MONO_MASK 0x0080 /* DSP1RX_MONO */
+#define WM8996_DSP1RX_MONO_SHIFT 7 /* DSP1RX_MONO */
+#define WM8996_DSP1RX_MONO_WIDTH 1 /* DSP1RX_MONO */
+#define WM8996_DSP1RX_MUTERATE 0x0020 /* DSP1RX_MUTERATE */
+#define WM8996_DSP1RX_MUTERATE_MASK 0x0020 /* DSP1RX_MUTERATE */
+#define WM8996_DSP1RX_MUTERATE_SHIFT 5 /* DSP1RX_MUTERATE */
+#define WM8996_DSP1RX_MUTERATE_WIDTH 1 /* DSP1RX_MUTERATE */
+#define WM8996_DSP1RX_UNMUTE_RAMP 0x0010 /* DSP1RX_UNMUTE_RAMP */
+#define WM8996_DSP1RX_UNMUTE_RAMP_MASK 0x0010 /* DSP1RX_UNMUTE_RAMP */
+#define WM8996_DSP1RX_UNMUTE_RAMP_SHIFT 4 /* DSP1RX_UNMUTE_RAMP */
+#define WM8996_DSP1RX_UNMUTE_RAMP_WIDTH 1 /* DSP1RX_UNMUTE_RAMP */
+
+/*
+ * R1057 (0x421) - DSP1 RX Filters (2)
+ */
+#define WM8996_DSP1RX_3D_GAIN_MASK 0x3E00 /* DSP1RX_3D_GAIN - [13:9] */
+#define WM8996_DSP1RX_3D_GAIN_SHIFT 9 /* DSP1RX_3D_GAIN - [13:9] */
+#define WM8996_DSP1RX_3D_GAIN_WIDTH 5 /* DSP1RX_3D_GAIN - [13:9] */
+#define WM8996_DSP1RX_3D_ENA 0x0100 /* DSP1RX_3D_ENA */
+#define WM8996_DSP1RX_3D_ENA_MASK 0x0100 /* DSP1RX_3D_ENA */
+#define WM8996_DSP1RX_3D_ENA_SHIFT 8 /* DSP1RX_3D_ENA */
+#define WM8996_DSP1RX_3D_ENA_WIDTH 1 /* DSP1RX_3D_ENA */
+
+/*
+ * R1088 (0x440) - DSP1 DRC (1)
+ */
+#define WM8996_DSP1DRC_SIG_DET_RMS_MASK 0xF800 /* DSP1DRC_SIG_DET_RMS - [15:11] */
+#define WM8996_DSP1DRC_SIG_DET_RMS_SHIFT 11 /* DSP1DRC_SIG_DET_RMS - [15:11] */
+#define WM8996_DSP1DRC_SIG_DET_RMS_WIDTH 5 /* DSP1DRC_SIG_DET_RMS - [15:11] */
+#define WM8996_DSP1DRC_SIG_DET_PK_MASK 0x0600 /* DSP1DRC_SIG_DET_PK - [10:9] */
+#define WM8996_DSP1DRC_SIG_DET_PK_SHIFT 9 /* DSP1DRC_SIG_DET_PK - [10:9] */
+#define WM8996_DSP1DRC_SIG_DET_PK_WIDTH 2 /* DSP1DRC_SIG_DET_PK - [10:9] */
+#define WM8996_DSP1DRC_NG_ENA 0x0100 /* DSP1DRC_NG_ENA */
+#define WM8996_DSP1DRC_NG_ENA_MASK 0x0100 /* DSP1DRC_NG_ENA */
+#define WM8996_DSP1DRC_NG_ENA_SHIFT 8 /* DSP1DRC_NG_ENA */
+#define WM8996_DSP1DRC_NG_ENA_WIDTH 1 /* DSP1DRC_NG_ENA */
+#define WM8996_DSP1DRC_SIG_DET_MODE 0x0080 /* DSP1DRC_SIG_DET_MODE */
+#define WM8996_DSP1DRC_SIG_DET_MODE_MASK 0x0080 /* DSP1DRC_SIG_DET_MODE */
+#define WM8996_DSP1DRC_SIG_DET_MODE_SHIFT 7 /* DSP1DRC_SIG_DET_MODE */
+#define WM8996_DSP1DRC_SIG_DET_MODE_WIDTH 1 /* DSP1DRC_SIG_DET_MODE */
+#define WM8996_DSP1DRC_SIG_DET 0x0040 /* DSP1DRC_SIG_DET */
+#define WM8996_DSP1DRC_SIG_DET_MASK 0x0040 /* DSP1DRC_SIG_DET */
+#define WM8996_DSP1DRC_SIG_DET_SHIFT 6 /* DSP1DRC_SIG_DET */
+#define WM8996_DSP1DRC_SIG_DET_WIDTH 1 /* DSP1DRC_SIG_DET */
+#define WM8996_DSP1DRC_KNEE2_OP_ENA 0x0020 /* DSP1DRC_KNEE2_OP_ENA */
+#define WM8996_DSP1DRC_KNEE2_OP_ENA_MASK 0x0020 /* DSP1DRC_KNEE2_OP_ENA */
+#define WM8996_DSP1DRC_KNEE2_OP_ENA_SHIFT 5 /* DSP1DRC_KNEE2_OP_ENA */
+#define WM8996_DSP1DRC_KNEE2_OP_ENA_WIDTH 1 /* DSP1DRC_KNEE2_OP_ENA */
+#define WM8996_DSP1DRC_QR 0x0010 /* DSP1DRC_QR */
+#define WM8996_DSP1DRC_QR_MASK 0x0010 /* DSP1DRC_QR */
+#define WM8996_DSP1DRC_QR_SHIFT 4 /* DSP1DRC_QR */
+#define WM8996_DSP1DRC_QR_WIDTH 1 /* DSP1DRC_QR */
+#define WM8996_DSP1DRC_ANTICLIP 0x0008 /* DSP1DRC_ANTICLIP */
+#define WM8996_DSP1DRC_ANTICLIP_MASK 0x0008 /* DSP1DRC_ANTICLIP */
+#define WM8996_DSP1DRC_ANTICLIP_SHIFT 3 /* DSP1DRC_ANTICLIP */
+#define WM8996_DSP1DRC_ANTICLIP_WIDTH 1 /* DSP1DRC_ANTICLIP */
+#define WM8996_DSP1RX_DRC_ENA 0x0004 /* DSP1RX_DRC_ENA */
+#define WM8996_DSP1RX_DRC_ENA_MASK 0x0004 /* DSP1RX_DRC_ENA */
+#define WM8996_DSP1RX_DRC_ENA_SHIFT 2 /* DSP1RX_DRC_ENA */
+#define WM8996_DSP1RX_DRC_ENA_WIDTH 1 /* DSP1RX_DRC_ENA */
+#define WM8996_DSP1TXL_DRC_ENA 0x0002 /* DSP1TXL_DRC_ENA */
+#define WM8996_DSP1TXL_DRC_ENA_MASK 0x0002 /* DSP1TXL_DRC_ENA */
+#define WM8996_DSP1TXL_DRC_ENA_SHIFT 1 /* DSP1TXL_DRC_ENA */
+#define WM8996_DSP1TXL_DRC_ENA_WIDTH 1 /* DSP1TXL_DRC_ENA */
+#define WM8996_DSP1TXR_DRC_ENA 0x0001 /* DSP1TXR_DRC_ENA */
+#define WM8996_DSP1TXR_DRC_ENA_MASK 0x0001 /* DSP1TXR_DRC_ENA */
+#define WM8996_DSP1TXR_DRC_ENA_SHIFT 0 /* DSP1TXR_DRC_ENA */
+#define WM8996_DSP1TXR_DRC_ENA_WIDTH 1 /* DSP1TXR_DRC_ENA */
+
+/*
+ * R1089 (0x441) - DSP1 DRC (2)
+ */
+#define WM8996_DSP1DRC_ATK_MASK 0x1E00 /* DSP1DRC_ATK - [12:9] */
+#define WM8996_DSP1DRC_ATK_SHIFT 9 /* DSP1DRC_ATK - [12:9] */
+#define WM8996_DSP1DRC_ATK_WIDTH 4 /* DSP1DRC_ATK - [12:9] */
+#define WM8996_DSP1DRC_DCY_MASK 0x01E0 /* DSP1DRC_DCY - [8:5] */
+#define WM8996_DSP1DRC_DCY_SHIFT 5 /* DSP1DRC_DCY - [8:5] */
+#define WM8996_DSP1DRC_DCY_WIDTH 4 /* DSP1DRC_DCY - [8:5] */
+#define WM8996_DSP1DRC_MINGAIN_MASK 0x001C /* DSP1DRC_MINGAIN - [4:2] */
+#define WM8996_DSP1DRC_MINGAIN_SHIFT 2 /* DSP1DRC_MINGAIN - [4:2] */
+#define WM8996_DSP1DRC_MINGAIN_WIDTH 3 /* DSP1DRC_MINGAIN - [4:2] */
+#define WM8996_DSP1DRC_MAXGAIN_MASK 0x0003 /* DSP1DRC_MAXGAIN - [1:0] */
+#define WM8996_DSP1DRC_MAXGAIN_SHIFT 0 /* DSP1DRC_MAXGAIN - [1:0] */
+#define WM8996_DSP1DRC_MAXGAIN_WIDTH 2 /* DSP1DRC_MAXGAIN - [1:0] */
+
+/*
+ * R1090 (0x442) - DSP1 DRC (3)
+ */
+#define WM8996_DSP1DRC_NG_MINGAIN_MASK 0xF000 /* DSP1DRC_NG_MINGAIN - [15:12] */
+#define WM8996_DSP1DRC_NG_MINGAIN_SHIFT 12 /* DSP1DRC_NG_MINGAIN - [15:12] */
+#define WM8996_DSP1DRC_NG_MINGAIN_WIDTH 4 /* DSP1DRC_NG_MINGAIN - [15:12] */
+#define WM8996_DSP1DRC_NG_EXP_MASK 0x0C00 /* DSP1DRC_NG_EXP - [11:10] */
+#define WM8996_DSP1DRC_NG_EXP_SHIFT 10 /* DSP1DRC_NG_EXP - [11:10] */
+#define WM8996_DSP1DRC_NG_EXP_WIDTH 2 /* DSP1DRC_NG_EXP - [11:10] */
+#define WM8996_DSP1DRC_QR_THR_MASK 0x0300 /* DSP1DRC_QR_THR - [9:8] */
+#define WM8996_DSP1DRC_QR_THR_SHIFT 8 /* DSP1DRC_QR_THR - [9:8] */
+#define WM8996_DSP1DRC_QR_THR_WIDTH 2 /* DSP1DRC_QR_THR - [9:8] */
+#define WM8996_DSP1DRC_QR_DCY_MASK 0x00C0 /* DSP1DRC_QR_DCY - [7:6] */
+#define WM8996_DSP1DRC_QR_DCY_SHIFT 6 /* DSP1DRC_QR_DCY - [7:6] */
+#define WM8996_DSP1DRC_QR_DCY_WIDTH 2 /* DSP1DRC_QR_DCY - [7:6] */
+#define WM8996_DSP1DRC_HI_COMP_MASK 0x0038 /* DSP1DRC_HI_COMP - [5:3] */
+#define WM8996_DSP1DRC_HI_COMP_SHIFT 3 /* DSP1DRC_HI_COMP - [5:3] */
+#define WM8996_DSP1DRC_HI_COMP_WIDTH 3 /* DSP1DRC_HI_COMP - [5:3] */
+#define WM8996_DSP1DRC_LO_COMP_MASK 0x0007 /* DSP1DRC_LO_COMP - [2:0] */
+#define WM8996_DSP1DRC_LO_COMP_SHIFT 0 /* DSP1DRC_LO_COMP - [2:0] */
+#define WM8996_DSP1DRC_LO_COMP_WIDTH 3 /* DSP1DRC_LO_COMP - [2:0] */
+
+/*
+ * R1091 (0x443) - DSP1 DRC (4)
+ */
+#define WM8996_DSP1DRC_KNEE_IP_MASK 0x07E0 /* DSP1DRC_KNEE_IP - [10:5] */
+#define WM8996_DSP1DRC_KNEE_IP_SHIFT 5 /* DSP1DRC_KNEE_IP - [10:5] */
+#define WM8996_DSP1DRC_KNEE_IP_WIDTH 6 /* DSP1DRC_KNEE_IP - [10:5] */
+#define WM8996_DSP1DRC_KNEE_OP_MASK 0x001F /* DSP1DRC_KNEE_OP - [4:0] */
+#define WM8996_DSP1DRC_KNEE_OP_SHIFT 0 /* DSP1DRC_KNEE_OP - [4:0] */
+#define WM8996_DSP1DRC_KNEE_OP_WIDTH 5 /* DSP1DRC_KNEE_OP - [4:0] */
+
+/*
+ * R1092 (0x444) - DSP1 DRC (5)
+ */
+#define WM8996_DSP1DRC_KNEE2_IP_MASK 0x03E0 /* DSP1DRC_KNEE2_IP - [9:5] */
+#define WM8996_DSP1DRC_KNEE2_IP_SHIFT 5 /* DSP1DRC_KNEE2_IP - [9:5] */
+#define WM8996_DSP1DRC_KNEE2_IP_WIDTH 5 /* DSP1DRC_KNEE2_IP - [9:5] */
+#define WM8996_DSP1DRC_KNEE2_OP_MASK 0x001F /* DSP1DRC_KNEE2_OP - [4:0] */
+#define WM8996_DSP1DRC_KNEE2_OP_SHIFT 0 /* DSP1DRC_KNEE2_OP - [4:0] */
+#define WM8996_DSP1DRC_KNEE2_OP_WIDTH 5 /* DSP1DRC_KNEE2_OP - [4:0] */
+
+/*
+ * R1152 (0x480) - DSP1 RX EQ Gains (1)
+ */
+#define WM8996_DSP1RX_EQ_B1_GAIN_MASK 0xF800 /* DSP1RX_EQ_B1_GAIN - [15:11] */
+#define WM8996_DSP1RX_EQ_B1_GAIN_SHIFT 11 /* DSP1RX_EQ_B1_GAIN - [15:11] */
+#define WM8996_DSP1RX_EQ_B1_GAIN_WIDTH 5 /* DSP1RX_EQ_B1_GAIN - [15:11] */
+#define WM8996_DSP1RX_EQ_B2_GAIN_MASK 0x07C0 /* DSP1RX_EQ_B2_GAIN - [10:6] */
+#define WM8996_DSP1RX_EQ_B2_GAIN_SHIFT 6 /* DSP1RX_EQ_B2_GAIN - [10:6] */
+#define WM8996_DSP1RX_EQ_B2_GAIN_WIDTH 5 /* DSP1RX_EQ_B2_GAIN - [10:6] */
+#define WM8996_DSP1RX_EQ_B3_GAIN_MASK 0x003E /* DSP1RX_EQ_B3_GAIN - [5:1] */
+#define WM8996_DSP1RX_EQ_B3_GAIN_SHIFT 1 /* DSP1RX_EQ_B3_GAIN - [5:1] */
+#define WM8996_DSP1RX_EQ_B3_GAIN_WIDTH 5 /* DSP1RX_EQ_B3_GAIN - [5:1] */
+#define WM8996_DSP1RX_EQ_ENA 0x0001 /* DSP1RX_EQ_ENA */
+#define WM8996_DSP1RX_EQ_ENA_MASK 0x0001 /* DSP1RX_EQ_ENA */
+#define WM8996_DSP1RX_EQ_ENA_SHIFT 0 /* DSP1RX_EQ_ENA */
+#define WM8996_DSP1RX_EQ_ENA_WIDTH 1 /* DSP1RX_EQ_ENA */
+
+/*
+ * R1153 (0x481) - DSP1 RX EQ Gains (2)
+ */
+#define WM8996_DSP1RX_EQ_B4_GAIN_MASK 0xF800 /* DSP1RX_EQ_B4_GAIN - [15:11] */
+#define WM8996_DSP1RX_EQ_B4_GAIN_SHIFT 11 /* DSP1RX_EQ_B4_GAIN - [15:11] */
+#define WM8996_DSP1RX_EQ_B4_GAIN_WIDTH 5 /* DSP1RX_EQ_B4_GAIN - [15:11] */
+#define WM8996_DSP1RX_EQ_B5_GAIN_MASK 0x07C0 /* DSP1RX_EQ_B5_GAIN - [10:6] */
+#define WM8996_DSP1RX_EQ_B5_GAIN_SHIFT 6 /* DSP1RX_EQ_B5_GAIN - [10:6] */
+#define WM8996_DSP1RX_EQ_B5_GAIN_WIDTH 5 /* DSP1RX_EQ_B5_GAIN - [10:6] */
+
+/*
+ * R1154 (0x482) - DSP1 RX EQ Band 1 A
+ */
+#define WM8996_DSP1RX_EQ_B1_A_MASK 0xFFFF /* DSP1RX_EQ_B1_A - [15:0] */
+#define WM8996_DSP1RX_EQ_B1_A_SHIFT 0 /* DSP1RX_EQ_B1_A - [15:0] */
+#define WM8996_DSP1RX_EQ_B1_A_WIDTH 16 /* DSP1RX_EQ_B1_A - [15:0] */
+
+/*
+ * R1155 (0x483) - DSP1 RX EQ Band 1 B
+ */
+#define WM8996_DSP1RX_EQ_B1_B_MASK 0xFFFF /* DSP1RX_EQ_B1_B - [15:0] */
+#define WM8996_DSP1RX_EQ_B1_B_SHIFT 0 /* DSP1RX_EQ_B1_B - [15:0] */
+#define WM8996_DSP1RX_EQ_B1_B_WIDTH 16 /* DSP1RX_EQ_B1_B - [15:0] */
+
+/*
+ * R1156 (0x484) - DSP1 RX EQ Band 1 PG
+ */
+#define WM8996_DSP1RX_EQ_B1_PG_MASK 0xFFFF /* DSP1RX_EQ_B1_PG - [15:0] */
+#define WM8996_DSP1RX_EQ_B1_PG_SHIFT 0 /* DSP1RX_EQ_B1_PG - [15:0] */
+#define WM8996_DSP1RX_EQ_B1_PG_WIDTH 16 /* DSP1RX_EQ_B1_PG - [15:0] */
+
+/*
+ * R1157 (0x485) - DSP1 RX EQ Band 2 A
+ */
+#define WM8996_DSP1RX_EQ_B2_A_MASK 0xFFFF /* DSP1RX_EQ_B2_A - [15:0] */
+#define WM8996_DSP1RX_EQ_B2_A_SHIFT 0 /* DSP1RX_EQ_B2_A - [15:0] */
+#define WM8996_DSP1RX_EQ_B2_A_WIDTH 16 /* DSP1RX_EQ_B2_A - [15:0] */
+
+/*
+ * R1158 (0x486) - DSP1 RX EQ Band 2 B
+ */
+#define WM8996_DSP1RX_EQ_B2_B_MASK 0xFFFF /* DSP1RX_EQ_B2_B - [15:0] */
+#define WM8996_DSP1RX_EQ_B2_B_SHIFT 0 /* DSP1RX_EQ_B2_B - [15:0] */
+#define WM8996_DSP1RX_EQ_B2_B_WIDTH 16 /* DSP1RX_EQ_B2_B - [15:0] */
+
+/*
+ * R1159 (0x487) - DSP1 RX EQ Band 2 C
+ */
+#define WM8996_DSP1RX_EQ_B2_C_MASK 0xFFFF /* DSP1RX_EQ_B2_C - [15:0] */
+#define WM8996_DSP1RX_EQ_B2_C_SHIFT 0 /* DSP1RX_EQ_B2_C - [15:0] */
+#define WM8996_DSP1RX_EQ_B2_C_WIDTH 16 /* DSP1RX_EQ_B2_C - [15:0] */
+
+/*
+ * R1160 (0x488) - DSP1 RX EQ Band 2 PG
+ */
+#define WM8996_DSP1RX_EQ_B2_PG_MASK 0xFFFF /* DSP1RX_EQ_B2_PG - [15:0] */
+#define WM8996_DSP1RX_EQ_B2_PG_SHIFT 0 /* DSP1RX_EQ_B2_PG - [15:0] */
+#define WM8996_DSP1RX_EQ_B2_PG_WIDTH 16 /* DSP1RX_EQ_B2_PG - [15:0] */
+
+/*
+ * R1161 (0x489) - DSP1 RX EQ Band 3 A
+ */
+#define WM8996_DSP1RX_EQ_B3_A_MASK 0xFFFF /* DSP1RX_EQ_B3_A - [15:0] */
+#define WM8996_DSP1RX_EQ_B3_A_SHIFT 0 /* DSP1RX_EQ_B3_A - [15:0] */
+#define WM8996_DSP1RX_EQ_B3_A_WIDTH 16 /* DSP1RX_EQ_B3_A - [15:0] */
+
+/*
+ * R1162 (0x48A) - DSP1 RX EQ Band 3 B
+ */
+#define WM8996_DSP1RX_EQ_B3_B_MASK 0xFFFF /* DSP1RX_EQ_B3_B - [15:0] */
+#define WM8996_DSP1RX_EQ_B3_B_SHIFT 0 /* DSP1RX_EQ_B3_B - [15:0] */
+#define WM8996_DSP1RX_EQ_B3_B_WIDTH 16 /* DSP1RX_EQ_B3_B - [15:0] */
+
+/*
+ * R1163 (0x48B) - DSP1 RX EQ Band 3 C
+ */
+#define WM8996_DSP1RX_EQ_B3_C_MASK 0xFFFF /* DSP1RX_EQ_B3_C - [15:0] */
+#define WM8996_DSP1RX_EQ_B3_C_SHIFT 0 /* DSP1RX_EQ_B3_C - [15:0] */
+#define WM8996_DSP1RX_EQ_B3_C_WIDTH 16 /* DSP1RX_EQ_B3_C - [15:0] */
+
+/*
+ * R1164 (0x48C) - DSP1 RX EQ Band 3 PG
+ */
+#define WM8996_DSP1RX_EQ_B3_PG_MASK 0xFFFF /* DSP1RX_EQ_B3_PG - [15:0] */
+#define WM8996_DSP1RX_EQ_B3_PG_SHIFT 0 /* DSP1RX_EQ_B3_PG - [15:0] */
+#define WM8996_DSP1RX_EQ_B3_PG_WIDTH 16 /* DSP1RX_EQ_B3_PG - [15:0] */
+
+/*
+ * R1165 (0x48D) - DSP1 RX EQ Band 4 A
+ */
+#define WM8996_DSP1RX_EQ_B4_A_MASK 0xFFFF /* DSP1RX_EQ_B4_A - [15:0] */
+#define WM8996_DSP1RX_EQ_B4_A_SHIFT 0 /* DSP1RX_EQ_B4_A - [15:0] */
+#define WM8996_DSP1RX_EQ_B4_A_WIDTH 16 /* DSP1RX_EQ_B4_A - [15:0] */
+
+/*
+ * R1166 (0x48E) - DSP1 RX EQ Band 4 B
+ */
+#define WM8996_DSP1RX_EQ_B4_B_MASK 0xFFFF /* DSP1RX_EQ_B4_B - [15:0] */
+#define WM8996_DSP1RX_EQ_B4_B_SHIFT 0 /* DSP1RX_EQ_B4_B - [15:0] */
+#define WM8996_DSP1RX_EQ_B4_B_WIDTH 16 /* DSP1RX_EQ_B4_B - [15:0] */
+
+/*
+ * R1167 (0x48F) - DSP1 RX EQ Band 4 C
+ */
+#define WM8996_DSP1RX_EQ_B4_C_MASK 0xFFFF /* DSP1RX_EQ_B4_C - [15:0] */
+#define WM8996_DSP1RX_EQ_B4_C_SHIFT 0 /* DSP1RX_EQ_B4_C - [15:0] */
+#define WM8996_DSP1RX_EQ_B4_C_WIDTH 16 /* DSP1RX_EQ_B4_C - [15:0] */
+
+/*
+ * R1168 (0x490) - DSP1 RX EQ Band 4 PG
+ */
+#define WM8996_DSP1RX_EQ_B4_PG_MASK 0xFFFF /* DSP1RX_EQ_B4_PG - [15:0] */
+#define WM8996_DSP1RX_EQ_B4_PG_SHIFT 0 /* DSP1RX_EQ_B4_PG - [15:0] */
+#define WM8996_DSP1RX_EQ_B4_PG_WIDTH 16 /* DSP1RX_EQ_B4_PG - [15:0] */
+
+/*
+ * R1169 (0x491) - DSP1 RX EQ Band 5 A
+ */
+#define WM8996_DSP1RX_EQ_B5_A_MASK 0xFFFF /* DSP1RX_EQ_B5_A - [15:0] */
+#define WM8996_DSP1RX_EQ_B5_A_SHIFT 0 /* DSP1RX_EQ_B5_A - [15:0] */
+#define WM8996_DSP1RX_EQ_B5_A_WIDTH 16 /* DSP1RX_EQ_B5_A - [15:0] */
+
+/*
+ * R1170 (0x492) - DSP1 RX EQ Band 5 B
+ */
+#define WM8996_DSP1RX_EQ_B5_B_MASK 0xFFFF /* DSP1RX_EQ_B5_B - [15:0] */
+#define WM8996_DSP1RX_EQ_B5_B_SHIFT 0 /* DSP1RX_EQ_B5_B - [15:0] */
+#define WM8996_DSP1RX_EQ_B5_B_WIDTH 16 /* DSP1RX_EQ_B5_B - [15:0] */
+
+/*
+ * R1171 (0x493) - DSP1 RX EQ Band 5 PG
+ */
+#define WM8996_DSP1RX_EQ_B5_PG_MASK 0xFFFF /* DSP1RX_EQ_B5_PG - [15:0] */
+#define WM8996_DSP1RX_EQ_B5_PG_SHIFT 0 /* DSP1RX_EQ_B5_PG - [15:0] */
+#define WM8996_DSP1RX_EQ_B5_PG_WIDTH 16 /* DSP1RX_EQ_B5_PG - [15:0] */
+
+/*
+ * R1280 (0x500) - DSP2 TX Left Volume
+ */
+#define WM8996_DSP2TX_VU 0x0100 /* DSP2TX_VU */
+#define WM8996_DSP2TX_VU_MASK 0x0100 /* DSP2TX_VU */
+#define WM8996_DSP2TX_VU_SHIFT 8 /* DSP2TX_VU */
+#define WM8996_DSP2TX_VU_WIDTH 1 /* DSP2TX_VU */
+#define WM8996_DSP2TXL_VOL_MASK 0x00FF /* DSP2TXL_VOL - [7:0] */
+#define WM8996_DSP2TXL_VOL_SHIFT 0 /* DSP2TXL_VOL - [7:0] */
+#define WM8996_DSP2TXL_VOL_WIDTH 8 /* DSP2TXL_VOL - [7:0] */
+
+/*
+ * R1281 (0x501) - DSP2 TX Right Volume
+ */
+#define WM8996_DSP2TX_VU 0x0100 /* DSP2TX_VU */
+#define WM8996_DSP2TX_VU_MASK 0x0100 /* DSP2TX_VU */
+#define WM8996_DSP2TX_VU_SHIFT 8 /* DSP2TX_VU */
+#define WM8996_DSP2TX_VU_WIDTH 1 /* DSP2TX_VU */
+#define WM8996_DSP2TXR_VOL_MASK 0x00FF /* DSP2TXR_VOL - [7:0] */
+#define WM8996_DSP2TXR_VOL_SHIFT 0 /* DSP2TXR_VOL - [7:0] */
+#define WM8996_DSP2TXR_VOL_WIDTH 8 /* DSP2TXR_VOL - [7:0] */
+
+/*
+ * R1282 (0x502) - DSP2 RX Left Volume
+ */
+#define WM8996_DSP2RX_VU 0x0100 /* DSP2RX_VU */
+#define WM8996_DSP2RX_VU_MASK 0x0100 /* DSP2RX_VU */
+#define WM8996_DSP2RX_VU_SHIFT 8 /* DSP2RX_VU */
+#define WM8996_DSP2RX_VU_WIDTH 1 /* DSP2RX_VU */
+#define WM8996_DSP2RXL_VOL_MASK 0x00FF /* DSP2RXL_VOL - [7:0] */
+#define WM8996_DSP2RXL_VOL_SHIFT 0 /* DSP2RXL_VOL - [7:0] */
+#define WM8996_DSP2RXL_VOL_WIDTH 8 /* DSP2RXL_VOL - [7:0] */
+
+/*
+ * R1283 (0x503) - DSP2 RX Right Volume
+ */
+#define WM8996_DSP2RX_VU 0x0100 /* DSP2RX_VU */
+#define WM8996_DSP2RX_VU_MASK 0x0100 /* DSP2RX_VU */
+#define WM8996_DSP2RX_VU_SHIFT 8 /* DSP2RX_VU */
+#define WM8996_DSP2RX_VU_WIDTH 1 /* DSP2RX_VU */
+#define WM8996_DSP2RXR_VOL_MASK 0x00FF /* DSP2RXR_VOL - [7:0] */
+#define WM8996_DSP2RXR_VOL_SHIFT 0 /* DSP2RXR_VOL - [7:0] */
+#define WM8996_DSP2RXR_VOL_WIDTH 8 /* DSP2RXR_VOL - [7:0] */
+
+/*
+ * R1296 (0x510) - DSP2 TX Filters
+ */
+#define WM8996_DSP2TX_NF 0x2000 /* DSP2TX_NF */
+#define WM8996_DSP2TX_NF_MASK 0x2000 /* DSP2TX_NF */
+#define WM8996_DSP2TX_NF_SHIFT 13 /* DSP2TX_NF */
+#define WM8996_DSP2TX_NF_WIDTH 1 /* DSP2TX_NF */
+#define WM8996_DSP2TXL_HPF 0x1000 /* DSP2TXL_HPF */
+#define WM8996_DSP2TXL_HPF_MASK 0x1000 /* DSP2TXL_HPF */
+#define WM8996_DSP2TXL_HPF_SHIFT 12 /* DSP2TXL_HPF */
+#define WM8996_DSP2TXL_HPF_WIDTH 1 /* DSP2TXL_HPF */
+#define WM8996_DSP2TXR_HPF 0x0800 /* DSP2TXR_HPF */
+#define WM8996_DSP2TXR_HPF_MASK 0x0800 /* DSP2TXR_HPF */
+#define WM8996_DSP2TXR_HPF_SHIFT 11 /* DSP2TXR_HPF */
+#define WM8996_DSP2TXR_HPF_WIDTH 1 /* DSP2TXR_HPF */
+#define WM8996_DSP2TX_HPF_MODE_MASK 0x0018 /* DSP2TX_HPF_MODE - [4:3] */
+#define WM8996_DSP2TX_HPF_MODE_SHIFT 3 /* DSP2TX_HPF_MODE - [4:3] */
+#define WM8996_DSP2TX_HPF_MODE_WIDTH 2 /* DSP2TX_HPF_MODE - [4:3] */
+#define WM8996_DSP2TX_HPF_CUT_MASK 0x0007 /* DSP2TX_HPF_CUT - [2:0] */
+#define WM8996_DSP2TX_HPF_CUT_SHIFT 0 /* DSP2TX_HPF_CUT - [2:0] */
+#define WM8996_DSP2TX_HPF_CUT_WIDTH 3 /* DSP2TX_HPF_CUT - [2:0] */
+
+/*
+ * R1312 (0x520) - DSP2 RX Filters (1)
+ */
+#define WM8996_DSP2RX_MUTE 0x0200 /* DSP2RX_MUTE */
+#define WM8996_DSP2RX_MUTE_MASK 0x0200 /* DSP2RX_MUTE */
+#define WM8996_DSP2RX_MUTE_SHIFT 9 /* DSP2RX_MUTE */
+#define WM8996_DSP2RX_MUTE_WIDTH 1 /* DSP2RX_MUTE */
+#define WM8996_DSP2RX_MONO 0x0080 /* DSP2RX_MONO */
+#define WM8996_DSP2RX_MONO_MASK 0x0080 /* DSP2RX_MONO */
+#define WM8996_DSP2RX_MONO_SHIFT 7 /* DSP2RX_MONO */
+#define WM8996_DSP2RX_MONO_WIDTH 1 /* DSP2RX_MONO */
+#define WM8996_DSP2RX_MUTERATE 0x0020 /* DSP2RX_MUTERATE */
+#define WM8996_DSP2RX_MUTERATE_MASK 0x0020 /* DSP2RX_MUTERATE */
+#define WM8996_DSP2RX_MUTERATE_SHIFT 5 /* DSP2RX_MUTERATE */
+#define WM8996_DSP2RX_MUTERATE_WIDTH 1 /* DSP2RX_MUTERATE */
+#define WM8996_DSP2RX_UNMUTE_RAMP 0x0010 /* DSP2RX_UNMUTE_RAMP */
+#define WM8996_DSP2RX_UNMUTE_RAMP_MASK 0x0010 /* DSP2RX_UNMUTE_RAMP */
+#define WM8996_DSP2RX_UNMUTE_RAMP_SHIFT 4 /* DSP2RX_UNMUTE_RAMP */
+#define WM8996_DSP2RX_UNMUTE_RAMP_WIDTH 1 /* DSP2RX_UNMUTE_RAMP */
+
+/*
+ * R1313 (0x521) - DSP2 RX Filters (2)
+ */
+#define WM8996_DSP2RX_3D_GAIN_MASK 0x3E00 /* DSP2RX_3D_GAIN - [13:9] */
+#define WM8996_DSP2RX_3D_GAIN_SHIFT 9 /* DSP2RX_3D_GAIN - [13:9] */
+#define WM8996_DSP2RX_3D_GAIN_WIDTH 5 /* DSP2RX_3D_GAIN - [13:9] */
+#define WM8996_DSP2RX_3D_ENA 0x0100 /* DSP2RX_3D_ENA */
+#define WM8996_DSP2RX_3D_ENA_MASK 0x0100 /* DSP2RX_3D_ENA */
+#define WM8996_DSP2RX_3D_ENA_SHIFT 8 /* DSP2RX_3D_ENA */
+#define WM8996_DSP2RX_3D_ENA_WIDTH 1 /* DSP2RX_3D_ENA */
+
+/*
+ * R1344 (0x540) - DSP2 DRC (1)
+ */
+#define WM8996_DSP2DRC_SIG_DET_RMS_MASK 0xF800 /* DSP2DRC_SIG_DET_RMS - [15:11] */
+#define WM8996_DSP2DRC_SIG_DET_RMS_SHIFT 11 /* DSP2DRC_SIG_DET_RMS - [15:11] */
+#define WM8996_DSP2DRC_SIG_DET_RMS_WIDTH 5 /* DSP2DRC_SIG_DET_RMS - [15:11] */
+#define WM8996_DSP2DRC_SIG_DET_PK_MASK 0x0600 /* DSP2DRC_SIG_DET_PK - [10:9] */
+#define WM8996_DSP2DRC_SIG_DET_PK_SHIFT 9 /* DSP2DRC_SIG_DET_PK - [10:9] */
+#define WM8996_DSP2DRC_SIG_DET_PK_WIDTH 2 /* DSP2DRC_SIG_DET_PK - [10:9] */
+#define WM8996_DSP2DRC_NG_ENA 0x0100 /* DSP2DRC_NG_ENA */
+#define WM8996_DSP2DRC_NG_ENA_MASK 0x0100 /* DSP2DRC_NG_ENA */
+#define WM8996_DSP2DRC_NG_ENA_SHIFT 8 /* DSP2DRC_NG_ENA */
+#define WM8996_DSP2DRC_NG_ENA_WIDTH 1 /* DSP2DRC_NG_ENA */
+#define WM8996_DSP2DRC_SIG_DET_MODE 0x0080 /* DSP2DRC_SIG_DET_MODE */
+#define WM8996_DSP2DRC_SIG_DET_MODE_MASK 0x0080 /* DSP2DRC_SIG_DET_MODE */
+#define WM8996_DSP2DRC_SIG_DET_MODE_SHIFT 7 /* DSP2DRC_SIG_DET_MODE */
+#define WM8996_DSP2DRC_SIG_DET_MODE_WIDTH 1 /* DSP2DRC_SIG_DET_MODE */
+#define WM8996_DSP2DRC_SIG_DET 0x0040 /* DSP2DRC_SIG_DET */
+#define WM8996_DSP2DRC_SIG_DET_MASK 0x0040 /* DSP2DRC_SIG_DET */
+#define WM8996_DSP2DRC_SIG_DET_SHIFT 6 /* DSP2DRC_SIG_DET */
+#define WM8996_DSP2DRC_SIG_DET_WIDTH 1 /* DSP2DRC_SIG_DET */
+#define WM8996_DSP2DRC_KNEE2_OP_ENA 0x0020 /* DSP2DRC_KNEE2_OP_ENA */
+#define WM8996_DSP2DRC_KNEE2_OP_ENA_MASK 0x0020 /* DSP2DRC_KNEE2_OP_ENA */
+#define WM8996_DSP2DRC_KNEE2_OP_ENA_SHIFT 5 /* DSP2DRC_KNEE2_OP_ENA */
+#define WM8996_DSP2DRC_KNEE2_OP_ENA_WIDTH 1 /* DSP2DRC_KNEE2_OP_ENA */
+#define WM8996_DSP2DRC_QR 0x0010 /* DSP2DRC_QR */
+#define WM8996_DSP2DRC_QR_MASK 0x0010 /* DSP2DRC_QR */
+#define WM8996_DSP2DRC_QR_SHIFT 4 /* DSP2DRC_QR */
+#define WM8996_DSP2DRC_QR_WIDTH 1 /* DSP2DRC_QR */
+#define WM8996_DSP2DRC_ANTICLIP 0x0008 /* DSP2DRC_ANTICLIP */
+#define WM8996_DSP2DRC_ANTICLIP_MASK 0x0008 /* DSP2DRC_ANTICLIP */
+#define WM8996_DSP2DRC_ANTICLIP_SHIFT 3 /* DSP2DRC_ANTICLIP */
+#define WM8996_DSP2DRC_ANTICLIP_WIDTH 1 /* DSP2DRC_ANTICLIP */
+#define WM8996_DSP2RX_DRC_ENA 0x0004 /* DSP2RX_DRC_ENA */
+#define WM8996_DSP2RX_DRC_ENA_MASK 0x0004 /* DSP2RX_DRC_ENA */
+#define WM8996_DSP2RX_DRC_ENA_SHIFT 2 /* DSP2RX_DRC_ENA */
+#define WM8996_DSP2RX_DRC_ENA_WIDTH 1 /* DSP2RX_DRC_ENA */
+#define WM8996_DSP2TXL_DRC_ENA 0x0002 /* DSP2TXL_DRC_ENA */
+#define WM8996_DSP2TXL_DRC_ENA_MASK 0x0002 /* DSP2TXL_DRC_ENA */
+#define WM8996_DSP2TXL_DRC_ENA_SHIFT 1 /* DSP2TXL_DRC_ENA */
+#define WM8996_DSP2TXL_DRC_ENA_WIDTH 1 /* DSP2TXL_DRC_ENA */
+#define WM8996_DSP2TXR_DRC_ENA 0x0001 /* DSP2TXR_DRC_ENA */
+#define WM8996_DSP2TXR_DRC_ENA_MASK 0x0001 /* DSP2TXR_DRC_ENA */
+#define WM8996_DSP2TXR_DRC_ENA_SHIFT 0 /* DSP2TXR_DRC_ENA */
+#define WM8996_DSP2TXR_DRC_ENA_WIDTH 1 /* DSP2TXR_DRC_ENA */
+
+/*
+ * R1345 (0x541) - DSP2 DRC (2)
+ */
+#define WM8996_DSP2DRC_ATK_MASK 0x1E00 /* DSP2DRC_ATK - [12:9] */
+#define WM8996_DSP2DRC_ATK_SHIFT 9 /* DSP2DRC_ATK - [12:9] */
+#define WM8996_DSP2DRC_ATK_WIDTH 4 /* DSP2DRC_ATK - [12:9] */
+#define WM8996_DSP2DRC_DCY_MASK 0x01E0 /* DSP2DRC_DCY - [8:5] */
+#define WM8996_DSP2DRC_DCY_SHIFT 5 /* DSP2DRC_DCY - [8:5] */
+#define WM8996_DSP2DRC_DCY_WIDTH 4 /* DSP2DRC_DCY - [8:5] */
+#define WM8996_DSP2DRC_MINGAIN_MASK 0x001C /* DSP2DRC_MINGAIN - [4:2] */
+#define WM8996_DSP2DRC_MINGAIN_SHIFT 2 /* DSP2DRC_MINGAIN - [4:2] */
+#define WM8996_DSP2DRC_MINGAIN_WIDTH 3 /* DSP2DRC_MINGAIN - [4:2] */
+#define WM8996_DSP2DRC_MAXGAIN_MASK 0x0003 /* DSP2DRC_MAXGAIN - [1:0] */
+#define WM8996_DSP2DRC_MAXGAIN_SHIFT 0 /* DSP2DRC_MAXGAIN - [1:0] */
+#define WM8996_DSP2DRC_MAXGAIN_WIDTH 2 /* DSP2DRC_MAXGAIN - [1:0] */
+
+/*
+ * R1346 (0x542) - DSP2 DRC (3)
+ */
+#define WM8996_DSP2DRC_NG_MINGAIN_MASK 0xF000 /* DSP2DRC_NG_MINGAIN - [15:12] */
+#define WM8996_DSP2DRC_NG_MINGAIN_SHIFT 12 /* DSP2DRC_NG_MINGAIN - [15:12] */
+#define WM8996_DSP2DRC_NG_MINGAIN_WIDTH 4 /* DSP2DRC_NG_MINGAIN - [15:12] */
+#define WM8996_DSP2DRC_NG_EXP_MASK 0x0C00 /* DSP2DRC_NG_EXP - [11:10] */
+#define WM8996_DSP2DRC_NG_EXP_SHIFT 10 /* DSP2DRC_NG_EXP - [11:10] */
+#define WM8996_DSP2DRC_NG_EXP_WIDTH 2 /* DSP2DRC_NG_EXP - [11:10] */
+#define WM8996_DSP2DRC_QR_THR_MASK 0x0300 /* DSP2DRC_QR_THR - [9:8] */
+#define WM8996_DSP2DRC_QR_THR_SHIFT 8 /* DSP2DRC_QR_THR - [9:8] */
+#define WM8996_DSP2DRC_QR_THR_WIDTH 2 /* DSP2DRC_QR_THR - [9:8] */
+#define WM8996_DSP2DRC_QR_DCY_MASK 0x00C0 /* DSP2DRC_QR_DCY - [7:6] */
+#define WM8996_DSP2DRC_QR_DCY_SHIFT 6 /* DSP2DRC_QR_DCY - [7:6] */
+#define WM8996_DSP2DRC_QR_DCY_WIDTH 2 /* DSP2DRC_QR_DCY - [7:6] */
+#define WM8996_DSP2DRC_HI_COMP_MASK 0x0038 /* DSP2DRC_HI_COMP - [5:3] */
+#define WM8996_DSP2DRC_HI_COMP_SHIFT 3 /* DSP2DRC_HI_COMP - [5:3] */
+#define WM8996_DSP2DRC_HI_COMP_WIDTH 3 /* DSP2DRC_HI_COMP - [5:3] */
+#define WM8996_DSP2DRC_LO_COMP_MASK 0x0007 /* DSP2DRC_LO_COMP - [2:0] */
+#define WM8996_DSP2DRC_LO_COMP_SHIFT 0 /* DSP2DRC_LO_COMP - [2:0] */
+#define WM8996_DSP2DRC_LO_COMP_WIDTH 3 /* DSP2DRC_LO_COMP - [2:0] */
+
+/*
+ * R1347 (0x543) - DSP2 DRC (4)
+ */
+#define WM8996_DSP2DRC_KNEE_IP_MASK 0x07E0 /* DSP2DRC_KNEE_IP - [10:5] */
+#define WM8996_DSP2DRC_KNEE_IP_SHIFT 5 /* DSP2DRC_KNEE_IP - [10:5] */
+#define WM8996_DSP2DRC_KNEE_IP_WIDTH 6 /* DSP2DRC_KNEE_IP - [10:5] */
+#define WM8996_DSP2DRC_KNEE_OP_MASK 0x001F /* DSP2DRC_KNEE_OP - [4:0] */
+#define WM8996_DSP2DRC_KNEE_OP_SHIFT 0 /* DSP2DRC_KNEE_OP - [4:0] */
+#define WM8996_DSP2DRC_KNEE_OP_WIDTH 5 /* DSP2DRC_KNEE_OP - [4:0] */
+
+/*
+ * R1348 (0x544) - DSP2 DRC (5)
+ */
+#define WM8996_DSP2DRC_KNEE2_IP_MASK 0x03E0 /* DSP2DRC_KNEE2_IP - [9:5] */
+#define WM8996_DSP2DRC_KNEE2_IP_SHIFT 5 /* DSP2DRC_KNEE2_IP - [9:5] */
+#define WM8996_DSP2DRC_KNEE2_IP_WIDTH 5 /* DSP2DRC_KNEE2_IP - [9:5] */
+#define WM8996_DSP2DRC_KNEE2_OP_MASK 0x001F /* DSP2DRC_KNEE2_OP - [4:0] */
+#define WM8996_DSP2DRC_KNEE2_OP_SHIFT 0 /* DSP2DRC_KNEE2_OP - [4:0] */
+#define WM8996_DSP2DRC_KNEE2_OP_WIDTH 5 /* DSP2DRC_KNEE2_OP - [4:0] */
+
+/*
+ * R1408 (0x580) - DSP2 RX EQ Gains (1)
+ */
+#define WM8996_DSP2RX_EQ_B1_GAIN_MASK 0xF800 /* DSP2RX_EQ_B1_GAIN - [15:11] */
+#define WM8996_DSP2RX_EQ_B1_GAIN_SHIFT 11 /* DSP2RX_EQ_B1_GAIN - [15:11] */
+#define WM8996_DSP2RX_EQ_B1_GAIN_WIDTH 5 /* DSP2RX_EQ_B1_GAIN - [15:11] */
+#define WM8996_DSP2RX_EQ_B2_GAIN_MASK 0x07C0 /* DSP2RX_EQ_B2_GAIN - [10:6] */
+#define WM8996_DSP2RX_EQ_B2_GAIN_SHIFT 6 /* DSP2RX_EQ_B2_GAIN - [10:6] */
+#define WM8996_DSP2RX_EQ_B2_GAIN_WIDTH 5 /* DSP2RX_EQ_B2_GAIN - [10:6] */
+#define WM8996_DSP2RX_EQ_B3_GAIN_MASK 0x003E /* DSP2RX_EQ_B3_GAIN - [5:1] */
+#define WM8996_DSP2RX_EQ_B3_GAIN_SHIFT 1 /* DSP2RX_EQ_B3_GAIN - [5:1] */
+#define WM8996_DSP2RX_EQ_B3_GAIN_WIDTH 5 /* DSP2RX_EQ_B3_GAIN - [5:1] */
+#define WM8996_DSP2RX_EQ_ENA 0x0001 /* DSP2RX_EQ_ENA */
+#define WM8996_DSP2RX_EQ_ENA_MASK 0x0001 /* DSP2RX_EQ_ENA */
+#define WM8996_DSP2RX_EQ_ENA_SHIFT 0 /* DSP2RX_EQ_ENA */
+#define WM8996_DSP2RX_EQ_ENA_WIDTH 1 /* DSP2RX_EQ_ENA */
+
+/*
+ * R1409 (0x581) - DSP2 RX EQ Gains (2)
+ */
+#define WM8996_DSP2RX_EQ_B4_GAIN_MASK 0xF800 /* DSP2RX_EQ_B4_GAIN - [15:11] */
+#define WM8996_DSP2RX_EQ_B4_GAIN_SHIFT 11 /* DSP2RX_EQ_B4_GAIN - [15:11] */
+#define WM8996_DSP2RX_EQ_B4_GAIN_WIDTH 5 /* DSP2RX_EQ_B4_GAIN - [15:11] */
+#define WM8996_DSP2RX_EQ_B5_GAIN_MASK 0x07C0 /* DSP2RX_EQ_B5_GAIN - [10:6] */
+#define WM8996_DSP2RX_EQ_B5_GAIN_SHIFT 6 /* DSP2RX_EQ_B5_GAIN - [10:6] */
+#define WM8996_DSP2RX_EQ_B5_GAIN_WIDTH 5 /* DSP2RX_EQ_B5_GAIN - [10:6] */
+
+/*
+ * R1410 (0x582) - DSP2 RX EQ Band 1 A
+ */
+#define WM8996_DSP2RX_EQ_B1_A_MASK 0xFFFF /* DSP2RX_EQ_B1_A - [15:0] */
+#define WM8996_DSP2RX_EQ_B1_A_SHIFT 0 /* DSP2RX_EQ_B1_A - [15:0] */
+#define WM8996_DSP2RX_EQ_B1_A_WIDTH 16 /* DSP2RX_EQ_B1_A - [15:0] */
+
+/*
+ * R1411 (0x583) - DSP2 RX EQ Band 1 B
+ */
+#define WM8996_DSP2RX_EQ_B1_B_MASK 0xFFFF /* DSP2RX_EQ_B1_B - [15:0] */
+#define WM8996_DSP2RX_EQ_B1_B_SHIFT 0 /* DSP2RX_EQ_B1_B - [15:0] */
+#define WM8996_DSP2RX_EQ_B1_B_WIDTH 16 /* DSP2RX_EQ_B1_B - [15:0] */
+
+/*
+ * R1412 (0x584) - DSP2 RX EQ Band 1 PG
+ */
+#define WM8996_DSP2RX_EQ_B1_PG_MASK 0xFFFF /* DSP2RX_EQ_B1_PG - [15:0] */
+#define WM8996_DSP2RX_EQ_B1_PG_SHIFT 0 /* DSP2RX_EQ_B1_PG - [15:0] */
+#define WM8996_DSP2RX_EQ_B1_PG_WIDTH 16 /* DSP2RX_EQ_B1_PG - [15:0] */
+
+/*
+ * R1413 (0x585) - DSP2 RX EQ Band 2 A
+ */
+#define WM8996_DSP2RX_EQ_B2_A_MASK 0xFFFF /* DSP2RX_EQ_B2_A - [15:0] */
+#define WM8996_DSP2RX_EQ_B2_A_SHIFT 0 /* DSP2RX_EQ_B2_A - [15:0] */
+#define WM8996_DSP2RX_EQ_B2_A_WIDTH 16 /* DSP2RX_EQ_B2_A - [15:0] */
+
+/*
+ * R1414 (0x586) - DSP2 RX EQ Band 2 B
+ */
+#define WM8996_DSP2RX_EQ_B2_B_MASK 0xFFFF /* DSP2RX_EQ_B2_B - [15:0] */
+#define WM8996_DSP2RX_EQ_B2_B_SHIFT 0 /* DSP2RX_EQ_B2_B - [15:0] */
+#define WM8996_DSP2RX_EQ_B2_B_WIDTH 16 /* DSP2RX_EQ_B2_B - [15:0] */
+
+/*
+ * R1415 (0x587) - DSP2 RX EQ Band 2 C
+ */
+#define WM8996_DSP2RX_EQ_B2_C_MASK 0xFFFF /* DSP2RX_EQ_B2_C - [15:0] */
+#define WM8996_DSP2RX_EQ_B2_C_SHIFT 0 /* DSP2RX_EQ_B2_C - [15:0] */
+#define WM8996_DSP2RX_EQ_B2_C_WIDTH 16 /* DSP2RX_EQ_B2_C - [15:0] */
+
+/*
+ * R1416 (0x588) - DSP2 RX EQ Band 2 PG
+ */
+#define WM8996_DSP2RX_EQ_B2_PG_MASK 0xFFFF /* DSP2RX_EQ_B2_PG - [15:0] */
+#define WM8996_DSP2RX_EQ_B2_PG_SHIFT 0 /* DSP2RX_EQ_B2_PG - [15:0] */
+#define WM8996_DSP2RX_EQ_B2_PG_WIDTH 16 /* DSP2RX_EQ_B2_PG - [15:0] */
+
+/*
+ * R1417 (0x589) - DSP2 RX EQ Band 3 A
+ */
+#define WM8996_DSP2RX_EQ_B3_A_MASK 0xFFFF /* DSP2RX_EQ_B3_A - [15:0] */
+#define WM8996_DSP2RX_EQ_B3_A_SHIFT 0 /* DSP2RX_EQ_B3_A - [15:0] */
+#define WM8996_DSP2RX_EQ_B3_A_WIDTH 16 /* DSP2RX_EQ_B3_A - [15:0] */
+
+/*
+ * R1418 (0x58A) - DSP2 RX EQ Band 3 B
+ */
+#define WM8996_DSP2RX_EQ_B3_B_MASK 0xFFFF /* DSP2RX_EQ_B3_B - [15:0] */
+#define WM8996_DSP2RX_EQ_B3_B_SHIFT 0 /* DSP2RX_EQ_B3_B - [15:0] */
+#define WM8996_DSP2RX_EQ_B3_B_WIDTH 16 /* DSP2RX_EQ_B3_B - [15:0] */
+
+/*
+ * R1419 (0x58B) - DSP2 RX EQ Band 3 C
+ */
+#define WM8996_DSP2RX_EQ_B3_C_MASK 0xFFFF /* DSP2RX_EQ_B3_C - [15:0] */
+#define WM8996_DSP2RX_EQ_B3_C_SHIFT 0 /* DSP2RX_EQ_B3_C - [15:0] */
+#define WM8996_DSP2RX_EQ_B3_C_WIDTH 16 /* DSP2RX_EQ_B3_C - [15:0] */
+
+/*
+ * R1420 (0x58C) - DSP2 RX EQ Band 3 PG
+ */
+#define WM8996_DSP2RX_EQ_B3_PG_MASK 0xFFFF /* DSP2RX_EQ_B3_PG - [15:0] */
+#define WM8996_DSP2RX_EQ_B3_PG_SHIFT 0 /* DSP2RX_EQ_B3_PG - [15:0] */
+#define WM8996_DSP2RX_EQ_B3_PG_WIDTH 16 /* DSP2RX_EQ_B3_PG - [15:0] */
+
+/*
+ * R1421 (0x58D) - DSP2 RX EQ Band 4 A
+ */
+#define WM8996_DSP2RX_EQ_B4_A_MASK 0xFFFF /* DSP2RX_EQ_B4_A - [15:0] */
+#define WM8996_DSP2RX_EQ_B4_A_SHIFT 0 /* DSP2RX_EQ_B4_A - [15:0] */
+#define WM8996_DSP2RX_EQ_B4_A_WIDTH 16 /* DSP2RX_EQ_B4_A - [15:0] */
+
+/*
+ * R1422 (0x58E) - DSP2 RX EQ Band 4 B
+ */
+#define WM8996_DSP2RX_EQ_B4_B_MASK 0xFFFF /* DSP2RX_EQ_B4_B - [15:0] */
+#define WM8996_DSP2RX_EQ_B4_B_SHIFT 0 /* DSP2RX_EQ_B4_B - [15:0] */
+#define WM8996_DSP2RX_EQ_B4_B_WIDTH 16 /* DSP2RX_EQ_B4_B - [15:0] */
+
+/*
+ * R1423 (0x58F) - DSP2 RX EQ Band 4 C
+ */
+#define WM8996_DSP2RX_EQ_B4_C_MASK 0xFFFF /* DSP2RX_EQ_B4_C - [15:0] */
+#define WM8996_DSP2RX_EQ_B4_C_SHIFT 0 /* DSP2RX_EQ_B4_C - [15:0] */
+#define WM8996_DSP2RX_EQ_B4_C_WIDTH 16 /* DSP2RX_EQ_B4_C - [15:0] */
+
+/*
+ * R1424 (0x590) - DSP2 RX EQ Band 4 PG
+ */
+#define WM8996_DSP2RX_EQ_B4_PG_MASK 0xFFFF /* DSP2RX_EQ_B4_PG - [15:0] */
+#define WM8996_DSP2RX_EQ_B4_PG_SHIFT 0 /* DSP2RX_EQ_B4_PG - [15:0] */
+#define WM8996_DSP2RX_EQ_B4_PG_WIDTH 16 /* DSP2RX_EQ_B4_PG - [15:0] */
+
+/*
+ * R1425 (0x591) - DSP2 RX EQ Band 5 A
+ */
+#define WM8996_DSP2RX_EQ_B5_A_MASK 0xFFFF /* DSP2RX_EQ_B5_A - [15:0] */
+#define WM8996_DSP2RX_EQ_B5_A_SHIFT 0 /* DSP2RX_EQ_B5_A - [15:0] */
+#define WM8996_DSP2RX_EQ_B5_A_WIDTH 16 /* DSP2RX_EQ_B5_A - [15:0] */
+
+/*
+ * R1426 (0x592) - DSP2 RX EQ Band 5 B
+ */
+#define WM8996_DSP2RX_EQ_B5_B_MASK 0xFFFF /* DSP2RX_EQ_B5_B - [15:0] */
+#define WM8996_DSP2RX_EQ_B5_B_SHIFT 0 /* DSP2RX_EQ_B5_B - [15:0] */
+#define WM8996_DSP2RX_EQ_B5_B_WIDTH 16 /* DSP2RX_EQ_B5_B - [15:0] */
+
+/*
+ * R1427 (0x593) - DSP2 RX EQ Band 5 PG
+ */
+#define WM8996_DSP2RX_EQ_B5_PG_MASK 0xFFFF /* DSP2RX_EQ_B5_PG - [15:0] */
+#define WM8996_DSP2RX_EQ_B5_PG_SHIFT 0 /* DSP2RX_EQ_B5_PG - [15:0] */
+#define WM8996_DSP2RX_EQ_B5_PG_WIDTH 16 /* DSP2RX_EQ_B5_PG - [15:0] */
+
+/*
+ * R1536 (0x600) - DAC1 Mixer Volumes
+ */
+#define WM8996_ADCR_DAC1_VOL_MASK 0x03E0 /* ADCR_DAC1_VOL - [9:5] */
+#define WM8996_ADCR_DAC1_VOL_SHIFT 5 /* ADCR_DAC1_VOL - [9:5] */
+#define WM8996_ADCR_DAC1_VOL_WIDTH 5 /* ADCR_DAC1_VOL - [9:5] */
+#define WM8996_ADCL_DAC1_VOL_MASK 0x001F /* ADCL_DAC1_VOL - [4:0] */
+#define WM8996_ADCL_DAC1_VOL_SHIFT 0 /* ADCL_DAC1_VOL - [4:0] */
+#define WM8996_ADCL_DAC1_VOL_WIDTH 5 /* ADCL_DAC1_VOL - [4:0] */
+
+/*
+ * R1537 (0x601) - DAC1 Left Mixer Routing
+ */
+#define WM8996_ADCR_TO_DAC1L 0x0020 /* ADCR_TO_DAC1L */
+#define WM8996_ADCR_TO_DAC1L_MASK 0x0020 /* ADCR_TO_DAC1L */
+#define WM8996_ADCR_TO_DAC1L_SHIFT 5 /* ADCR_TO_DAC1L */
+#define WM8996_ADCR_TO_DAC1L_WIDTH 1 /* ADCR_TO_DAC1L */
+#define WM8996_ADCL_TO_DAC1L 0x0010 /* ADCL_TO_DAC1L */
+#define WM8996_ADCL_TO_DAC1L_MASK 0x0010 /* ADCL_TO_DAC1L */
+#define WM8996_ADCL_TO_DAC1L_SHIFT 4 /* ADCL_TO_DAC1L */
+#define WM8996_ADCL_TO_DAC1L_WIDTH 1 /* ADCL_TO_DAC1L */
+#define WM8996_DSP2RXL_TO_DAC1L 0x0002 /* DSP2RXL_TO_DAC1L */
+#define WM8996_DSP2RXL_TO_DAC1L_MASK 0x0002 /* DSP2RXL_TO_DAC1L */
+#define WM8996_DSP2RXL_TO_DAC1L_SHIFT 1 /* DSP2RXL_TO_DAC1L */
+#define WM8996_DSP2RXL_TO_DAC1L_WIDTH 1 /* DSP2RXL_TO_DAC1L */
+#define WM8996_DSP1RXL_TO_DAC1L 0x0001 /* DSP1RXL_TO_DAC1L */
+#define WM8996_DSP1RXL_TO_DAC1L_MASK 0x0001 /* DSP1RXL_TO_DAC1L */
+#define WM8996_DSP1RXL_TO_DAC1L_SHIFT 0 /* DSP1RXL_TO_DAC1L */
+#define WM8996_DSP1RXL_TO_DAC1L_WIDTH 1 /* DSP1RXL_TO_DAC1L */
+
+/*
+ * R1538 (0x602) - DAC1 Right Mixer Routing
+ */
+#define WM8996_ADCR_TO_DAC1R 0x0020 /* ADCR_TO_DAC1R */
+#define WM8996_ADCR_TO_DAC1R_MASK 0x0020 /* ADCR_TO_DAC1R */
+#define WM8996_ADCR_TO_DAC1R_SHIFT 5 /* ADCR_TO_DAC1R */
+#define WM8996_ADCR_TO_DAC1R_WIDTH 1 /* ADCR_TO_DAC1R */
+#define WM8996_ADCL_TO_DAC1R 0x0010 /* ADCL_TO_DAC1R */
+#define WM8996_ADCL_TO_DAC1R_MASK 0x0010 /* ADCL_TO_DAC1R */
+#define WM8996_ADCL_TO_DAC1R_SHIFT 4 /* ADCL_TO_DAC1R */
+#define WM8996_ADCL_TO_DAC1R_WIDTH 1 /* ADCL_TO_DAC1R */
+#define WM8996_DSP2RXR_TO_DAC1R 0x0002 /* DSP2RXR_TO_DAC1R */
+#define WM8996_DSP2RXR_TO_DAC1R_MASK 0x0002 /* DSP2RXR_TO_DAC1R */
+#define WM8996_DSP2RXR_TO_DAC1R_SHIFT 1 /* DSP2RXR_TO_DAC1R */
+#define WM8996_DSP2RXR_TO_DAC1R_WIDTH 1 /* DSP2RXR_TO_DAC1R */
+#define WM8996_DSP1RXR_TO_DAC1R 0x0001 /* DSP1RXR_TO_DAC1R */
+#define WM8996_DSP1RXR_TO_DAC1R_MASK 0x0001 /* DSP1RXR_TO_DAC1R */
+#define WM8996_DSP1RXR_TO_DAC1R_SHIFT 0 /* DSP1RXR_TO_DAC1R */
+#define WM8996_DSP1RXR_TO_DAC1R_WIDTH 1 /* DSP1RXR_TO_DAC1R */
+
+/*
+ * R1539 (0x603) - DAC2 Mixer Volumes
+ */
+#define WM8996_ADCR_DAC2_VOL_MASK 0x03E0 /* ADCR_DAC2_VOL - [9:5] */
+#define WM8996_ADCR_DAC2_VOL_SHIFT 5 /* ADCR_DAC2_VOL - [9:5] */
+#define WM8996_ADCR_DAC2_VOL_WIDTH 5 /* ADCR_DAC2_VOL - [9:5] */
+#define WM8996_ADCL_DAC2_VOL_MASK 0x001F /* ADCL_DAC2_VOL - [4:0] */
+#define WM8996_ADCL_DAC2_VOL_SHIFT 0 /* ADCL_DAC2_VOL - [4:0] */
+#define WM8996_ADCL_DAC2_VOL_WIDTH 5 /* ADCL_DAC2_VOL - [4:0] */
+
+/*
+ * R1540 (0x604) - DAC2 Left Mixer Routing
+ */
+#define WM8996_ADCR_TO_DAC2L 0x0020 /* ADCR_TO_DAC2L */
+#define WM8996_ADCR_TO_DAC2L_MASK 0x0020 /* ADCR_TO_DAC2L */
+#define WM8996_ADCR_TO_DAC2L_SHIFT 5 /* ADCR_TO_DAC2L */
+#define WM8996_ADCR_TO_DAC2L_WIDTH 1 /* ADCR_TO_DAC2L */
+#define WM8996_ADCL_TO_DAC2L 0x0010 /* ADCL_TO_DAC2L */
+#define WM8996_ADCL_TO_DAC2L_MASK 0x0010 /* ADCL_TO_DAC2L */
+#define WM8996_ADCL_TO_DAC2L_SHIFT 4 /* ADCL_TO_DAC2L */
+#define WM8996_ADCL_TO_DAC2L_WIDTH 1 /* ADCL_TO_DAC2L */
+#define WM8996_DSP2RXL_TO_DAC2L 0x0002 /* DSP2RXL_TO_DAC2L */
+#define WM8996_DSP2RXL_TO_DAC2L_MASK 0x0002 /* DSP2RXL_TO_DAC2L */
+#define WM8996_DSP2RXL_TO_DAC2L_SHIFT 1 /* DSP2RXL_TO_DAC2L */
+#define WM8996_DSP2RXL_TO_DAC2L_WIDTH 1 /* DSP2RXL_TO_DAC2L */
+#define WM8996_DSP1RXL_TO_DAC2L 0x0001 /* DSP1RXL_TO_DAC2L */
+#define WM8996_DSP1RXL_TO_DAC2L_MASK 0x0001 /* DSP1RXL_TO_DAC2L */
+#define WM8996_DSP1RXL_TO_DAC2L_SHIFT 0 /* DSP1RXL_TO_DAC2L */
+#define WM8996_DSP1RXL_TO_DAC2L_WIDTH 1 /* DSP1RXL_TO_DAC2L */
+
+/*
+ * R1541 (0x605) - DAC2 Right Mixer Routing
+ */
+#define WM8996_ADCR_TO_DAC2R 0x0020 /* ADCR_TO_DAC2R */
+#define WM8996_ADCR_TO_DAC2R_MASK 0x0020 /* ADCR_TO_DAC2R */
+#define WM8996_ADCR_TO_DAC2R_SHIFT 5 /* ADCR_TO_DAC2R */
+#define WM8996_ADCR_TO_DAC2R_WIDTH 1 /* ADCR_TO_DAC2R */
+#define WM8996_ADCL_TO_DAC2R 0x0010 /* ADCL_TO_DAC2R */
+#define WM8996_ADCL_TO_DAC2R_MASK 0x0010 /* ADCL_TO_DAC2R */
+#define WM8996_ADCL_TO_DAC2R_SHIFT 4 /* ADCL_TO_DAC2R */
+#define WM8996_ADCL_TO_DAC2R_WIDTH 1 /* ADCL_TO_DAC2R */
+#define WM8996_DSP2RXR_TO_DAC2R 0x0002 /* DSP2RXR_TO_DAC2R */
+#define WM8996_DSP2RXR_TO_DAC2R_MASK 0x0002 /* DSP2RXR_TO_DAC2R */
+#define WM8996_DSP2RXR_TO_DAC2R_SHIFT 1 /* DSP2RXR_TO_DAC2R */
+#define WM8996_DSP2RXR_TO_DAC2R_WIDTH 1 /* DSP2RXR_TO_DAC2R */
+#define WM8996_DSP1RXR_TO_DAC2R 0x0001 /* DSP1RXR_TO_DAC2R */
+#define WM8996_DSP1RXR_TO_DAC2R_MASK 0x0001 /* DSP1RXR_TO_DAC2R */
+#define WM8996_DSP1RXR_TO_DAC2R_SHIFT 0 /* DSP1RXR_TO_DAC2R */
+#define WM8996_DSP1RXR_TO_DAC2R_WIDTH 1 /* DSP1RXR_TO_DAC2R */
+
+/*
+ * R1542 (0x606) - DSP1 TX Left Mixer Routing
+ */
+#define WM8996_ADC1L_TO_DSP1TXL 0x0002 /* ADC1L_TO_DSP1TXL */
+#define WM8996_ADC1L_TO_DSP1TXL_MASK 0x0002 /* ADC1L_TO_DSP1TXL */
+#define WM8996_ADC1L_TO_DSP1TXL_SHIFT 1 /* ADC1L_TO_DSP1TXL */
+#define WM8996_ADC1L_TO_DSP1TXL_WIDTH 1 /* ADC1L_TO_DSP1TXL */
+#define WM8996_DACL_TO_DSP1TXL 0x0001 /* DACL_TO_DSP1TXL */
+#define WM8996_DACL_TO_DSP1TXL_MASK 0x0001 /* DACL_TO_DSP1TXL */
+#define WM8996_DACL_TO_DSP1TXL_SHIFT 0 /* DACL_TO_DSP1TXL */
+#define WM8996_DACL_TO_DSP1TXL_WIDTH 1 /* DACL_TO_DSP1TXL */
+
+/*
+ * R1543 (0x607) - DSP1 TX Right Mixer Routing
+ */
+#define WM8996_ADC1R_TO_DSP1TXR 0x0002 /* ADC1R_TO_DSP1TXR */
+#define WM8996_ADC1R_TO_DSP1TXR_MASK 0x0002 /* ADC1R_TO_DSP1TXR */
+#define WM8996_ADC1R_TO_DSP1TXR_SHIFT 1 /* ADC1R_TO_DSP1TXR */
+#define WM8996_ADC1R_TO_DSP1TXR_WIDTH 1 /* ADC1R_TO_DSP1TXR */
+#define WM8996_DACR_TO_DSP1TXR 0x0001 /* DACR_TO_DSP1TXR */
+#define WM8996_DACR_TO_DSP1TXR_MASK 0x0001 /* DACR_TO_DSP1TXR */
+#define WM8996_DACR_TO_DSP1TXR_SHIFT 0 /* DACR_TO_DSP1TXR */
+#define WM8996_DACR_TO_DSP1TXR_WIDTH 1 /* DACR_TO_DSP1TXR */
+
+/*
+ * R1544 (0x608) - DSP2 TX Left Mixer Routing
+ */
+#define WM8996_ADC2L_TO_DSP2TXL 0x0002 /* ADC2L_TO_DSP2TXL */
+#define WM8996_ADC2L_TO_DSP2TXL_MASK 0x0002 /* ADC2L_TO_DSP2TXL */
+#define WM8996_ADC2L_TO_DSP2TXL_SHIFT 1 /* ADC2L_TO_DSP2TXL */
+#define WM8996_ADC2L_TO_DSP2TXL_WIDTH 1 /* ADC2L_TO_DSP2TXL */
+#define WM8996_DACL_TO_DSP2TXL 0x0001 /* DACL_TO_DSP2TXL */
+#define WM8996_DACL_TO_DSP2TXL_MASK 0x0001 /* DACL_TO_DSP2TXL */
+#define WM8996_DACL_TO_DSP2TXL_SHIFT 0 /* DACL_TO_DSP2TXL */
+#define WM8996_DACL_TO_DSP2TXL_WIDTH 1 /* DACL_TO_DSP2TXL */
+
+/*
+ * R1545 (0x609) - DSP2 TX Right Mixer Routing
+ */
+#define WM8996_ADC2R_TO_DSP2TXR 0x0002 /* ADC2R_TO_DSP2TXR */
+#define WM8996_ADC2R_TO_DSP2TXR_MASK 0x0002 /* ADC2R_TO_DSP2TXR */
+#define WM8996_ADC2R_TO_DSP2TXR_SHIFT 1 /* ADC2R_TO_DSP2TXR */
+#define WM8996_ADC2R_TO_DSP2TXR_WIDTH 1 /* ADC2R_TO_DSP2TXR */
+#define WM8996_DACR_TO_DSP2TXR 0x0001 /* DACR_TO_DSP2TXR */
+#define WM8996_DACR_TO_DSP2TXR_MASK 0x0001 /* DACR_TO_DSP2TXR */
+#define WM8996_DACR_TO_DSP2TXR_SHIFT 0 /* DACR_TO_DSP2TXR */
+#define WM8996_DACR_TO_DSP2TXR_WIDTH 1 /* DACR_TO_DSP2TXR */
+
+/*
+ * R1546 (0x60A) - DSP TX Mixer Select
+ */
+#define WM8996_DAC_TO_DSPTX_SRC 0x0001 /* DAC_TO_DSPTX_SRC */
+#define WM8996_DAC_TO_DSPTX_SRC_MASK 0x0001 /* DAC_TO_DSPTX_SRC */
+#define WM8996_DAC_TO_DSPTX_SRC_SHIFT 0 /* DAC_TO_DSPTX_SRC */
+#define WM8996_DAC_TO_DSPTX_SRC_WIDTH 1 /* DAC_TO_DSPTX_SRC */
+
+/*
+ * R1552 (0x610) - DAC Softmute
+ */
+#define WM8996_DAC_SOFTMUTEMODE 0x0002 /* DAC_SOFTMUTEMODE */
+#define WM8996_DAC_SOFTMUTEMODE_MASK 0x0002 /* DAC_SOFTMUTEMODE */
+#define WM8996_DAC_SOFTMUTEMODE_SHIFT 1 /* DAC_SOFTMUTEMODE */
+#define WM8996_DAC_SOFTMUTEMODE_WIDTH 1 /* DAC_SOFTMUTEMODE */
+#define WM8996_DAC_MUTERATE 0x0001 /* DAC_MUTERATE */
+#define WM8996_DAC_MUTERATE_MASK 0x0001 /* DAC_MUTERATE */
+#define WM8996_DAC_MUTERATE_SHIFT 0 /* DAC_MUTERATE */
+#define WM8996_DAC_MUTERATE_WIDTH 1 /* DAC_MUTERATE */
+
+/*
+ * R1568 (0x620) - Oversampling
+ */
+#define WM8996_SPK_OSR128 0x0008 /* SPK_OSR128 */
+#define WM8996_SPK_OSR128_MASK 0x0008 /* SPK_OSR128 */
+#define WM8996_SPK_OSR128_SHIFT 3 /* SPK_OSR128 */
+#define WM8996_SPK_OSR128_WIDTH 1 /* SPK_OSR128 */
+#define WM8996_DMIC_OSR64 0x0004 /* DMIC_OSR64 */
+#define WM8996_DMIC_OSR64_MASK 0x0004 /* DMIC_OSR64 */
+#define WM8996_DMIC_OSR64_SHIFT 2 /* DMIC_OSR64 */
+#define WM8996_DMIC_OSR64_WIDTH 1 /* DMIC_OSR64 */
+#define WM8996_ADC_OSR128 0x0002 /* ADC_OSR128 */
+#define WM8996_ADC_OSR128_MASK 0x0002 /* ADC_OSR128 */
+#define WM8996_ADC_OSR128_SHIFT 1 /* ADC_OSR128 */
+#define WM8996_ADC_OSR128_WIDTH 1 /* ADC_OSR128 */
+#define WM8996_DAC_OSR128 0x0001 /* DAC_OSR128 */
+#define WM8996_DAC_OSR128_MASK 0x0001 /* DAC_OSR128 */
+#define WM8996_DAC_OSR128_SHIFT 0 /* DAC_OSR128 */
+#define WM8996_DAC_OSR128_WIDTH 1 /* DAC_OSR128 */
+
+/*
+ * R1569 (0x621) - Sidetone
+ */
+#define WM8996_ST_LPF 0x1000 /* ST_LPF */
+#define WM8996_ST_LPF_MASK 0x1000 /* ST_LPF */
+#define WM8996_ST_LPF_SHIFT 12 /* ST_LPF */
+#define WM8996_ST_LPF_WIDTH 1 /* ST_LPF */
+#define WM8996_ST_HPF_CUT_MASK 0x0380 /* ST_HPF_CUT - [9:7] */
+#define WM8996_ST_HPF_CUT_SHIFT 7 /* ST_HPF_CUT - [9:7] */
+#define WM8996_ST_HPF_CUT_WIDTH 3 /* ST_HPF_CUT - [9:7] */
+#define WM8996_ST_HPF 0x0040 /* ST_HPF */
+#define WM8996_ST_HPF_MASK 0x0040 /* ST_HPF */
+#define WM8996_ST_HPF_SHIFT 6 /* ST_HPF */
+#define WM8996_ST_HPF_WIDTH 1 /* ST_HPF */
+#define WM8996_STR_SEL 0x0002 /* STR_SEL */
+#define WM8996_STR_SEL_MASK 0x0002 /* STR_SEL */
+#define WM8996_STR_SEL_SHIFT 1 /* STR_SEL */
+#define WM8996_STR_SEL_WIDTH 1 /* STR_SEL */
+#define WM8996_STL_SEL 0x0001 /* STL_SEL */
+#define WM8996_STL_SEL_MASK 0x0001 /* STL_SEL */
+#define WM8996_STL_SEL_SHIFT 0 /* STL_SEL */
+#define WM8996_STL_SEL_WIDTH 1 /* STL_SEL */
+
+/*
+ * R1792 (0x700) - GPIO 1
+ */
+#define WM8996_GP1_DIR 0x8000 /* GP1_DIR */
+#define WM8996_GP1_DIR_MASK 0x8000 /* GP1_DIR */
+#define WM8996_GP1_DIR_SHIFT 15 /* GP1_DIR */
+#define WM8996_GP1_DIR_WIDTH 1 /* GP1_DIR */
+#define WM8996_GP1_PU 0x4000 /* GP1_PU */
+#define WM8996_GP1_PU_MASK 0x4000 /* GP1_PU */
+#define WM8996_GP1_PU_SHIFT 14 /* GP1_PU */
+#define WM8996_GP1_PU_WIDTH 1 /* GP1_PU */
+#define WM8996_GP1_PD 0x2000 /* GP1_PD */
+#define WM8996_GP1_PD_MASK 0x2000 /* GP1_PD */
+#define WM8996_GP1_PD_SHIFT 13 /* GP1_PD */
+#define WM8996_GP1_PD_WIDTH 1 /* GP1_PD */
+#define WM8996_GP1_POL 0x0400 /* GP1_POL */
+#define WM8996_GP1_POL_MASK 0x0400 /* GP1_POL */
+#define WM8996_GP1_POL_SHIFT 10 /* GP1_POL */
+#define WM8996_GP1_POL_WIDTH 1 /* GP1_POL */
+#define WM8996_GP1_OP_CFG 0x0200 /* GP1_OP_CFG */
+#define WM8996_GP1_OP_CFG_MASK 0x0200 /* GP1_OP_CFG */
+#define WM8996_GP1_OP_CFG_SHIFT 9 /* GP1_OP_CFG */
+#define WM8996_GP1_OP_CFG_WIDTH 1 /* GP1_OP_CFG */
+#define WM8996_GP1_DB 0x0100 /* GP1_DB */
+#define WM8996_GP1_DB_MASK 0x0100 /* GP1_DB */
+#define WM8996_GP1_DB_SHIFT 8 /* GP1_DB */
+#define WM8996_GP1_DB_WIDTH 1 /* GP1_DB */
+#define WM8996_GP1_LVL 0x0040 /* GP1_LVL */
+#define WM8996_GP1_LVL_MASK 0x0040 /* GP1_LVL */
+#define WM8996_GP1_LVL_SHIFT 6 /* GP1_LVL */
+#define WM8996_GP1_LVL_WIDTH 1 /* GP1_LVL */
+#define WM8996_GP1_FN_MASK 0x000F /* GP1_FN - [3:0] */
+#define WM8996_GP1_FN_SHIFT 0 /* GP1_FN - [3:0] */
+#define WM8996_GP1_FN_WIDTH 4 /* GP1_FN - [3:0] */
+
+/*
+ * R1793 (0x701) - GPIO 2
+ */
+#define WM8996_GP2_DIR 0x8000 /* GP2_DIR */
+#define WM8996_GP2_DIR_MASK 0x8000 /* GP2_DIR */
+#define WM8996_GP2_DIR_SHIFT 15 /* GP2_DIR */
+#define WM8996_GP2_DIR_WIDTH 1 /* GP2_DIR */
+#define WM8996_GP2_PU 0x4000 /* GP2_PU */
+#define WM8996_GP2_PU_MASK 0x4000 /* GP2_PU */
+#define WM8996_GP2_PU_SHIFT 14 /* GP2_PU */
+#define WM8996_GP2_PU_WIDTH 1 /* GP2_PU */
+#define WM8996_GP2_PD 0x2000 /* GP2_PD */
+#define WM8996_GP2_PD_MASK 0x2000 /* GP2_PD */
+#define WM8996_GP2_PD_SHIFT 13 /* GP2_PD */
+#define WM8996_GP2_PD_WIDTH 1 /* GP2_PD */
+#define WM8996_GP2_POL 0x0400 /* GP2_POL */
+#define WM8996_GP2_POL_MASK 0x0400 /* GP2_POL */
+#define WM8996_GP2_POL_SHIFT 10 /* GP2_POL */
+#define WM8996_GP2_POL_WIDTH 1 /* GP2_POL */
+#define WM8996_GP2_OP_CFG 0x0200 /* GP2_OP_CFG */
+#define WM8996_GP2_OP_CFG_MASK 0x0200 /* GP2_OP_CFG */
+#define WM8996_GP2_OP_CFG_SHIFT 9 /* GP2_OP_CFG */
+#define WM8996_GP2_OP_CFG_WIDTH 1 /* GP2_OP_CFG */
+#define WM8996_GP2_DB 0x0100 /* GP2_DB */
+#define WM8996_GP2_DB_MASK 0x0100 /* GP2_DB */
+#define WM8996_GP2_DB_SHIFT 8 /* GP2_DB */
+#define WM8996_GP2_DB_WIDTH 1 /* GP2_DB */
+#define WM8996_GP2_LVL 0x0040 /* GP2_LVL */
+#define WM8996_GP2_LVL_MASK 0x0040 /* GP2_LVL */
+#define WM8996_GP2_LVL_SHIFT 6 /* GP2_LVL */
+#define WM8996_GP2_LVL_WIDTH 1 /* GP2_LVL */
+#define WM8996_GP2_FN_MASK 0x000F /* GP2_FN - [3:0] */
+#define WM8996_GP2_FN_SHIFT 0 /* GP2_FN - [3:0] */
+#define WM8996_GP2_FN_WIDTH 4 /* GP2_FN - [3:0] */
+
+/*
+ * R1794 (0x702) - GPIO 3
+ */
+#define WM8996_GP3_DIR 0x8000 /* GP3_DIR */
+#define WM8996_GP3_DIR_MASK 0x8000 /* GP3_DIR */
+#define WM8996_GP3_DIR_SHIFT 15 /* GP3_DIR */
+#define WM8996_GP3_DIR_WIDTH 1 /* GP3_DIR */
+#define WM8996_GP3_PU 0x4000 /* GP3_PU */
+#define WM8996_GP3_PU_MASK 0x4000 /* GP3_PU */
+#define WM8996_GP3_PU_SHIFT 14 /* GP3_PU */
+#define WM8996_GP3_PU_WIDTH 1 /* GP3_PU */
+#define WM8996_GP3_PD 0x2000 /* GP3_PD */
+#define WM8996_GP3_PD_MASK 0x2000 /* GP3_PD */
+#define WM8996_GP3_PD_SHIFT 13 /* GP3_PD */
+#define WM8996_GP3_PD_WIDTH 1 /* GP3_PD */
+#define WM8996_GP3_POL 0x0400 /* GP3_POL */
+#define WM8996_GP3_POL_MASK 0x0400 /* GP3_POL */
+#define WM8996_GP3_POL_SHIFT 10 /* GP3_POL */
+#define WM8996_GP3_POL_WIDTH 1 /* GP3_POL */
+#define WM8996_GP3_OP_CFG 0x0200 /* GP3_OP_CFG */
+#define WM8996_GP3_OP_CFG_MASK 0x0200 /* GP3_OP_CFG */
+#define WM8996_GP3_OP_CFG_SHIFT 9 /* GP3_OP_CFG */
+#define WM8996_GP3_OP_CFG_WIDTH 1 /* GP3_OP_CFG */
+#define WM8996_GP3_DB 0x0100 /* GP3_DB */
+#define WM8996_GP3_DB_MASK 0x0100 /* GP3_DB */
+#define WM8996_GP3_DB_SHIFT 8 /* GP3_DB */
+#define WM8996_GP3_DB_WIDTH 1 /* GP3_DB */
+#define WM8996_GP3_LVL 0x0040 /* GP3_LVL */
+#define WM8996_GP3_LVL_MASK 0x0040 /* GP3_LVL */
+#define WM8996_GP3_LVL_SHIFT 6 /* GP3_LVL */
+#define WM8996_GP3_LVL_WIDTH 1 /* GP3_LVL */
+#define WM8996_GP3_FN_MASK 0x000F /* GP3_FN - [3:0] */
+#define WM8996_GP3_FN_SHIFT 0 /* GP3_FN - [3:0] */
+#define WM8996_GP3_FN_WIDTH 4 /* GP3_FN - [3:0] */
+
+/*
+ * R1795 (0x703) - GPIO 4
+ */
+#define WM8996_GP4_DIR 0x8000 /* GP4_DIR */
+#define WM8996_GP4_DIR_MASK 0x8000 /* GP4_DIR */
+#define WM8996_GP4_DIR_SHIFT 15 /* GP4_DIR */
+#define WM8996_GP4_DIR_WIDTH 1 /* GP4_DIR */
+#define WM8996_GP4_PU 0x4000 /* GP4_PU */
+#define WM8996_GP4_PU_MASK 0x4000 /* GP4_PU */
+#define WM8996_GP4_PU_SHIFT 14 /* GP4_PU */
+#define WM8996_GP4_PU_WIDTH 1 /* GP4_PU */
+#define WM8996_GP4_PD 0x2000 /* GP4_PD */
+#define WM8996_GP4_PD_MASK 0x2000 /* GP4_PD */
+#define WM8996_GP4_PD_SHIFT 13 /* GP4_PD */
+#define WM8996_GP4_PD_WIDTH 1 /* GP4_PD */
+#define WM8996_GP4_POL 0x0400 /* GP4_POL */
+#define WM8996_GP4_POL_MASK 0x0400 /* GP4_POL */
+#define WM8996_GP4_POL_SHIFT 10 /* GP4_POL */
+#define WM8996_GP4_POL_WIDTH 1 /* GP4_POL */
+#define WM8996_GP4_OP_CFG 0x0200 /* GP4_OP_CFG */
+#define WM8996_GP4_OP_CFG_MASK 0x0200 /* GP4_OP_CFG */
+#define WM8996_GP4_OP_CFG_SHIFT 9 /* GP4_OP_CFG */
+#define WM8996_GP4_OP_CFG_WIDTH 1 /* GP4_OP_CFG */
+#define WM8996_GP4_DB 0x0100 /* GP4_DB */
+#define WM8996_GP4_DB_MASK 0x0100 /* GP4_DB */
+#define WM8996_GP4_DB_SHIFT 8 /* GP4_DB */
+#define WM8996_GP4_DB_WIDTH 1 /* GP4_DB */
+#define WM8996_GP4_LVL 0x0040 /* GP4_LVL */
+#define WM8996_GP4_LVL_MASK 0x0040 /* GP4_LVL */
+#define WM8996_GP4_LVL_SHIFT 6 /* GP4_LVL */
+#define WM8996_GP4_LVL_WIDTH 1 /* GP4_LVL */
+#define WM8996_GP4_FN_MASK 0x000F /* GP4_FN - [3:0] */
+#define WM8996_GP4_FN_SHIFT 0 /* GP4_FN - [3:0] */
+#define WM8996_GP4_FN_WIDTH 4 /* GP4_FN - [3:0] */
+
+/*
+ * R1796 (0x704) - GPIO 5
+ */
+#define WM8996_GP5_DIR 0x8000 /* GP5_DIR */
+#define WM8996_GP5_DIR_MASK 0x8000 /* GP5_DIR */
+#define WM8996_GP5_DIR_SHIFT 15 /* GP5_DIR */
+#define WM8996_GP5_DIR_WIDTH 1 /* GP5_DIR */
+#define WM8996_GP5_PU 0x4000 /* GP5_PU */
+#define WM8996_GP5_PU_MASK 0x4000 /* GP5_PU */
+#define WM8996_GP5_PU_SHIFT 14 /* GP5_PU */
+#define WM8996_GP5_PU_WIDTH 1 /* GP5_PU */
+#define WM8996_GP5_PD 0x2000 /* GP5_PD */
+#define WM8996_GP5_PD_MASK 0x2000 /* GP5_PD */
+#define WM8996_GP5_PD_SHIFT 13 /* GP5_PD */
+#define WM8996_GP5_PD_WIDTH 1 /* GP5_PD */
+#define WM8996_GP5_POL 0x0400 /* GP5_POL */
+#define WM8996_GP5_POL_MASK 0x0400 /* GP5_POL */
+#define WM8996_GP5_POL_SHIFT 10 /* GP5_POL */
+#define WM8996_GP5_POL_WIDTH 1 /* GP5_POL */
+#define WM8996_GP5_OP_CFG 0x0200 /* GP5_OP_CFG */
+#define WM8996_GP5_OP_CFG_MASK 0x0200 /* GP5_OP_CFG */
+#define WM8996_GP5_OP_CFG_SHIFT 9 /* GP5_OP_CFG */
+#define WM8996_GP5_OP_CFG_WIDTH 1 /* GP5_OP_CFG */
+#define WM8996_GP5_DB 0x0100 /* GP5_DB */
+#define WM8996_GP5_DB_MASK 0x0100 /* GP5_DB */
+#define WM8996_GP5_DB_SHIFT 8 /* GP5_DB */
+#define WM8996_GP5_DB_WIDTH 1 /* GP5_DB */
+#define WM8996_GP5_LVL 0x0040 /* GP5_LVL */
+#define WM8996_GP5_LVL_MASK 0x0040 /* GP5_LVL */
+#define WM8996_GP5_LVL_SHIFT 6 /* GP5_LVL */
+#define WM8996_GP5_LVL_WIDTH 1 /* GP5_LVL */
+#define WM8996_GP5_FN_MASK 0x000F /* GP5_FN - [3:0] */
+#define WM8996_GP5_FN_SHIFT 0 /* GP5_FN - [3:0] */
+#define WM8996_GP5_FN_WIDTH 4 /* GP5_FN - [3:0] */
+
+/*
+ * R1824 (0x720) - Pull Control (1)
+ */
+#define WM8996_DMICDAT2_PD 0x1000 /* DMICDAT2_PD */
+#define WM8996_DMICDAT2_PD_MASK 0x1000 /* DMICDAT2_PD */
+#define WM8996_DMICDAT2_PD_SHIFT 12 /* DMICDAT2_PD */
+#define WM8996_DMICDAT2_PD_WIDTH 1 /* DMICDAT2_PD */
+#define WM8996_DMICDAT1_PD 0x0400 /* DMICDAT1_PD */
+#define WM8996_DMICDAT1_PD_MASK 0x0400 /* DMICDAT1_PD */
+#define WM8996_DMICDAT1_PD_SHIFT 10 /* DMICDAT1_PD */
+#define WM8996_DMICDAT1_PD_WIDTH 1 /* DMICDAT1_PD */
+#define WM8996_MCLK2_PU 0x0200 /* MCLK2_PU */
+#define WM8996_MCLK2_PU_MASK 0x0200 /* MCLK2_PU */
+#define WM8996_MCLK2_PU_SHIFT 9 /* MCLK2_PU */
+#define WM8996_MCLK2_PU_WIDTH 1 /* MCLK2_PU */
+#define WM8996_MCLK2_PD 0x0100 /* MCLK2_PD */
+#define WM8996_MCLK2_PD_MASK 0x0100 /* MCLK2_PD */
+#define WM8996_MCLK2_PD_SHIFT 8 /* MCLK2_PD */
+#define WM8996_MCLK2_PD_WIDTH 1 /* MCLK2_PD */
+#define WM8996_MCLK1_PU 0x0080 /* MCLK1_PU */
+#define WM8996_MCLK1_PU_MASK 0x0080 /* MCLK1_PU */
+#define WM8996_MCLK1_PU_SHIFT 7 /* MCLK1_PU */
+#define WM8996_MCLK1_PU_WIDTH 1 /* MCLK1_PU */
+#define WM8996_MCLK1_PD 0x0040 /* MCLK1_PD */
+#define WM8996_MCLK1_PD_MASK 0x0040 /* MCLK1_PD */
+#define WM8996_MCLK1_PD_SHIFT 6 /* MCLK1_PD */
+#define WM8996_MCLK1_PD_WIDTH 1 /* MCLK1_PD */
+#define WM8996_DACDAT1_PU 0x0020 /* DACDAT1_PU */
+#define WM8996_DACDAT1_PU_MASK 0x0020 /* DACDAT1_PU */
+#define WM8996_DACDAT1_PU_SHIFT 5 /* DACDAT1_PU */
+#define WM8996_DACDAT1_PU_WIDTH 1 /* DACDAT1_PU */
+#define WM8996_DACDAT1_PD 0x0010 /* DACDAT1_PD */
+#define WM8996_DACDAT1_PD_MASK 0x0010 /* DACDAT1_PD */
+#define WM8996_DACDAT1_PD_SHIFT 4 /* DACDAT1_PD */
+#define WM8996_DACDAT1_PD_WIDTH 1 /* DACDAT1_PD */
+#define WM8996_DACLRCLK1_PU 0x0008 /* DACLRCLK1_PU */
+#define WM8996_DACLRCLK1_PU_MASK 0x0008 /* DACLRCLK1_PU */
+#define WM8996_DACLRCLK1_PU_SHIFT 3 /* DACLRCLK1_PU */
+#define WM8996_DACLRCLK1_PU_WIDTH 1 /* DACLRCLK1_PU */
+#define WM8996_DACLRCLK1_PD 0x0004 /* DACLRCLK1_PD */
+#define WM8996_DACLRCLK1_PD_MASK 0x0004 /* DACLRCLK1_PD */
+#define WM8996_DACLRCLK1_PD_SHIFT 2 /* DACLRCLK1_PD */
+#define WM8996_DACLRCLK1_PD_WIDTH 1 /* DACLRCLK1_PD */
+#define WM8996_BCLK1_PU 0x0002 /* BCLK1_PU */
+#define WM8996_BCLK1_PU_MASK 0x0002 /* BCLK1_PU */
+#define WM8996_BCLK1_PU_SHIFT 1 /* BCLK1_PU */
+#define WM8996_BCLK1_PU_WIDTH 1 /* BCLK1_PU */
+#define WM8996_BCLK1_PD 0x0001 /* BCLK1_PD */
+#define WM8996_BCLK1_PD_MASK 0x0001 /* BCLK1_PD */
+#define WM8996_BCLK1_PD_SHIFT 0 /* BCLK1_PD */
+#define WM8996_BCLK1_PD_WIDTH 1 /* BCLK1_PD */
+
+/*
+ * R1825 (0x721) - Pull Control (2)
+ */
+#define WM8996_LDO1ENA_PD 0x0100 /* LDO1ENA_PD */
+#define WM8996_LDO1ENA_PD_MASK 0x0100 /* LDO1ENA_PD */
+#define WM8996_LDO1ENA_PD_SHIFT 8 /* LDO1ENA_PD */
+#define WM8996_LDO1ENA_PD_WIDTH 1 /* LDO1ENA_PD */
+#define WM8996_ADDR_PD 0x0040 /* ADDR_PD */
+#define WM8996_ADDR_PD_MASK 0x0040 /* ADDR_PD */
+#define WM8996_ADDR_PD_SHIFT 6 /* ADDR_PD */
+#define WM8996_ADDR_PD_WIDTH 1 /* ADDR_PD */
+#define WM8996_DACDAT2_PU 0x0020 /* DACDAT2_PU */
+#define WM8996_DACDAT2_PU_MASK 0x0020 /* DACDAT2_PU */
+#define WM8996_DACDAT2_PU_SHIFT 5 /* DACDAT2_PU */
+#define WM8996_DACDAT2_PU_WIDTH 1 /* DACDAT2_PU */
+#define WM8996_DACDAT2_PD 0x0010 /* DACDAT2_PD */
+#define WM8996_DACDAT2_PD_MASK 0x0010 /* DACDAT2_PD */
+#define WM8996_DACDAT2_PD_SHIFT 4 /* DACDAT2_PD */
+#define WM8996_DACDAT2_PD_WIDTH 1 /* DACDAT2_PD */
+#define WM8996_DACLRCLK2_PU 0x0008 /* DACLRCLK2_PU */
+#define WM8996_DACLRCLK2_PU_MASK 0x0008 /* DACLRCLK2_PU */
+#define WM8996_DACLRCLK2_PU_SHIFT 3 /* DACLRCLK2_PU */
+#define WM8996_DACLRCLK2_PU_WIDTH 1 /* DACLRCLK2_PU */
+#define WM8996_DACLRCLK2_PD 0x0004 /* DACLRCLK2_PD */
+#define WM8996_DACLRCLK2_PD_MASK 0x0004 /* DACLRCLK2_PD */
+#define WM8996_DACLRCLK2_PD_SHIFT 2 /* DACLRCLK2_PD */
+#define WM8996_DACLRCLK2_PD_WIDTH 1 /* DACLRCLK2_PD */
+#define WM8996_BCLK2_PU 0x0002 /* BCLK2_PU */
+#define WM8996_BCLK2_PU_MASK 0x0002 /* BCLK2_PU */
+#define WM8996_BCLK2_PU_SHIFT 1 /* BCLK2_PU */
+#define WM8996_BCLK2_PU_WIDTH 1 /* BCLK2_PU */
+#define WM8996_BCLK2_PD 0x0001 /* BCLK2_PD */
+#define WM8996_BCLK2_PD_MASK 0x0001 /* BCLK2_PD */
+#define WM8996_BCLK2_PD_SHIFT 0 /* BCLK2_PD */
+#define WM8996_BCLK2_PD_WIDTH 1 /* BCLK2_PD */
+
+/*
+ * R1840 (0x730) - Interrupt Status 1
+ */
+#define WM8996_GP5_EINT 0x0010 /* GP5_EINT */
+#define WM8996_GP5_EINT_MASK 0x0010 /* GP5_EINT */
+#define WM8996_GP5_EINT_SHIFT 4 /* GP5_EINT */
+#define WM8996_GP5_EINT_WIDTH 1 /* GP5_EINT */
+#define WM8996_GP4_EINT 0x0008 /* GP4_EINT */
+#define WM8996_GP4_EINT_MASK 0x0008 /* GP4_EINT */
+#define WM8996_GP4_EINT_SHIFT 3 /* GP4_EINT */
+#define WM8996_GP4_EINT_WIDTH 1 /* GP4_EINT */
+#define WM8996_GP3_EINT 0x0004 /* GP3_EINT */
+#define WM8996_GP3_EINT_MASK 0x0004 /* GP3_EINT */
+#define WM8996_GP3_EINT_SHIFT 2 /* GP3_EINT */
+#define WM8996_GP3_EINT_WIDTH 1 /* GP3_EINT */
+#define WM8996_GP2_EINT 0x0002 /* GP2_EINT */
+#define WM8996_GP2_EINT_MASK 0x0002 /* GP2_EINT */
+#define WM8996_GP2_EINT_SHIFT 1 /* GP2_EINT */
+#define WM8996_GP2_EINT_WIDTH 1 /* GP2_EINT */
+#define WM8996_GP1_EINT 0x0001 /* GP1_EINT */
+#define WM8996_GP1_EINT_MASK 0x0001 /* GP1_EINT */
+#define WM8996_GP1_EINT_SHIFT 0 /* GP1_EINT */
+#define WM8996_GP1_EINT_WIDTH 1 /* GP1_EINT */
+
+/*
+ * R1841 (0x731) - Interrupt Status 2
+ */
+#define WM8996_DCS_DONE_23_EINT 0x1000 /* DCS_DONE_23_EINT */
+#define WM8996_DCS_DONE_23_EINT_MASK 0x1000 /* DCS_DONE_23_EINT */
+#define WM8996_DCS_DONE_23_EINT_SHIFT 12 /* DCS_DONE_23_EINT */
+#define WM8996_DCS_DONE_23_EINT_WIDTH 1 /* DCS_DONE_23_EINT */
+#define WM8996_DCS_DONE_01_EINT 0x0800 /* DCS_DONE_01_EINT */
+#define WM8996_DCS_DONE_01_EINT_MASK 0x0800 /* DCS_DONE_01_EINT */
+#define WM8996_DCS_DONE_01_EINT_SHIFT 11 /* DCS_DONE_01_EINT */
+#define WM8996_DCS_DONE_01_EINT_WIDTH 1 /* DCS_DONE_01_EINT */
+#define WM8996_WSEQ_DONE_EINT 0x0400 /* WSEQ_DONE_EINT */
+#define WM8996_WSEQ_DONE_EINT_MASK 0x0400 /* WSEQ_DONE_EINT */
+#define WM8996_WSEQ_DONE_EINT_SHIFT 10 /* WSEQ_DONE_EINT */
+#define WM8996_WSEQ_DONE_EINT_WIDTH 1 /* WSEQ_DONE_EINT */
+#define WM8996_FIFOS_ERR_EINT 0x0200 /* FIFOS_ERR_EINT */
+#define WM8996_FIFOS_ERR_EINT_MASK 0x0200 /* FIFOS_ERR_EINT */
+#define WM8996_FIFOS_ERR_EINT_SHIFT 9 /* FIFOS_ERR_EINT */
+#define WM8996_FIFOS_ERR_EINT_WIDTH 1 /* FIFOS_ERR_EINT */
+#define WM8996_DSP2DRC_SIG_DET_EINT 0x0080 /* DSP2DRC_SIG_DET_EINT */
+#define WM8996_DSP2DRC_SIG_DET_EINT_MASK 0x0080 /* DSP2DRC_SIG_DET_EINT */
+#define WM8996_DSP2DRC_SIG_DET_EINT_SHIFT 7 /* DSP2DRC_SIG_DET_EINT */
+#define WM8996_DSP2DRC_SIG_DET_EINT_WIDTH 1 /* DSP2DRC_SIG_DET_EINT */
+#define WM8996_DSP1DRC_SIG_DET_EINT 0x0040 /* DSP1DRC_SIG_DET_EINT */
+#define WM8996_DSP1DRC_SIG_DET_EINT_MASK 0x0040 /* DSP1DRC_SIG_DET_EINT */
+#define WM8996_DSP1DRC_SIG_DET_EINT_SHIFT 6 /* DSP1DRC_SIG_DET_EINT */
+#define WM8996_DSP1DRC_SIG_DET_EINT_WIDTH 1 /* DSP1DRC_SIG_DET_EINT */
+#define WM8996_FLL_SW_CLK_DONE_EINT 0x0008 /* FLL_SW_CLK_DONE_EINT */
+#define WM8996_FLL_SW_CLK_DONE_EINT_MASK 0x0008 /* FLL_SW_CLK_DONE_EINT */
+#define WM8996_FLL_SW_CLK_DONE_EINT_SHIFT 3 /* FLL_SW_CLK_DONE_EINT */
+#define WM8996_FLL_SW_CLK_DONE_EINT_WIDTH 1 /* FLL_SW_CLK_DONE_EINT */
+#define WM8996_FLL_LOCK_EINT 0x0004 /* FLL_LOCK_EINT */
+#define WM8996_FLL_LOCK_EINT_MASK 0x0004 /* FLL_LOCK_EINT */
+#define WM8996_FLL_LOCK_EINT_SHIFT 2 /* FLL_LOCK_EINT */
+#define WM8996_FLL_LOCK_EINT_WIDTH 1 /* FLL_LOCK_EINT */
+#define WM8996_HP_DONE_EINT 0x0002 /* HP_DONE_EINT */
+#define WM8996_HP_DONE_EINT_MASK 0x0002 /* HP_DONE_EINT */
+#define WM8996_HP_DONE_EINT_SHIFT 1 /* HP_DONE_EINT */
+#define WM8996_HP_DONE_EINT_WIDTH 1 /* HP_DONE_EINT */
+#define WM8996_MICD_EINT 0x0001 /* MICD_EINT */
+#define WM8996_MICD_EINT_MASK 0x0001 /* MICD_EINT */
+#define WM8996_MICD_EINT_SHIFT 0 /* MICD_EINT */
+#define WM8996_MICD_EINT_WIDTH 1 /* MICD_EINT */
+
+/*
+ * R1842 (0x732) - Interrupt Raw Status 2
+ */
+#define WM8996_DCS_DONE_23_STS 0x1000 /* DCS_DONE_23_STS */
+#define WM8996_DCS_DONE_23_STS_MASK 0x1000 /* DCS_DONE_23_STS */
+#define WM8996_DCS_DONE_23_STS_SHIFT 12 /* DCS_DONE_23_STS */
+#define WM8996_DCS_DONE_23_STS_WIDTH 1 /* DCS_DONE_23_STS */
+#define WM8996_DCS_DONE_01_STS 0x0800 /* DCS_DONE_01_STS */
+#define WM8996_DCS_DONE_01_STS_MASK 0x0800 /* DCS_DONE_01_STS */
+#define WM8996_DCS_DONE_01_STS_SHIFT 11 /* DCS_DONE_01_STS */
+#define WM8996_DCS_DONE_01_STS_WIDTH 1 /* DCS_DONE_01_STS */
+#define WM8996_WSEQ_DONE_STS 0x0400 /* WSEQ_DONE_STS */
+#define WM8996_WSEQ_DONE_STS_MASK 0x0400 /* WSEQ_DONE_STS */
+#define WM8996_WSEQ_DONE_STS_SHIFT 10 /* WSEQ_DONE_STS */
+#define WM8996_WSEQ_DONE_STS_WIDTH 1 /* WSEQ_DONE_STS */
+#define WM8996_FIFOS_ERR_STS 0x0200 /* FIFOS_ERR_STS */
+#define WM8996_FIFOS_ERR_STS_MASK 0x0200 /* FIFOS_ERR_STS */
+#define WM8996_FIFOS_ERR_STS_SHIFT 9 /* FIFOS_ERR_STS */
+#define WM8996_FIFOS_ERR_STS_WIDTH 1 /* FIFOS_ERR_STS */
+#define WM8996_DSP2DRC_SIG_DET_STS 0x0080 /* DSP2DRC_SIG_DET_STS */
+#define WM8996_DSP2DRC_SIG_DET_STS_MASK 0x0080 /* DSP2DRC_SIG_DET_STS */
+#define WM8996_DSP2DRC_SIG_DET_STS_SHIFT 7 /* DSP2DRC_SIG_DET_STS */
+#define WM8996_DSP2DRC_SIG_DET_STS_WIDTH 1 /* DSP2DRC_SIG_DET_STS */
+#define WM8996_DSP1DRC_SIG_DET_STS 0x0040 /* DSP1DRC_SIG_DET_STS */
+#define WM8996_DSP1DRC_SIG_DET_STS_MASK 0x0040 /* DSP1DRC_SIG_DET_STS */
+#define WM8996_DSP1DRC_SIG_DET_STS_SHIFT 6 /* DSP1DRC_SIG_DET_STS */
+#define WM8996_DSP1DRC_SIG_DET_STS_WIDTH 1 /* DSP1DRC_SIG_DET_STS */
+#define WM8996_FLL_LOCK_STS 0x0004 /* FLL_LOCK_STS */
+#define WM8996_FLL_LOCK_STS_MASK 0x0004 /* FLL_LOCK_STS */
+#define WM8996_FLL_LOCK_STS_SHIFT 2 /* FLL_LOCK_STS */
+#define WM8996_FLL_LOCK_STS_WIDTH 1 /* FLL_LOCK_STS */
+
+/*
+ * R1848 (0x738) - Interrupt Status 1 Mask
+ */
+#define WM8996_IM_GP5_EINT 0x0010 /* IM_GP5_EINT */
+#define WM8996_IM_GP5_EINT_MASK 0x0010 /* IM_GP5_EINT */
+#define WM8996_IM_GP5_EINT_SHIFT 4 /* IM_GP5_EINT */
+#define WM8996_IM_GP5_EINT_WIDTH 1 /* IM_GP5_EINT */
+#define WM8996_IM_GP4_EINT 0x0008 /* IM_GP4_EINT */
+#define WM8996_IM_GP4_EINT_MASK 0x0008 /* IM_GP4_EINT */
+#define WM8996_IM_GP4_EINT_SHIFT 3 /* IM_GP4_EINT */
+#define WM8996_IM_GP4_EINT_WIDTH 1 /* IM_GP4_EINT */
+#define WM8996_IM_GP3_EINT 0x0004 /* IM_GP3_EINT */
+#define WM8996_IM_GP3_EINT_MASK 0x0004 /* IM_GP3_EINT */
+#define WM8996_IM_GP3_EINT_SHIFT 2 /* IM_GP3_EINT */
+#define WM8996_IM_GP3_EINT_WIDTH 1 /* IM_GP3_EINT */
+#define WM8996_IM_GP2_EINT 0x0002 /* IM_GP2_EINT */
+#define WM8996_IM_GP2_EINT_MASK 0x0002 /* IM_GP2_EINT */
+#define WM8996_IM_GP2_EINT_SHIFT 1 /* IM_GP2_EINT */
+#define WM8996_IM_GP2_EINT_WIDTH 1 /* IM_GP2_EINT */
+#define WM8996_IM_GP1_EINT 0x0001 /* IM_GP1_EINT */
+#define WM8996_IM_GP1_EINT_MASK 0x0001 /* IM_GP1_EINT */
+#define WM8996_IM_GP1_EINT_SHIFT 0 /* IM_GP1_EINT */
+#define WM8996_IM_GP1_EINT_WIDTH 1 /* IM_GP1_EINT */
+
+/*
+ * R1849 (0x739) - Interrupt Status 2 Mask
+ */
+#define WM8996_IM_DCS_DONE_23_EINT 0x1000 /* IM_DCS_DONE_23_EINT */
+#define WM8996_IM_DCS_DONE_23_EINT_MASK 0x1000 /* IM_DCS_DONE_23_EINT */
+#define WM8996_IM_DCS_DONE_23_EINT_SHIFT 12 /* IM_DCS_DONE_23_EINT */
+#define WM8996_IM_DCS_DONE_23_EINT_WIDTH 1 /* IM_DCS_DONE_23_EINT */
+#define WM8996_IM_DCS_DONE_01_EINT 0x0800 /* IM_DCS_DONE_01_EINT */
+#define WM8996_IM_DCS_DONE_01_EINT_MASK 0x0800 /* IM_DCS_DONE_01_EINT */
+#define WM8996_IM_DCS_DONE_01_EINT_SHIFT 11 /* IM_DCS_DONE_01_EINT */
+#define WM8996_IM_DCS_DONE_01_EINT_WIDTH 1 /* IM_DCS_DONE_01_EINT */
+#define WM8996_IM_WSEQ_DONE_EINT 0x0400 /* IM_WSEQ_DONE_EINT */
+#define WM8996_IM_WSEQ_DONE_EINT_MASK 0x0400 /* IM_WSEQ_DONE_EINT */
+#define WM8996_IM_WSEQ_DONE_EINT_SHIFT 10 /* IM_WSEQ_DONE_EINT */
+#define WM8996_IM_WSEQ_DONE_EINT_WIDTH 1 /* IM_WSEQ_DONE_EINT */
+#define WM8996_IM_FIFOS_ERR_EINT 0x0200 /* IM_FIFOS_ERR_EINT */
+#define WM8996_IM_FIFOS_ERR_EINT_MASK 0x0200 /* IM_FIFOS_ERR_EINT */
+#define WM8996_IM_FIFOS_ERR_EINT_SHIFT 9 /* IM_FIFOS_ERR_EINT */
+#define WM8996_IM_FIFOS_ERR_EINT_WIDTH 1 /* IM_FIFOS_ERR_EINT */
+#define WM8996_IM_DSP2DRC_SIG_DET_EINT 0x0080 /* IM_DSP2DRC_SIG_DET_EINT */
+#define WM8996_IM_DSP2DRC_SIG_DET_EINT_MASK 0x0080 /* IM_DSP2DRC_SIG_DET_EINT */
+#define WM8996_IM_DSP2DRC_SIG_DET_EINT_SHIFT 7 /* IM_DSP2DRC_SIG_DET_EINT */
+#define WM8996_IM_DSP2DRC_SIG_DET_EINT_WIDTH 1 /* IM_DSP2DRC_SIG_DET_EINT */
+#define WM8996_IM_DSP1DRC_SIG_DET_EINT 0x0040 /* IM_DSP1DRC_SIG_DET_EINT */
+#define WM8996_IM_DSP1DRC_SIG_DET_EINT_MASK 0x0040 /* IM_DSP1DRC_SIG_DET_EINT */
+#define WM8996_IM_DSP1DRC_SIG_DET_EINT_SHIFT 6 /* IM_DSP1DRC_SIG_DET_EINT */
+#define WM8996_IM_DSP1DRC_SIG_DET_EINT_WIDTH 1 /* IM_DSP1DRC_SIG_DET_EINT */
+#define WM8996_IM_FLL_SW_CLK_DONE_EINT 0x0008 /* IM_FLL_SW_CLK_DONE_EINT */
+#define WM8996_IM_FLL_SW_CLK_DONE_EINT_MASK 0x0008 /* IM_FLL_SW_CLK_DONE_EINT */
+#define WM8996_IM_FLL_SW_CLK_DONE_EINT_SHIFT 3 /* IM_FLL_SW_CLK_DONE_EINT */
+#define WM8996_IM_FLL_SW_CLK_DONE_EINT_WIDTH 1 /* IM_FLL_SW_CLK_DONE_EINT */
+#define WM8996_IM_FLL_LOCK_EINT 0x0004 /* IM_FLL_LOCK_EINT */
+#define WM8996_IM_FLL_LOCK_EINT_MASK 0x0004 /* IM_FLL_LOCK_EINT */
+#define WM8996_IM_FLL_LOCK_EINT_SHIFT 2 /* IM_FLL_LOCK_EINT */
+#define WM8996_IM_FLL_LOCK_EINT_WIDTH 1 /* IM_FLL_LOCK_EINT */
+#define WM8996_IM_HP_DONE_EINT 0x0002 /* IM_HP_DONE_EINT */
+#define WM8996_IM_HP_DONE_EINT_MASK 0x0002 /* IM_HP_DONE_EINT */
+#define WM8996_IM_HP_DONE_EINT_SHIFT 1 /* IM_HP_DONE_EINT */
+#define WM8996_IM_HP_DONE_EINT_WIDTH 1 /* IM_HP_DONE_EINT */
+#define WM8996_IM_MICD_EINT 0x0001 /* IM_MICD_EINT */
+#define WM8996_IM_MICD_EINT_MASK 0x0001 /* IM_MICD_EINT */
+#define WM8996_IM_MICD_EINT_SHIFT 0 /* IM_MICD_EINT */
+#define WM8996_IM_MICD_EINT_WIDTH 1 /* IM_MICD_EINT */
+
+/*
+ * R1856 (0x740) - Interrupt Control
+ */
+#define WM8996_IM_IRQ 0x0001 /* IM_IRQ */
+#define WM8996_IM_IRQ_MASK 0x0001 /* IM_IRQ */
+#define WM8996_IM_IRQ_SHIFT 0 /* IM_IRQ */
+#define WM8996_IM_IRQ_WIDTH 1 /* IM_IRQ */
+
+/*
+ * R2048 (0x800) - Left PDM Speaker
+ */
+#define WM8996_SPKL_ENA 0x0010 /* SPKL_ENA */
+#define WM8996_SPKL_ENA_MASK 0x0010 /* SPKL_ENA */
+#define WM8996_SPKL_ENA_SHIFT 4 /* SPKL_ENA */
+#define WM8996_SPKL_ENA_WIDTH 1 /* SPKL_ENA */
+#define WM8996_SPKL_MUTE 0x0008 /* SPKL_MUTE */
+#define WM8996_SPKL_MUTE_MASK 0x0008 /* SPKL_MUTE */
+#define WM8996_SPKL_MUTE_SHIFT 3 /* SPKL_MUTE */
+#define WM8996_SPKL_MUTE_WIDTH 1 /* SPKL_MUTE */
+#define WM8996_SPKL_MUTE_ZC 0x0004 /* SPKL_MUTE_ZC */
+#define WM8996_SPKL_MUTE_ZC_MASK 0x0004 /* SPKL_MUTE_ZC */
+#define WM8996_SPKL_MUTE_ZC_SHIFT 2 /* SPKL_MUTE_ZC */
+#define WM8996_SPKL_MUTE_ZC_WIDTH 1 /* SPKL_MUTE_ZC */
+#define WM8996_SPKL_SRC_MASK 0x0003 /* SPKL_SRC - [1:0] */
+#define WM8996_SPKL_SRC_SHIFT 0 /* SPKL_SRC - [1:0] */
+#define WM8996_SPKL_SRC_WIDTH 2 /* SPKL_SRC - [1:0] */
+
+/*
+ * R2049 (0x801) - Right PDM Speaker
+ */
+#define WM8996_SPKR_ENA 0x0010 /* SPKR_ENA */
+#define WM8996_SPKR_ENA_MASK 0x0010 /* SPKR_ENA */
+#define WM8996_SPKR_ENA_SHIFT 4 /* SPKR_ENA */
+#define WM8996_SPKR_ENA_WIDTH 1 /* SPKR_ENA */
+#define WM8996_SPKR_MUTE 0x0008 /* SPKR_MUTE */
+#define WM8996_SPKR_MUTE_MASK 0x0008 /* SPKR_MUTE */
+#define WM8996_SPKR_MUTE_SHIFT 3 /* SPKR_MUTE */
+#define WM8996_SPKR_MUTE_WIDTH 1 /* SPKR_MUTE */
+#define WM8996_SPKR_MUTE_ZC 0x0004 /* SPKR_MUTE_ZC */
+#define WM8996_SPKR_MUTE_ZC_MASK 0x0004 /* SPKR_MUTE_ZC */
+#define WM8996_SPKR_MUTE_ZC_SHIFT 2 /* SPKR_MUTE_ZC */
+#define WM8996_SPKR_MUTE_ZC_WIDTH 1 /* SPKR_MUTE_ZC */
+#define WM8996_SPKR_SRC_MASK 0x0003 /* SPKR_SRC - [1:0] */
+#define WM8996_SPKR_SRC_SHIFT 0 /* SPKR_SRC - [1:0] */
+#define WM8996_SPKR_SRC_WIDTH 2 /* SPKR_SRC - [1:0] */
+
+/*
+ * R2050 (0x802) - PDM Speaker Mute Sequence
+ */
+#define WM8996_SPK_MUTE_ENDIAN 0x0100 /* SPK_MUTE_ENDIAN */
+#define WM8996_SPK_MUTE_ENDIAN_MASK 0x0100 /* SPK_MUTE_ENDIAN */
+#define WM8996_SPK_MUTE_ENDIAN_SHIFT 8 /* SPK_MUTE_ENDIAN */
+#define WM8996_SPK_MUTE_ENDIAN_WIDTH 1 /* SPK_MUTE_ENDIAN */
+#define WM8996_SPK_MUTE_SEQ1_MASK 0x00FF /* SPK_MUTE_SEQ1 - [7:0] */
+#define WM8996_SPK_MUTE_SEQ1_SHIFT 0 /* SPK_MUTE_SEQ1 - [7:0] */
+#define WM8996_SPK_MUTE_SEQ1_WIDTH 8 /* SPK_MUTE_SEQ1 - [7:0] */
+
+/*
+ * R2051 (0x803) - PDM Speaker Volume
+ */
+#define WM8996_SPKR_VOL_MASK 0x00F0 /* SPKR_VOL - [7:4] */
+#define WM8996_SPKR_VOL_SHIFT 4 /* SPKR_VOL - [7:4] */
+#define WM8996_SPKR_VOL_WIDTH 4 /* SPKR_VOL - [7:4] */
+#define WM8996_SPKL_VOL_MASK 0x000F /* SPKL_VOL - [3:0] */
+#define WM8996_SPKL_VOL_SHIFT 0 /* SPKL_VOL - [3:0] */
+#define WM8996_SPKL_VOL_WIDTH 4 /* SPKL_VOL - [3:0] */
+
+#endif
diff --git a/sound/soc/codecs/wm_hubs.c b/sound/soc/codecs/wm_hubs.c
index 4cc2d56..e763c54 100644
--- a/sound/soc/codecs/wm_hubs.c
+++ b/sound/soc/codecs/wm_hubs.c
@@ -440,9 +440,8 @@
reg |= WM8993_HPOUT1L_DLY | WM8993_HPOUT1R_DLY;
snd_soc_write(codec, WM8993_ANALOGUE_HP_0, reg);
- /* Smallest supported update interval */
snd_soc_update_bits(codec, WM8993_DC_SERVO_1,
- WM8993_DCS_TIMER_PERIOD_01_MASK, 1);
+ WM8993_DCS_TIMER_PERIOD_01_MASK, 0);
calibrate_dc_servo(codec);
diff --git a/sound/soc/omap/n810.c b/sound/soc/omap/n810.c
index 83d213b..62e292f 100644
--- a/sound/soc/omap/n810.c
+++ b/sound/soc/omap/n810.c
@@ -3,7 +3,7 @@
*
* Copyright (C) 2008 Nokia Corporation
*
- * Contact: Jarkko Nikula <jhnikula@gmail.com>
+ * Contact: Jarkko Nikula <jarkko.nikula@bitmer.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@@ -402,6 +402,6 @@
module_init(n810_soc_init);
module_exit(n810_soc_exit);
-MODULE_AUTHOR("Jarkko Nikula <jhnikula@gmail.com>");
+MODULE_AUTHOR("Jarkko Nikula <jarkko.nikula@bitmer.com>");
MODULE_DESCRIPTION("ALSA SoC Nokia N810");
MODULE_LICENSE("GPL");
diff --git a/sound/soc/omap/omap-mcbsp.c b/sound/soc/omap/omap-mcbsp.c
index 07b7723..ebcc2d4 100644
--- a/sound/soc/omap/omap-mcbsp.c
+++ b/sound/soc/omap/omap-mcbsp.c
@@ -3,7 +3,7 @@
*
* Copyright (C) 2008 Nokia Corporation
*
- * Contact: Jarkko Nikula <jhnikula@gmail.com>
+ * Contact: Jarkko Nikula <jarkko.nikula@bitmer.com>
* Peter Ujfalusi <peter.ujfalusi@ti.com>
*
* This program is free software; you can redistribute it and/or
@@ -780,6 +780,6 @@
}
module_exit(snd_omap_mcbsp_exit);
-MODULE_AUTHOR("Jarkko Nikula <jhnikula@gmail.com>");
+MODULE_AUTHOR("Jarkko Nikula <jarkko.nikula@bitmer.com>");
MODULE_DESCRIPTION("OMAP I2S SoC Interface");
MODULE_LICENSE("GPL");
diff --git a/sound/soc/omap/omap-mcbsp.h b/sound/soc/omap/omap-mcbsp.h
index 9a7dedd..65cde9d 100644
--- a/sound/soc/omap/omap-mcbsp.h
+++ b/sound/soc/omap/omap-mcbsp.h
@@ -3,7 +3,7 @@
*
* Copyright (C) 2008 Nokia Corporation
*
- * Contact: Jarkko Nikula <jhnikula@gmail.com>
+ * Contact: Jarkko Nikula <jarkko.nikula@bitmer.com>
* Peter Ujfalusi <peter.ujfalusi@ti.com>
*
* This program is free software; you can redistribute it and/or
diff --git a/sound/soc/omap/omap-pcm.c b/sound/soc/omap/omap-pcm.c
index b2f5751..9b5c88a 100644
--- a/sound/soc/omap/omap-pcm.c
+++ b/sound/soc/omap/omap-pcm.c
@@ -3,7 +3,7 @@
*
* Copyright (C) 2008 Nokia Corporation
*
- * Contact: Jarkko Nikula <jhnikula@gmail.com>
+ * Contact: Jarkko Nikula <jarkko.nikula@bitmer.com>
* Peter Ujfalusi <peter.ujfalusi@ti.com>
*
* This program is free software; you can redistribute it and/or
@@ -436,6 +436,6 @@
}
module_exit(snd_omap_pcm_exit);
-MODULE_AUTHOR("Jarkko Nikula <jhnikula@gmail.com>");
+MODULE_AUTHOR("Jarkko Nikula <jarkko.nikula@bitmer.com>");
MODULE_DESCRIPTION("OMAP PCM DMA module");
MODULE_LICENSE("GPL");
diff --git a/sound/soc/omap/omap-pcm.h b/sound/soc/omap/omap-pcm.h
index a0ed1db..f95fe30 100644
--- a/sound/soc/omap/omap-pcm.h
+++ b/sound/soc/omap/omap-pcm.h
@@ -3,7 +3,7 @@
*
* Copyright (C) 2008 Nokia Corporation
*
- * Contact: Jarkko Nikula <jhnikula@gmail.com>
+ * Contact: Jarkko Nikula <jarkko.nikula@bitmer.com>
* Peter Ujfalusi <peter.ujfalusi@ti.com>
*
* This program is free software; you can redistribute it and/or
diff --git a/sound/soc/omap/rx51.c b/sound/soc/omap/rx51.c
index 0aae998..893300a 100644
--- a/sound/soc/omap/rx51.c
+++ b/sound/soc/omap/rx51.c
@@ -5,7 +5,7 @@
*
* Contact: Peter Ujfalusi <peter.ujfalusi@ti.com>
* Eduardo Valentin <eduardo.valentin@nokia.com>
- * Jarkko Nikula <jhnikula@gmail.com>
+ * Jarkko Nikula <jarkko.nikula@bitmer.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
diff --git a/sound/soc/samsung/Kconfig b/sound/soc/samsung/Kconfig
index 54b0e4b..b99091f 100644
--- a/sound/soc/samsung/Kconfig
+++ b/sound/soc/samsung/Kconfig
@@ -183,7 +183,7 @@
tristate "Audio support for Wolfson Speyside"
depends on SND_SOC_SAMSUNG && MACH_WLF_CRAGG_6410
select SND_SAMSUNG_I2S
- select SND_SOC_WM8915
+ select SND_SOC_WM8996
select SND_SOC_WM9081
config SND_SOC_SPEYSIDE_WM8962
diff --git a/sound/soc/samsung/Makefile b/sound/soc/samsung/Makefile
index 9eb3b12..8509d3c 100644
--- a/sound/soc/samsung/Makefile
+++ b/sound/soc/samsung/Makefile
@@ -1,5 +1,6 @@
# S3c24XX Platform Support
snd-soc-s3c24xx-objs := dma.o
+snd-soc-idma-objs := idma.o
snd-soc-s3c24xx-i2s-objs := s3c24xx-i2s.o
snd-soc-s3c2412-i2s-objs := s3c2412-i2s.o
snd-soc-ac97-objs := ac97.o
@@ -16,6 +17,7 @@
obj-$(CONFIG_SND_SAMSUNG_SPDIF) += snd-soc-samsung-spdif.o
obj-$(CONFIG_SND_SAMSUNG_PCM) += snd-soc-pcm.o
obj-$(CONFIG_SND_SAMSUNG_I2S) += snd-soc-i2s.o
+obj-$(CONFIG_SND_SAMSUNG_I2S) += snd-soc-idma.o
# S3C24XX Machine Support
snd-soc-jive-wm8750-objs := jive_wm8750.o
diff --git a/sound/soc/samsung/idma.c b/sound/soc/samsung/idma.c
new file mode 100644
index 0000000..ebde074
--- /dev/null
+++ b/sound/soc/samsung/idma.c
@@ -0,0 +1,453 @@
+/*
+ * sound/soc/samsung/idma.c
+ *
+ * Copyright (c) 2011 Samsung Electronics Co., Ltd.
+ * http://www.samsung.com
+ *
+ * I2S0's Internal DMA driver
+ *
+ * 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/interrupt.h>
+#include <linux/platform_device.h>
+#include <linux/dma-mapping.h>
+#include <linux/slab.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+
+#include "i2s.h"
+#include "idma.h"
+#include "dma.h"
+#include "i2s-regs.h"
+
+#define ST_RUNNING (1<<0)
+#define ST_OPENED (1<<1)
+
+static const struct snd_pcm_hardware idma_hardware = {
+ .info = SNDRV_PCM_INFO_INTERLEAVED |
+ SNDRV_PCM_INFO_BLOCK_TRANSFER |
+ SNDRV_PCM_INFO_MMAP |
+ SNDRV_PCM_INFO_MMAP_VALID |
+ SNDRV_PCM_INFO_PAUSE |
+ SNDRV_PCM_INFO_RESUME,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_U16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE |
+ SNDRV_PCM_FMTBIT_U24_LE |
+ SNDRV_PCM_FMTBIT_U8 |
+ SNDRV_PCM_FMTBIT_S8,
+ .channels_min = 2,
+ .channels_max = 2,
+ .buffer_bytes_max = MAX_IDMA_BUFFER,
+ .period_bytes_min = 128,
+ .period_bytes_max = MAX_IDMA_PERIOD,
+ .periods_min = 1,
+ .periods_max = 2,
+};
+
+struct idma_ctrl {
+ spinlock_t lock;
+ int state;
+ dma_addr_t start;
+ dma_addr_t pos;
+ dma_addr_t end;
+ dma_addr_t period;
+ dma_addr_t periodsz;
+ void *token;
+ void (*cb)(void *dt, int bytes_xfer);
+};
+
+static struct idma_info {
+ spinlock_t lock;
+ void __iomem *regs;
+ dma_addr_t lp_tx_addr;
+} idma;
+
+static void idma_getpos(dma_addr_t *src)
+{
+ *src = idma.lp_tx_addr +
+ (readl(idma.regs + I2STRNCNT) & 0xffffff) * 4;
+}
+
+static int idma_enqueue(struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct idma_ctrl *prtd = substream->runtime->private_data;
+ u32 val;
+
+ spin_lock(&prtd->lock);
+ prtd->token = (void *) substream;
+ spin_unlock(&prtd->lock);
+
+ /* Internal DMA Level0 Interrupt Address */
+ val = idma.lp_tx_addr + prtd->periodsz;
+ writel(val, idma.regs + I2SLVL0ADDR);
+
+ /* Start address0 of I2S internal DMA operation. */
+ val = idma.lp_tx_addr;
+ writel(val, idma.regs + I2SSTR0);
+
+ /*
+ * Transfer block size for I2S internal DMA.
+ * Should decide transfer size before start dma operation
+ */
+ val = readl(idma.regs + I2SSIZE);
+ val &= ~(I2SSIZE_TRNMSK << I2SSIZE_SHIFT);
+ val |= (((runtime->dma_bytes >> 2) &
+ I2SSIZE_TRNMSK) << I2SSIZE_SHIFT);
+ writel(val, idma.regs + I2SSIZE);
+
+ val = readl(idma.regs + I2SAHB);
+ val |= AHB_INTENLVL0;
+ writel(val, idma.regs + I2SAHB);
+
+ return 0;
+}
+
+static void idma_setcallbk(struct snd_pcm_substream *substream,
+ void (*cb)(void *, int))
+{
+ struct idma_ctrl *prtd = substream->runtime->private_data;
+
+ spin_lock(&prtd->lock);
+ prtd->cb = cb;
+ spin_unlock(&prtd->lock);
+}
+
+static void idma_control(int op)
+{
+ u32 val = readl(idma.regs + I2SAHB);
+
+ spin_lock(&idma.lock);
+
+ switch (op) {
+ case LPAM_DMA_START:
+ val |= (AHB_INTENLVL0 | AHB_DMAEN);
+ break;
+ case LPAM_DMA_STOP:
+ val &= ~(AHB_INTENLVL0 | AHB_DMAEN);
+ break;
+ default:
+ spin_unlock(&idma.lock);
+ return;
+ }
+
+ writel(val, idma.regs + I2SAHB);
+ spin_unlock(&idma.lock);
+}
+
+static void idma_done(void *id, int bytes_xfer)
+{
+ struct snd_pcm_substream *substream = id;
+ struct idma_ctrl *prtd = substream->runtime->private_data;
+
+ if (prtd && (prtd->state & ST_RUNNING))
+ snd_pcm_period_elapsed(substream);
+}
+
+static int idma_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct idma_ctrl *prtd = substream->runtime->private_data;
+ u32 mod = readl(idma.regs + I2SMOD);
+ u32 ahb = readl(idma.regs + I2SAHB);
+
+ ahb |= (AHB_DMARLD | AHB_INTMASK);
+ mod |= MOD_TXS_IDMA;
+ writel(ahb, idma.regs + I2SAHB);
+ writel(mod, idma.regs + I2SMOD);
+
+ snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer);
+ runtime->dma_bytes = params_buffer_bytes(params);
+
+ prtd->start = prtd->pos = runtime->dma_addr;
+ prtd->period = params_periods(params);
+ prtd->periodsz = params_period_bytes(params);
+ prtd->end = runtime->dma_addr + runtime->dma_bytes;
+
+ idma_setcallbk(substream, idma_done);
+
+ return 0;
+}
+
+static int idma_hw_free(struct snd_pcm_substream *substream)
+{
+ snd_pcm_set_runtime_buffer(substream, NULL);
+
+ return 0;
+}
+
+static int idma_prepare(struct snd_pcm_substream *substream)
+{
+ struct idma_ctrl *prtd = substream->runtime->private_data;
+
+ prtd->pos = prtd->start;
+
+ /* flush the DMA channel */
+ idma_control(LPAM_DMA_STOP);
+ idma_enqueue(substream);
+
+ return 0;
+}
+
+static int idma_trigger(struct snd_pcm_substream *substream, int cmd)
+{
+ struct idma_ctrl *prtd = substream->runtime->private_data;
+ int ret = 0;
+
+ spin_lock(&prtd->lock);
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_RESUME:
+ case SNDRV_PCM_TRIGGER_START:
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ prtd->state |= ST_RUNNING;
+ idma_control(LPAM_DMA_START);
+ break;
+
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ case SNDRV_PCM_TRIGGER_STOP:
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ prtd->state &= ~ST_RUNNING;
+ idma_control(LPAM_DMA_STOP);
+ break;
+
+ default:
+ ret = -EINVAL;
+ break;
+ }
+
+ spin_unlock(&prtd->lock);
+
+ return ret;
+}
+
+static snd_pcm_uframes_t
+ idma_pointer(struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct idma_ctrl *prtd = runtime->private_data;
+ dma_addr_t src;
+ unsigned long res;
+
+ spin_lock(&prtd->lock);
+
+ idma_getpos(&src);
+ res = src - prtd->start;
+
+ spin_unlock(&prtd->lock);
+
+ return bytes_to_frames(substream->runtime, res);
+}
+
+static int idma_mmap(struct snd_pcm_substream *substream,
+ struct vm_area_struct *vma)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ unsigned long size, offset;
+ int ret;
+
+ /* From snd_pcm_lib_mmap_iomem */
+ vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
+ vma->vm_flags |= VM_IO;
+ size = vma->vm_end - vma->vm_start;
+ offset = vma->vm_pgoff << PAGE_SHIFT;
+ ret = io_remap_pfn_range(vma, vma->vm_start,
+ (runtime->dma_addr + offset) >> PAGE_SHIFT,
+ size, vma->vm_page_prot);
+
+ return ret;
+}
+
+static irqreturn_t iis_irq(int irqno, void *dev_id)
+{
+ struct idma_ctrl *prtd = (struct idma_ctrl *)dev_id;
+ u32 iiscon, iisahb, val, addr;
+
+ iisahb = readl(idma.regs + I2SAHB);
+ iiscon = readl(idma.regs + I2SCON);
+
+ val = (iisahb & AHB_LVL0INT) ? AHB_CLRLVL0INT : 0;
+
+ if (val) {
+ iisahb |= val;
+ writel(iisahb, idma.regs + I2SAHB);
+
+ addr = readl(idma.regs + I2SLVL0ADDR) - idma.lp_tx_addr;
+ addr += prtd->periodsz;
+ addr %= (prtd->end - prtd->start);
+ addr += idma.lp_tx_addr;
+
+ writel(addr, idma.regs + I2SLVL0ADDR);
+
+ if (prtd->cb)
+ prtd->cb(prtd->token, prtd->period);
+ }
+
+ return IRQ_HANDLED;
+}
+
+static int idma_open(struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct idma_ctrl *prtd;
+ int ret;
+
+ snd_soc_set_runtime_hwparams(substream, &idma_hardware);
+
+ prtd = kzalloc(sizeof(struct idma_ctrl), GFP_KERNEL);
+ if (prtd == NULL)
+ return -ENOMEM;
+
+ ret = request_irq(IRQ_I2S0, iis_irq, 0, "i2s", prtd);
+ if (ret < 0) {
+ pr_err("fail to claim i2s irq , ret = %d\n", ret);
+ kfree(prtd);
+ return ret;
+ }
+
+ spin_lock_init(&prtd->lock);
+
+ runtime->private_data = prtd;
+
+ return 0;
+}
+
+static int idma_close(struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct idma_ctrl *prtd = runtime->private_data;
+
+ free_irq(IRQ_I2S0, prtd);
+
+ if (!prtd)
+ pr_err("idma_close called with prtd == NULL\n");
+
+ kfree(prtd);
+
+ return 0;
+}
+
+static struct snd_pcm_ops idma_ops = {
+ .open = idma_open,
+ .close = idma_close,
+ .ioctl = snd_pcm_lib_ioctl,
+ .trigger = idma_trigger,
+ .pointer = idma_pointer,
+ .mmap = idma_mmap,
+ .hw_params = idma_hw_params,
+ .hw_free = idma_hw_free,
+ .prepare = idma_prepare,
+};
+
+static void idma_free(struct snd_pcm *pcm)
+{
+ struct snd_pcm_substream *substream;
+ struct snd_dma_buffer *buf;
+
+ substream = pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream;
+ if (!substream)
+ return;
+
+ buf = &substream->dma_buffer;
+ if (!buf->area)
+ return;
+
+ iounmap(buf->area);
+
+ buf->area = NULL;
+ buf->addr = 0;
+}
+
+static int preallocate_idma_buffer(struct snd_pcm *pcm, int stream)
+{
+ struct snd_pcm_substream *substream = pcm->streams[stream].substream;
+ struct snd_dma_buffer *buf = &substream->dma_buffer;
+
+ buf->dev.dev = pcm->card->dev;
+ buf->private_data = NULL;
+
+ /* Assign PCM buffer pointers */
+ buf->dev.type = SNDRV_DMA_TYPE_CONTINUOUS;
+ buf->addr = idma.lp_tx_addr;
+ buf->bytes = idma_hardware.buffer_bytes_max;
+ buf->area = (unsigned char *)ioremap(buf->addr, buf->bytes);
+
+ return 0;
+}
+
+static u64 idma_mask = DMA_BIT_MASK(32);
+
+static int idma_new(struct snd_soc_pcm_runtime *rtd)
+{
+ struct snd_card *card = rtd->card->snd_card;
+ struct snd_soc_dai *dai = rtd->cpu_dai;
+ struct snd_pcm *pcm = rtd->pcm;
+ int ret = 0;
+
+ if (!card->dev->dma_mask)
+ card->dev->dma_mask = &idma_mask;
+ if (!card->dev->coherent_dma_mask)
+ card->dev->coherent_dma_mask = DMA_BIT_MASK(32);
+
+ if (dai->driver->playback.channels_min)
+ ret = preallocate_idma_buffer(pcm,
+ SNDRV_PCM_STREAM_PLAYBACK);
+
+ return ret;
+}
+
+void idma_reg_addr_init(void *regs, dma_addr_t addr)
+{
+ spin_lock_init(&idma.lock);
+ idma.regs = regs;
+ idma.lp_tx_addr = addr;
+}
+
+struct snd_soc_platform_driver asoc_idma_platform = {
+ .ops = &idma_ops,
+ .pcm_new = idma_new,
+ .pcm_free = idma_free,
+};
+
+static int __devinit asoc_idma_platform_probe(struct platform_device *pdev)
+{
+ return snd_soc_register_platform(&pdev->dev, &asoc_idma_platform);
+}
+
+static int __devexit asoc_idma_platform_remove(struct platform_device *pdev)
+{
+ snd_soc_unregister_platform(&pdev->dev);
+ return 0;
+}
+
+static struct platform_driver asoc_idma_driver = {
+ .driver = {
+ .name = "samsung-idma",
+ .owner = THIS_MODULE,
+ },
+
+ .probe = asoc_idma_platform_probe,
+ .remove = __devexit_p(asoc_idma_platform_remove),
+};
+
+static int __init asoc_idma_init(void)
+{
+ return platform_driver_register(&asoc_idma_driver);
+}
+module_init(asoc_idma_init);
+
+static void __exit asoc_idma_exit(void)
+{
+ platform_driver_unregister(&asoc_idma_driver);
+}
+module_exit(asoc_idma_exit);
+
+MODULE_AUTHOR("Jaswinder Singh, <jassisinghbrar@gmail.com>");
+MODULE_DESCRIPTION("Samsung ASoC IDMA Driver");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/samsung/idma.h b/sound/soc/samsung/idma.h
new file mode 100644
index 0000000..4827321
--- /dev/null
+++ b/sound/soc/samsung/idma.h
@@ -0,0 +1,26 @@
+/*
+ * sound/soc/samsung/idma.h
+ *
+ * Copyright (c) 2011 Samsung Electronics Co., Ltd
+ * http://www.samsung.com
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ */
+
+#ifndef __SND_SOC_SAMSUNG_IDMA_H_
+#define __SND_SOC_SAMSUNG_IDMA_H_
+
+extern void idma_reg_addr_init(void *regs, dma_addr_t addr);
+
+/* dma_state */
+#define LPAM_DMA_STOP 0
+#define LPAM_DMA_START 1
+
+#define MAX_IDMA_PERIOD (128 * 1024)
+#define MAX_IDMA_BUFFER (160 * 1024)
+
+#endif /* __SND_SOC_SAMSUNG_IDMA_H_ */
diff --git a/sound/soc/samsung/jive_wm8750.c b/sound/soc/samsung/jive_wm8750.c
index 3b53ad5..14eb6ea 100644
--- a/sound/soc/samsung/jive_wm8750.c
+++ b/sound/soc/samsung/jive_wm8750.c
@@ -131,7 +131,7 @@
.cpu_dai_name = "s3c2412-i2s",
.codec_dai_name = "wm8750-hifi",
.platform_name = "samsung-audio",
- .codec_name = "wm8750-codec.0-0x1a",
+ .codec_name = "wm8750-codec.0-001a",
.init = jive_wm8750_init,
.ops = &jive_ops,
};
diff --git a/sound/soc/samsung/speyside.c b/sound/soc/samsung/speyside.c
index d6dee4d..590e927 100644
--- a/sound/soc/samsung/speyside.c
+++ b/sound/soc/samsung/speyside.c
@@ -14,10 +14,10 @@
#include <sound/jack.h>
#include <linux/gpio.h>
-#include "../codecs/wm8915.h"
+#include "../codecs/wm8996.h"
#include "../codecs/wm9081.h"
-#define WM8915_HPSEL_GPIO 214
+#define WM8996_HPSEL_GPIO 214
static int speyside_set_bias_level(struct snd_soc_card *card,
struct snd_soc_dapm_context *dapm,
@@ -31,12 +31,12 @@
switch (level) {
case SND_SOC_BIAS_STANDBY:
- ret = snd_soc_dai_set_sysclk(codec_dai, WM8915_SYSCLK_MCLK2,
+ ret = snd_soc_dai_set_sysclk(codec_dai, WM8996_SYSCLK_MCLK2,
32768, SND_SOC_CLOCK_IN);
if (ret < 0)
return ret;
- ret = snd_soc_dai_set_pll(codec_dai, WM8915_FLL_MCLK2,
+ ret = snd_soc_dai_set_pll(codec_dai, WM8996_FLL_MCLK2,
0, 0, 0);
if (ret < 0) {
pr_err("Failed to stop FLL\n");
@@ -65,7 +65,7 @@
case SND_SOC_BIAS_PREPARE:
if (card->dapm.bias_level == SND_SOC_BIAS_STANDBY) {
ret = snd_soc_dai_set_pll(codec_dai, 0,
- WM8915_FLL_MCLK2,
+ WM8996_FLL_MCLK2,
32768, 48000 * 256);
if (ret < 0) {
pr_err("Failed to start FLL\n");
@@ -73,7 +73,7 @@
}
ret = snd_soc_dai_set_sysclk(codec_dai,
- WM8915_SYSCLK_FLL,
+ WM8996_SYSCLK_FLL,
48000 * 256,
SND_SOC_CLOCK_IN);
if (ret < 0)
@@ -149,26 +149,26 @@
int polarity)
{
speyside_jack_polarity = !polarity;
- gpio_direction_output(WM8915_HPSEL_GPIO, speyside_jack_polarity);
+ gpio_direction_output(WM8996_HPSEL_GPIO, speyside_jack_polarity);
/* Re-run DAPM to make sure we're using the correct mic bias */
snd_soc_dapm_sync(&codec->dapm);
}
-static int speyside_wm8915_init(struct snd_soc_pcm_runtime *rtd)
+static int speyside_wm8996_init(struct snd_soc_pcm_runtime *rtd)
{
struct snd_soc_dai *dai = rtd->codec_dai;
struct snd_soc_codec *codec = rtd->codec;
int ret;
- ret = snd_soc_dai_set_sysclk(dai, WM8915_SYSCLK_MCLK2, 32768, 0);
+ ret = snd_soc_dai_set_sysclk(dai, WM8996_SYSCLK_MCLK2, 32768, 0);
if (ret < 0)
return ret;
- ret = gpio_request(WM8915_HPSEL_GPIO, "HP_SEL");
+ ret = gpio_request(WM8996_HPSEL_GPIO, "HP_SEL");
if (ret != 0)
pr_err("Failed to request HP_SEL GPIO: %d\n", ret);
- gpio_direction_output(WM8915_HPSEL_GPIO, speyside_jack_polarity);
+ gpio_direction_output(WM8996_HPSEL_GPIO, speyside_jack_polarity);
ret = snd_soc_jack_new(codec, "Headset",
SND_JACK_HEADSET | SND_JACK_BTN_0,
@@ -182,7 +182,7 @@
if (ret)
return ret;
- wm8915_detect(codec, &speyside_headset, speyside_set_polarity);
+ wm8996_detect(codec, &speyside_headset, speyside_set_polarity);
return 0;
}
@@ -205,16 +205,16 @@
.name = "CPU",
.stream_name = "CPU",
.cpu_dai_name = "samsung-i2s.0",
- .codec_dai_name = "wm8915-aif1",
+ .codec_dai_name = "wm8996-aif1",
.platform_name = "samsung-audio",
- .codec_name = "wm8915.1-001a",
- .init = speyside_wm8915_init,
+ .codec_name = "wm8996.1-001a",
+ .init = speyside_wm8996_init,
.ops = &speyside_ops,
},
{
.name = "Baseband",
.stream_name = "Baseband",
- .cpu_dai_name = "wm8915-aif2",
+ .cpu_dai_name = "wm8996-aif2",
.codec_dai_name = "wm1250-ev1",
.codec_name = "wm1250-ev1.1-0027",
.ops = &speyside_ops,
diff --git a/sound/soc/samsung/speyside_wm8962.c b/sound/soc/samsung/speyside_wm8962.c
index 8ac42bf..0b9eb5f 100644
--- a/sound/soc/samsung/speyside_wm8962.c
+++ b/sound/soc/samsung/speyside_wm8962.c
@@ -37,7 +37,7 @@
44100 * 256,
SND_SOC_CLOCK_IN);
if (ret < 0) {
- pr_err("Failed to set SYSCLK: %d\n");
+ pr_err("Failed to set SYSCLK: %d\n", ret);
return ret;
}
}
diff --git a/sound/soc/tegra/tegra_pcm.c b/sound/soc/tegra/tegra_pcm.c
index ff86e5e..c7cfd96 100644
--- a/sound/soc/tegra/tegra_pcm.c
+++ b/sound/soc/tegra/tegra_pcm.c
@@ -309,9 +309,14 @@
static void tegra_pcm_deallocate_dma_buffer(struct snd_pcm *pcm, int stream)
{
- struct snd_pcm_substream *substream = pcm->streams[stream].substream;
- struct snd_dma_buffer *buf = &substream->dma_buffer;
+ struct snd_pcm_substream *substream;
+ struct snd_dma_buffer *buf;
+ substream = pcm->streams[stream].substream;
+ if (!substream)
+ return;
+
+ buf = &substream->dma_buffer;
if (!buf->area)
return;
diff --git a/sound/soc/tegra/tegra_wm8903.c b/sound/soc/tegra/tegra_wm8903.c
index a42e9ac..661373c 100644
--- a/sound/soc/tegra/tegra_wm8903.c
+++ b/sound/soc/tegra/tegra_wm8903.c
@@ -56,6 +56,7 @@
#define GPIO_HP_MUTE BIT(1)
#define GPIO_INT_MIC_EN BIT(2)
#define GPIO_EXT_MIC_EN BIT(3)
+#define GPIO_HP_DET BIT(4)
struct tegra_wm8903 {
struct tegra_asoc_utils_data util_data;
@@ -304,6 +305,7 @@
snd_soc_jack_add_gpios(&tegra_wm8903_hp_jack,
1,
&tegra_wm8903_hp_jack_gpio);
+ machine->gpio_requested |= GPIO_HP_DET;
}
snd_soc_jack_new(codec, "Mic Jack", SND_JACK_MICROPHONE,
@@ -429,10 +431,10 @@
struct tegra_wm8903 *machine = snd_soc_card_get_drvdata(card);
struct tegra_wm8903_platform_data *pdata = machine->pdata;
- snd_soc_unregister_card(card);
-
- tegra_asoc_utils_fini(&machine->util_data);
-
+ if (machine->gpio_requested & GPIO_HP_DET)
+ snd_soc_jack_free_gpios(&tegra_wm8903_hp_jack,
+ 1,
+ &tegra_wm8903_hp_jack_gpio);
if (machine->gpio_requested & GPIO_EXT_MIC_EN)
gpio_free(pdata->gpio_ext_mic_en);
if (machine->gpio_requested & GPIO_INT_MIC_EN)
@@ -441,6 +443,11 @@
gpio_free(pdata->gpio_hp_mute);
if (machine->gpio_requested & GPIO_SPKR_EN)
gpio_free(pdata->gpio_spkr_en);
+ machine->gpio_requested = 0;
+
+ snd_soc_unregister_card(card);
+
+ tegra_asoc_utils_fini(&machine->util_data);
kfree(machine);
diff --git a/sound/soc/txx9/txx9aclc.c b/sound/soc/txx9/txx9aclc.c
index 34aa972..3de99af 100644
--- a/sound/soc/txx9/txx9aclc.c
+++ b/sound/soc/txx9/txx9aclc.c
@@ -290,6 +290,7 @@
static int txx9aclc_pcm_new(struct snd_soc_pcm_runtime *rtd)
{
+ struct snd_card *card = rtd->card->snd_card;
struct snd_soc_dai *dai = rtd->cpu_dai;
struct snd_pcm *pcm = rtd->pcm;
struct platform_device *pdev = to_platform_device(dai->platform->dev);
diff --git a/sound/usb/caiaq/audio.c b/sound/usb/caiaq/audio.c
index d0d493c..aa52b3e 100644
--- a/sound/usb/caiaq/audio.c
+++ b/sound/usb/caiaq/audio.c
@@ -614,6 +614,7 @@
struct snd_usb_caiaqdev *dev;
struct urb *out;
int frame, len, send_it = 0, outframe = 0;
+ size_t offset = 0;
if (urb->status || !info)
return;
@@ -634,7 +635,8 @@
len = urb->iso_frame_desc[outframe].actual_length;
out->iso_frame_desc[outframe].length = len;
out->iso_frame_desc[outframe].actual_length = 0;
- out->iso_frame_desc[outframe].offset = BYTES_PER_FRAME * frame;
+ out->iso_frame_desc[outframe].offset = offset;
+ offset += len;
if (len > 0) {
spin_lock(&dev->spinlock);
@@ -650,7 +652,7 @@
}
if (send_it) {
- out->number_of_packets = FRAMES_PER_URB;
+ out->number_of_packets = outframe;
out->transfer_flags = URB_ISO_ASAP;
usb_submit_urb(out, GFP_ATOMIC);
}
diff --git a/sound/usb/caiaq/input.c b/sound/usb/caiaq/input.c
index 4432ef7..a213813 100644
--- a/sound/usb/caiaq/input.c
+++ b/sound/usb/caiaq/input.c
@@ -30,7 +30,7 @@
static unsigned short keycode_rk2[] = { KEY_1, KEY_2, KEY_3, KEY_4,
KEY_5, KEY_6, KEY_7 };
static unsigned short keycode_rk3[] = { KEY_1, KEY_2, KEY_3, KEY_4,
- KEY_5, KEY_6, KEY_7, KEY_5, KEY_6 };
+ KEY_5, KEY_6, KEY_7, KEY_8, KEY_9 };
static unsigned short keycode_kore[] = {
KEY_FN_F1, /* "menu" */
diff --git a/sound/usb/endpoint.c b/sound/usb/endpoint.c
index 7c0d21e..7d46e48 100644
--- a/sound/usb/endpoint.c
+++ b/sound/usb/endpoint.c
@@ -352,7 +352,7 @@
continue;
}
if (((protocol == UAC_VERSION_1) && (fmt->bLength < 8)) ||
- ((protocol == UAC_VERSION_2) && (fmt->bLength != 6))) {
+ ((protocol == UAC_VERSION_2) && (fmt->bLength < 6))) {
snd_printk(KERN_ERR "%d:%u:%d : invalid UAC_FORMAT_TYPE desc\n",
dev->devnum, iface_no, altno);
continue;
diff --git a/sound/usb/mixer.c b/sound/usb/mixer.c
index c22fa76..c04d7c7 100644
--- a/sound/usb/mixer.c
+++ b/sound/usb/mixer.c
@@ -1191,6 +1191,11 @@
if (state->mixer->protocol == UAC_VERSION_1) {
csize = hdr->bControlSize;
+ if (!csize) {
+ snd_printdd(KERN_ERR "usbaudio: unit %u: "
+ "invalid bControlSize == 0\n", unitid);
+ return -EINVAL;
+ }
channels = (hdr->bLength - 7) / csize - 1;
bmaControls = hdr->bmaControls;
} else {
@@ -1934,15 +1939,13 @@
struct mixer_build state;
int err;
const struct usbmix_ctl_map *map;
- struct usb_host_interface *hostif;
void *p;
- hostif = mixer->chip->ctrl_intf;
memset(&state, 0, sizeof(state));
state.chip = mixer->chip;
state.mixer = mixer;
- state.buffer = hostif->extra;
- state.buflen = hostif->extralen;
+ state.buffer = mixer->hostif->extra;
+ state.buflen = mixer->hostif->extralen;
/* check the mapping table */
for (map = usbmix_ctl_maps; map->id; map++) {
@@ -1955,7 +1958,8 @@
}
p = NULL;
- while ((p = snd_usb_find_csint_desc(hostif->extra, hostif->extralen, p, UAC_OUTPUT_TERMINAL)) != NULL) {
+ while ((p = snd_usb_find_csint_desc(mixer->hostif->extra, mixer->hostif->extralen,
+ p, UAC_OUTPUT_TERMINAL)) != NULL) {
if (mixer->protocol == UAC_VERSION_1) {
struct uac1_output_terminal_descriptor *desc = p;
@@ -2162,17 +2166,15 @@
/* create the handler for the optional status interrupt endpoint */
static int snd_usb_mixer_status_create(struct usb_mixer_interface *mixer)
{
- struct usb_host_interface *hostif;
struct usb_endpoint_descriptor *ep;
void *transfer_buffer;
int buffer_length;
unsigned int epnum;
- hostif = mixer->chip->ctrl_intf;
/* we need one interrupt input endpoint */
- if (get_iface_desc(hostif)->bNumEndpoints < 1)
+ if (get_iface_desc(mixer->hostif)->bNumEndpoints < 1)
return 0;
- ep = get_endpoint(hostif, 0);
+ ep = get_endpoint(mixer->hostif, 0);
if (!usb_endpoint_dir_in(ep) || !usb_endpoint_xfer_int(ep))
return 0;
@@ -2202,7 +2204,6 @@
};
struct usb_mixer_interface *mixer;
struct snd_info_entry *entry;
- struct usb_host_interface *host_iface;
int err;
strcpy(chip->card->mixername, "USB Mixer");
@@ -2219,8 +2220,8 @@
return -ENOMEM;
}
- host_iface = &usb_ifnum_to_if(chip->dev, ctrlif)->altsetting[0];
- switch (get_iface_desc(host_iface)->bInterfaceProtocol) {
+ mixer->hostif = &usb_ifnum_to_if(chip->dev, ctrlif)->altsetting[0];
+ switch (get_iface_desc(mixer->hostif)->bInterfaceProtocol) {
case UAC_VERSION_1:
default:
mixer->protocol = UAC_VERSION_1;
diff --git a/sound/usb/mixer.h b/sound/usb/mixer.h
index ae1a14d..81b2d8a 100644
--- a/sound/usb/mixer.h
+++ b/sound/usb/mixer.h
@@ -3,6 +3,7 @@
struct usb_mixer_interface {
struct snd_usb_audio *chip;
+ struct usb_host_interface *hostif;
struct list_head list;
unsigned int ignore_ctl_error;
struct urb *urb;
diff --git a/sound/usb/quirks-table.h b/sound/usb/quirks-table.h
index dba0b7f..a42e3ef 100644
--- a/sound/usb/quirks-table.h
+++ b/sound/usb/quirks-table.h
@@ -1707,6 +1707,40 @@
}
}
},
+{
+ USB_DEVICE(0x0582, 0x0130),
+ .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) {
+ /* .vendor_name = "BOSS", */
+ /* .product_name = "MICRO BR-80", */
+ .ifnum = QUIRK_ANY_INTERFACE,
+ .type = QUIRK_COMPOSITE,
+ .data = (const struct snd_usb_audio_quirk[]) {
+ {
+ .ifnum = 0,
+ .type = QUIRK_IGNORE_INTERFACE
+ },
+ {
+ .ifnum = 1,
+ .type = QUIRK_AUDIO_STANDARD_INTERFACE
+ },
+ {
+ .ifnum = 2,
+ .type = QUIRK_AUDIO_STANDARD_INTERFACE
+ },
+ {
+ .ifnum = 3,
+ .type = QUIRK_MIDI_FIXED_ENDPOINT,
+ .data = & (const struct snd_usb_midi_endpoint_info) {
+ .out_cables = 0x0001,
+ .in_cables = 0x0001
+ }
+ },
+ {
+ .ifnum = -1
+ }
+ }
+ }
+},
/* Guillemot devices */
{
@@ -2417,6 +2451,12 @@
.idProduct = 0x1020,
},
+/* KeithMcMillen Stringport */
+{
+ USB_DEVICE(0x1f38, 0x0001),
+ .bInterfaceClass = USB_CLASS_AUDIO,
+},
+
/* Miditech devices */
{
USB_DEVICE(0x4752, 0x0011),
diff --git a/sound/usb/quirks.c b/sound/usb/quirks.c
index 77762c9..81e07d8 100644
--- a/sound/usb/quirks.c
+++ b/sound/usb/quirks.c
@@ -426,7 +426,7 @@
*/
static int snd_usb_cm6206_boot_quirk(struct usb_device *dev)
{
- int err, reg;
+ int err = 0, reg;
int val[] = {0x2004, 0x3000, 0xf800, 0x143f, 0x0000, 0x3000};
for (reg = 0; reg < ARRAY_SIZE(val); reg++) {
diff --git a/tools/perf/Makefile b/tools/perf/Makefile
index 56d62d3..3b8f7b8 100644
--- a/tools/perf/Makefile
+++ b/tools/perf/Makefile
@@ -181,9 +181,9 @@
$(OUTPUT)python/perf.so: $(PYRF_OBJS)
$(QUIET_GEN)CFLAGS='$(BASIC_CFLAGS)' $(PYTHON_WORD) util/setup.py \
- --quiet build_ext \
- --build-lib='$(OUTPUT)python' \
- --build-temp='$(OUTPUT)python/temp'
+ --quiet build_ext; \
+ mkdir -p $(OUTPUT)python && \
+ cp $(PYTHON_EXTBUILD_LIB)perf.so $(OUTPUT)python/
#
# No Perl scripts right now:
#
@@ -509,9 +509,13 @@
PYTHON_WORD := $(call shell-wordify,$(PYTHON))
- python-clean := $(PYTHON_WORD) util/setup.py clean \
- --build-lib='$(OUTPUT)python' \
- --build-temp='$(OUTPUT)python/temp'
+ # python extension build directories
+ PYTHON_EXTBUILD := $(OUTPUT)python_ext_build/
+ PYTHON_EXTBUILD_LIB := $(PYTHON_EXTBUILD)lib/
+ PYTHON_EXTBUILD_TMP := $(PYTHON_EXTBUILD)tmp/
+ export PYTHON_EXTBUILD_LIB PYTHON_EXTBUILD_TMP
+
+ python-clean := rm -rf $(PYTHON_EXTBUILD) $(OUTPUT)python/perf.so
ifdef NO_LIBPYTHON
$(call disable-python)
@@ -868,6 +872,9 @@
$(INSTALL) scripts/python/*.py -t '$(DESTDIR_SQ)$(perfexec_instdir_SQ)/scripts/python'
$(INSTALL) scripts/python/bin/* -t '$(DESTDIR_SQ)$(perfexec_instdir_SQ)/scripts/python/bin'
+install-python_ext:
+ $(PYTHON_WORD) util/setup.py --quiet install --root='/$(DESTDIR_SQ)'
+
install-doc:
$(MAKE) -C Documentation install
@@ -895,7 +902,7 @@
### Cleaning rules
clean:
- $(RM) $(OUTPUT){*.o,*/*.o,*/*/*.o,*/*/*/*.o,$(LIB_FILE),perf-archive}
+ $(RM) $(LIB_OBJS) $(BUILTIN_OBJS) $(LIB_FILE) $(OUTPUT)perf-archive $(OUTPUT)perf.o $(LANG_BINDINGS)
$(RM) $(ALL_PROGRAMS) perf
$(RM) *.spec *.pyc *.pyo */*.pyc */*.pyo $(OUTPUT)common-cmds.h TAGS tags cscope*
$(MAKE) -C Documentation/ clean
diff --git a/tools/perf/builtin-lock.c b/tools/perf/builtin-lock.c
index 9ac05aa..899080a 100644
--- a/tools/perf/builtin-lock.c
+++ b/tools/perf/builtin-lock.c
@@ -942,10 +942,10 @@
"-f",
"-m", "1024",
"-c", "1",
- "-e", "lock:lock_acquire:r",
- "-e", "lock:lock_acquired:r",
- "-e", "lock:lock_contended:r",
- "-e", "lock:lock_release:r",
+ "-e", "lock:lock_acquire",
+ "-e", "lock:lock_acquired",
+ "-e", "lock:lock_contended",
+ "-e", "lock:lock_release",
};
static int __cmd_record(int argc, const char **argv)
diff --git a/tools/perf/builtin-probe.c b/tools/perf/builtin-probe.c
index 5f2a5c7..710ae3d 100644
--- a/tools/perf/builtin-probe.c
+++ b/tools/perf/builtin-probe.c
@@ -134,10 +134,18 @@
{
int ret = 0;
- if (str)
- ret = parse_line_range_desc(str, ¶ms.line_range);
- INIT_LIST_HEAD(¶ms.line_range.line_list);
+ if (!str)
+ return 0;
+
+ if (params.show_lines) {
+ pr_warning("Warning: more than one --line options are"
+ " detected. Only the first one is valid.\n");
+ return 0;
+ }
+
params.show_lines = true;
+ ret = parse_line_range_desc(str, ¶ms.line_range);
+ INIT_LIST_HEAD(¶ms.line_range.line_list);
return ret;
}
diff --git a/tools/perf/builtin-record.c b/tools/perf/builtin-record.c
index 80dc5b7..f6426b4 100644
--- a/tools/perf/builtin-record.c
+++ b/tools/perf/builtin-record.c
@@ -30,8 +30,6 @@
#include <sched.h>
#include <sys/mman.h>
-#define FD(e, x, y) (*(int *)xyarray__entry(e->fd, x, y))
-
enum write_mode_t {
WRITE_FORCE,
WRITE_APPEND
@@ -438,7 +436,6 @@
static int __cmd_record(int argc, const char **argv)
{
- int i;
struct stat st;
int flags;
int err;
@@ -682,7 +679,6 @@
for (;;) {
int hits = samples;
- int thread;
mmap_read_all();
@@ -693,19 +689,8 @@
waking++;
}
- if (done) {
- for (i = 0; i < evsel_list->cpus->nr; i++) {
- struct perf_evsel *pos;
-
- list_for_each_entry(pos, &evsel_list->entries, node) {
- for (thread = 0;
- thread < evsel_list->threads->nr;
- thread++)
- ioctl(FD(pos, i, thread),
- PERF_EVENT_IOC_DISABLE);
- }
- }
- }
+ if (done)
+ perf_evlist__disable(evsel_list);
}
if (quiet || signr == SIGUSR1)
diff --git a/tools/perf/builtin-report.c b/tools/perf/builtin-report.c
index f854efd..d7ff277 100644
--- a/tools/perf/builtin-report.c
+++ b/tools/perf/builtin-report.c
@@ -162,23 +162,22 @@
{
if (!(self->sample_type & PERF_SAMPLE_CALLCHAIN)) {
if (sort__has_parent) {
- fprintf(stderr, "selected --sort parent, but no"
- " callchain data. Did you call"
- " perf record without -g?\n");
+ ui__warning("Selected --sort parent, but no "
+ "callchain data. Did you call "
+ "'perf record' without -g?\n");
return -EINVAL;
}
if (symbol_conf.use_callchain) {
- fprintf(stderr, "selected -g but no callchain data."
- " Did you call perf record without"
- " -g?\n");
+ ui__warning("Selected -g but no callchain data. Did "
+ "you call 'perf record' without -g?\n");
return -1;
}
} else if (!dont_use_callchains && callchain_param.mode != CHAIN_NONE &&
!symbol_conf.use_callchain) {
symbol_conf.use_callchain = true;
if (callchain_register_param(&callchain_param) < 0) {
- fprintf(stderr, "Can't register callchain"
- " params\n");
+ ui__warning("Can't register callchain "
+ "params.\n");
return -EINVAL;
}
}
diff --git a/tools/perf/builtin-sched.c b/tools/perf/builtin-sched.c
index dcfe887..5177964 100644
--- a/tools/perf/builtin-sched.c
+++ b/tools/perf/builtin-sched.c
@@ -1637,23 +1637,29 @@
.ordered_samples = true,
};
-static int read_events(void)
+static void read_events(bool destroy, struct perf_session **psession)
{
int err = -EINVAL;
struct perf_session *session = perf_session__new(input_name, O_RDONLY,
0, false, &event_ops);
if (session == NULL)
- return -ENOMEM;
+ die("No Memory");
if (perf_session__has_traces(session, "record -R")) {
err = perf_session__process_events(session, &event_ops);
+ if (err)
+ die("Failed to process events, error %d", err);
+
nr_events = session->hists.stats.nr_events[0];
nr_lost_events = session->hists.stats.total_lost;
nr_lost_chunks = session->hists.stats.nr_events[PERF_RECORD_LOST];
}
- perf_session__delete(session);
- return err;
+ if (destroy)
+ perf_session__delete(session);
+
+ if (psession)
+ *psession = session;
}
static void print_bad_events(void)
@@ -1689,9 +1695,10 @@
static void __cmd_lat(void)
{
struct rb_node *next;
+ struct perf_session *session;
setup_pager();
- read_events();
+ read_events(false, &session);
sort_lat();
printf("\n ---------------------------------------------------------------------------------------------------------------\n");
@@ -1717,6 +1724,7 @@
print_bad_events();
printf("\n");
+ perf_session__delete(session);
}
static struct trace_sched_handler map_ops = {
@@ -1731,7 +1739,7 @@
max_cpu = sysconf(_SC_NPROCESSORS_CONF);
setup_pager();
- read_events();
+ read_events(true, NULL);
print_bad_events();
}
@@ -1744,7 +1752,7 @@
test_calibrations();
- read_events();
+ read_events(true, NULL);
printf("nr_run_events: %ld\n", nr_run_events);
printf("nr_sleep_events: %ld\n", nr_sleep_events);
@@ -1769,7 +1777,7 @@
static const char * const sched_usage[] = {
- "perf sched [<options>] {record|latency|map|replay|trace}",
+ "perf sched [<options>] {record|latency|map|replay|script}",
NULL
};
diff --git a/tools/perf/util/config.c b/tools/perf/util/config.c
index e02d78c..fe02903 100644
--- a/tools/perf/util/config.c
+++ b/tools/perf/util/config.c
@@ -399,7 +399,6 @@
int perf_config(config_fn_t fn, void *data)
{
int ret = 0, found = 0;
- char *repo_config = NULL;
const char *home = NULL;
/* Setting $PERF_CONFIG makes perf read _only_ the given config file. */
@@ -414,19 +413,32 @@
home = getenv("HOME");
if (perf_config_global() && home) {
char *user_config = strdup(mkpath("%s/.perfconfig", home));
- if (!access(user_config, R_OK)) {
- ret += perf_config_from_file(fn, user_config, data);
- found += 1;
+ struct stat st;
+
+ if (user_config == NULL) {
+ warning("Not enough memory to process %s/.perfconfig, "
+ "ignoring it.", home);
+ goto out;
}
+
+ if (stat(user_config, &st) < 0)
+ goto out_free;
+
+ if (st.st_uid && (st.st_uid != geteuid())) {
+ warning("File %s not owned by current user or root, "
+ "ignoring it.", user_config);
+ goto out_free;
+ }
+
+ if (!st.st_size)
+ goto out_free;
+
+ ret += perf_config_from_file(fn, user_config, data);
+ found += 1;
+out_free:
free(user_config);
}
-
- repo_config = perf_pathdup("config");
- if (!access(repo_config, R_OK)) {
- ret += perf_config_from_file(fn, repo_config, data);
- found += 1;
- }
- free(repo_config);
+out:
if (found == 0)
return -1;
return ret;
diff --git a/tools/perf/util/dwarf-aux.c b/tools/perf/util/dwarf-aux.c
index fddf40f..ee51e9b 100644
--- a/tools/perf/util/dwarf-aux.c
+++ b/tools/perf/util/dwarf-aux.c
@@ -96,6 +96,39 @@
return *lineno ?: -ENOENT;
}
+static int __die_find_inline_cb(Dwarf_Die *die_mem, void *data);
+
+/**
+ * cu_walk_functions_at - Walk on function DIEs at given address
+ * @cu_die: A CU DIE
+ * @addr: An address
+ * @callback: A callback which called with found DIEs
+ * @data: A user data
+ *
+ * Walk on function DIEs at given @addr in @cu_die. Passed DIEs
+ * should be subprogram or inlined-subroutines.
+ */
+int cu_walk_functions_at(Dwarf_Die *cu_die, Dwarf_Addr addr,
+ int (*callback)(Dwarf_Die *, void *), void *data)
+{
+ Dwarf_Die die_mem;
+ Dwarf_Die *sc_die;
+ int ret = -ENOENT;
+
+ /* Inlined function could be recursive. Trace it until fail */
+ for (sc_die = die_find_realfunc(cu_die, addr, &die_mem);
+ sc_die != NULL;
+ sc_die = die_find_child(sc_die, __die_find_inline_cb, &addr,
+ &die_mem)) {
+ ret = callback(sc_die, data);
+ if (ret)
+ break;
+ }
+
+ return ret;
+
+}
+
/**
* die_compare_name - Compare diename and tname
* @dw_die: a DIE
@@ -198,6 +231,19 @@
return 0;
}
+/* Get attribute and translate it as a sdata */
+static int die_get_attr_sdata(Dwarf_Die *tp_die, unsigned int attr_name,
+ Dwarf_Sword *result)
+{
+ Dwarf_Attribute attr;
+
+ if (dwarf_attr(tp_die, attr_name, &attr) == NULL ||
+ dwarf_formsdata(&attr, result) != 0)
+ return -ENOENT;
+
+ return 0;
+}
+
/**
* die_is_signed_type - Check whether a type DIE is signed or not
* @tp_die: a DIE of a type
@@ -250,6 +296,50 @@
return 0;
}
+/* Get the call file index number in CU DIE */
+static int die_get_call_fileno(Dwarf_Die *in_die)
+{
+ Dwarf_Sword idx;
+
+ if (die_get_attr_sdata(in_die, DW_AT_call_file, &idx) == 0)
+ return (int)idx;
+ else
+ return -ENOENT;
+}
+
+/* Get the declared file index number in CU DIE */
+static int die_get_decl_fileno(Dwarf_Die *pdie)
+{
+ Dwarf_Sword idx;
+
+ if (die_get_attr_sdata(pdie, DW_AT_decl_file, &idx) == 0)
+ return (int)idx;
+ else
+ return -ENOENT;
+}
+
+/**
+ * die_get_call_file - Get callsite file name of inlined function instance
+ * @in_die: a DIE of an inlined function instance
+ *
+ * Get call-site file name of @in_die. This means from which file the inline
+ * function is called.
+ */
+const char *die_get_call_file(Dwarf_Die *in_die)
+{
+ Dwarf_Die cu_die;
+ Dwarf_Files *files;
+ int idx;
+
+ idx = die_get_call_fileno(in_die);
+ if (idx < 0 || !dwarf_diecu(in_die, &cu_die, NULL, NULL) ||
+ dwarf_getsrcfiles(&cu_die, &files, NULL) != 0)
+ return NULL;
+
+ return dwarf_filesrc(files, idx, NULL, NULL);
+}
+
+
/**
* die_find_child - Generic DIE search function in DIE tree
* @rt_die: a root DIE
@@ -374,9 +464,78 @@
return die_mem;
}
+struct __instance_walk_param {
+ void *addr;
+ int (*callback)(Dwarf_Die *, void *);
+ void *data;
+ int retval;
+};
+
+static int __die_walk_instances_cb(Dwarf_Die *inst, void *data)
+{
+ struct __instance_walk_param *iwp = data;
+ Dwarf_Attribute attr_mem;
+ Dwarf_Die origin_mem;
+ Dwarf_Attribute *attr;
+ Dwarf_Die *origin;
+ int tmp;
+
+ attr = dwarf_attr(inst, DW_AT_abstract_origin, &attr_mem);
+ if (attr == NULL)
+ return DIE_FIND_CB_CONTINUE;
+
+ origin = dwarf_formref_die(attr, &origin_mem);
+ if (origin == NULL || origin->addr != iwp->addr)
+ return DIE_FIND_CB_CONTINUE;
+
+ /* Ignore redundant instances */
+ if (dwarf_tag(inst) == DW_TAG_inlined_subroutine) {
+ dwarf_decl_line(origin, &tmp);
+ if (die_get_call_lineno(inst) == tmp) {
+ tmp = die_get_decl_fileno(origin);
+ if (die_get_call_fileno(inst) == tmp)
+ return DIE_FIND_CB_CONTINUE;
+ }
+ }
+
+ iwp->retval = iwp->callback(inst, iwp->data);
+
+ return (iwp->retval) ? DIE_FIND_CB_END : DIE_FIND_CB_CONTINUE;
+}
+
+/**
+ * die_walk_instances - Walk on instances of given DIE
+ * @or_die: an abstract original DIE
+ * @callback: a callback function which is called with instance DIE
+ * @data: user data
+ *
+ * Walk on the instances of give @in_die. @in_die must be an inlined function
+ * declartion. This returns the return value of @callback if it returns
+ * non-zero value, or -ENOENT if there is no instance.
+ */
+int die_walk_instances(Dwarf_Die *or_die, int (*callback)(Dwarf_Die *, void *),
+ void *data)
+{
+ Dwarf_Die cu_die;
+ Dwarf_Die die_mem;
+ struct __instance_walk_param iwp = {
+ .addr = or_die->addr,
+ .callback = callback,
+ .data = data,
+ .retval = -ENOENT,
+ };
+
+ if (dwarf_diecu(or_die, &cu_die, NULL, NULL) == NULL)
+ return -ENOENT;
+
+ die_find_child(&cu_die, __die_walk_instances_cb, &iwp, &die_mem);
+
+ return iwp.retval;
+}
+
/* Line walker internal parameters */
struct __line_walk_param {
- const char *fname;
+ bool recursive;
line_walk_callback_t callback;
void *data;
int retval;
@@ -385,39 +544,56 @@
static int __die_walk_funclines_cb(Dwarf_Die *in_die, void *data)
{
struct __line_walk_param *lw = data;
- Dwarf_Addr addr;
+ Dwarf_Addr addr = 0;
+ const char *fname;
int lineno;
if (dwarf_tag(in_die) == DW_TAG_inlined_subroutine) {
+ fname = die_get_call_file(in_die);
lineno = die_get_call_lineno(in_die);
- if (lineno > 0 && dwarf_entrypc(in_die, &addr) == 0) {
- lw->retval = lw->callback(lw->fname, lineno, addr,
- lw->data);
+ if (fname && lineno > 0 && dwarf_entrypc(in_die, &addr) == 0) {
+ lw->retval = lw->callback(fname, lineno, addr, lw->data);
if (lw->retval != 0)
return DIE_FIND_CB_END;
}
}
- return DIE_FIND_CB_SIBLING;
+ if (!lw->recursive)
+ /* Don't need to search recursively */
+ return DIE_FIND_CB_SIBLING;
+
+ if (addr) {
+ fname = dwarf_decl_file(in_die);
+ if (fname && dwarf_decl_line(in_die, &lineno) == 0) {
+ lw->retval = lw->callback(fname, lineno, addr, lw->data);
+ if (lw->retval != 0)
+ return DIE_FIND_CB_END;
+ }
+ }
+
+ /* Continue to search nested inlined function call-sites */
+ return DIE_FIND_CB_CONTINUE;
}
/* Walk on lines of blocks included in given DIE */
-static int __die_walk_funclines(Dwarf_Die *sp_die,
+static int __die_walk_funclines(Dwarf_Die *sp_die, bool recursive,
line_walk_callback_t callback, void *data)
{
struct __line_walk_param lw = {
+ .recursive = recursive,
.callback = callback,
.data = data,
.retval = 0,
};
Dwarf_Die die_mem;
Dwarf_Addr addr;
+ const char *fname;
int lineno;
/* Handle function declaration line */
- lw.fname = dwarf_decl_file(sp_die);
- if (lw.fname && dwarf_decl_line(sp_die, &lineno) == 0 &&
+ fname = dwarf_decl_file(sp_die);
+ if (fname && dwarf_decl_line(sp_die, &lineno) == 0 &&
dwarf_entrypc(sp_die, &addr) == 0) {
- lw.retval = callback(lw.fname, lineno, addr, data);
+ lw.retval = callback(fname, lineno, addr, data);
if (lw.retval != 0)
goto done;
}
@@ -430,7 +606,7 @@
{
struct __line_walk_param *lw = data;
- lw->retval = __die_walk_funclines(sp_die, lw->callback, lw->data);
+ lw->retval = __die_walk_funclines(sp_die, true, lw->callback, lw->data);
if (lw->retval != 0)
return DWARF_CB_ABORT;
@@ -439,7 +615,7 @@
/**
* die_walk_lines - Walk on lines inside given DIE
- * @rt_die: a root DIE (CU or subprogram)
+ * @rt_die: a root DIE (CU, subprogram or inlined_subroutine)
* @callback: callback routine
* @data: user data
*
@@ -460,12 +636,12 @@
size_t nlines, i;
/* Get the CU die */
- if (dwarf_tag(rt_die) == DW_TAG_subprogram)
+ if (dwarf_tag(rt_die) != DW_TAG_compile_unit)
cu_die = dwarf_diecu(rt_die, &die_mem, NULL, NULL);
else
cu_die = rt_die;
if (!cu_die) {
- pr_debug2("Failed to get CU from subprogram\n");
+ pr_debug2("Failed to get CU from given DIE.\n");
return -EINVAL;
}
@@ -509,7 +685,11 @@
* subroutines. We have to check functions list or given function.
*/
if (rt_die != cu_die)
- ret = __die_walk_funclines(rt_die, callback, data);
+ /*
+ * Don't need walk functions recursively, because nested
+ * inlined functions don't have lines of the specified DIE.
+ */
+ ret = __die_walk_funclines(rt_die, false, callback, data);
else {
struct __line_walk_param param = {
.callback = callback,
diff --git a/tools/perf/util/dwarf-aux.h b/tools/perf/util/dwarf-aux.h
index bc3b211..6ce1717 100644
--- a/tools/perf/util/dwarf-aux.h
+++ b/tools/perf/util/dwarf-aux.h
@@ -34,12 +34,19 @@
extern int cu_find_lineinfo(Dwarf_Die *cudie, unsigned long addr,
const char **fname, int *lineno);
+/* Walk on funcitons at given address */
+extern int cu_walk_functions_at(Dwarf_Die *cu_die, Dwarf_Addr addr,
+ int (*callback)(Dwarf_Die *, void *), void *data);
+
/* Compare diename and tname */
extern bool die_compare_name(Dwarf_Die *dw_die, const char *tname);
/* Get callsite line number of inline-function instance */
extern int die_get_call_lineno(Dwarf_Die *in_die);
+/* Get callsite file name of inlined function instance */
+extern const char *die_get_call_file(Dwarf_Die *in_die);
+
/* Get type die */
extern Dwarf_Die *die_get_type(Dwarf_Die *vr_die, Dwarf_Die *die_mem);
@@ -73,6 +80,10 @@
extern Dwarf_Die *die_find_inlinefunc(Dwarf_Die *sp_die, Dwarf_Addr addr,
Dwarf_Die *die_mem);
+/* Walk on the instances of given DIE */
+extern int die_walk_instances(Dwarf_Die *in_die,
+ int (*callback)(Dwarf_Die *, void *), void *data);
+
/* Walker on lines (Note: line number will not be sorted) */
typedef int (* line_walk_callback_t) (const char *fname, int lineno,
Dwarf_Addr addr, void *data);
diff --git a/tools/perf/util/evlist.c b/tools/perf/util/evlist.c
index b021ea9..e03e7bc 100644
--- a/tools/perf/util/evlist.c
+++ b/tools/perf/util/evlist.c
@@ -91,6 +91,19 @@
return 0;
}
+void perf_evlist__disable(struct perf_evlist *evlist)
+{
+ int cpu, thread;
+ struct perf_evsel *pos;
+
+ for (cpu = 0; cpu < evlist->cpus->nr; cpu++) {
+ list_for_each_entry(pos, &evlist->entries, node) {
+ for (thread = 0; thread < evlist->threads->nr; thread++)
+ ioctl(FD(pos, cpu, thread), PERF_EVENT_IOC_DISABLE);
+ }
+ }
+}
+
int perf_evlist__alloc_pollfd(struct perf_evlist *evlist)
{
int nfds = evlist->cpus->nr * evlist->threads->nr * evlist->nr_entries;
diff --git a/tools/perf/util/evlist.h b/tools/perf/util/evlist.h
index b2b8623..ce85ae9 100644
--- a/tools/perf/util/evlist.h
+++ b/tools/perf/util/evlist.h
@@ -53,6 +53,8 @@
int perf_evlist__mmap(struct perf_evlist *evlist, int pages, bool overwrite);
void perf_evlist__munmap(struct perf_evlist *evlist);
+void perf_evlist__disable(struct perf_evlist *evlist);
+
static inline void perf_evlist__set_maps(struct perf_evlist *evlist,
struct cpu_map *cpus,
struct thread_map *threads)
diff --git a/tools/perf/util/header.c b/tools/perf/util/header.c
index cb2959a..b6c1ad1 100644
--- a/tools/perf/util/header.c
+++ b/tools/perf/util/header.c
@@ -189,8 +189,8 @@
const char *name, bool is_kallsyms)
{
const size_t size = PATH_MAX;
- char *realname, *filename = malloc(size),
- *linkname = malloc(size), *targetname;
+ char *realname, *filename = zalloc(size),
+ *linkname = zalloc(size), *targetname;
int len, err = -1;
if (is_kallsyms) {
@@ -254,8 +254,8 @@
int build_id_cache__remove_s(const char *sbuild_id, const char *debugdir)
{
const size_t size = PATH_MAX;
- char *filename = malloc(size),
- *linkname = malloc(size);
+ char *filename = zalloc(size),
+ *linkname = zalloc(size);
int err = -1;
if (filename == NULL || linkname == NULL)
@@ -726,7 +726,16 @@
return -1;
bev.header = old_bev.header;
- bev.pid = 0;
+
+ /*
+ * As the pid is the missing value, we need to fill
+ * it properly. The header.misc value give us nice hint.
+ */
+ bev.pid = HOST_KERNEL_ID;
+ if (bev.header.misc == PERF_RECORD_MISC_GUEST_USER ||
+ bev.header.misc == PERF_RECORD_MISC_GUEST_KERNEL)
+ bev.pid = DEFAULT_GUEST_KERNEL_ID;
+
memcpy(bev.build_id, old_bev.build_id, sizeof(bev.build_id));
__event_process_build_id(&bev, filename, session);
diff --git a/tools/perf/util/probe-event.c b/tools/perf/util/probe-event.c
index b82d54f..1c7bfa5 100644
--- a/tools/perf/util/probe-event.c
+++ b/tools/perf/util/probe-event.c
@@ -1820,11 +1820,15 @@
ret = -ENOMEM;
goto error;
}
- tev->point.module = strdup(module);
- if (tev->point.module == NULL) {
- ret = -ENOMEM;
- goto error;
+
+ if (module) {
+ tev->point.module = strdup(module);
+ if (tev->point.module == NULL) {
+ ret = -ENOMEM;
+ goto error;
+ }
}
+
tev->point.offset = pev->point.offset;
tev->point.retprobe = pev->point.retprobe;
tev->nargs = pev->nargs;
diff --git a/tools/perf/util/probe-finder.c b/tools/perf/util/probe-finder.c
index 3e44a3e..555fc38 100644
--- a/tools/perf/util/probe-finder.c
+++ b/tools/perf/util/probe-finder.c
@@ -612,12 +612,12 @@
return ret;
}
-/* Find a variable in a subprogram die */
-static int find_variable(Dwarf_Die *sp_die, struct probe_finder *pf)
+/* Find a variable in a scope DIE */
+static int find_variable(Dwarf_Die *sc_die, struct probe_finder *pf)
{
- Dwarf_Die vr_die, *scopes;
+ Dwarf_Die vr_die;
char buf[32], *ptr;
- int ret, nscopes;
+ int ret = 0;
if (!is_c_varname(pf->pvar->var)) {
/* Copy raw parameters */
@@ -652,30 +652,16 @@
if (pf->tvar->name == NULL)
return -ENOMEM;
- pr_debug("Searching '%s' variable in context.\n",
- pf->pvar->var);
+ pr_debug("Searching '%s' variable in context.\n", pf->pvar->var);
/* Search child die for local variables and parameters. */
- if (die_find_variable_at(sp_die, pf->pvar->var, pf->addr, &vr_die))
- ret = convert_variable(&vr_die, pf);
- else {
- /* Search upper class */
- nscopes = dwarf_getscopes_die(sp_die, &scopes);
- while (nscopes-- > 1) {
- pr_debug("Searching variables in %s\n",
- dwarf_diename(&scopes[nscopes]));
- /* We should check this scope, so give dummy address */
- if (die_find_variable_at(&scopes[nscopes],
- pf->pvar->var, 0,
- &vr_die)) {
- ret = convert_variable(&vr_die, pf);
- goto found;
- }
- }
- if (scopes)
- free(scopes);
- ret = -ENOENT;
+ if (!die_find_variable_at(sc_die, pf->pvar->var, pf->addr, &vr_die)) {
+ /* Search again in global variables */
+ if (!die_find_variable_at(&pf->cu_die, pf->pvar->var, 0, &vr_die))
+ ret = -ENOENT;
}
-found:
+ if (ret == 0)
+ ret = convert_variable(&vr_die, pf);
+
if (ret < 0)
pr_warning("Failed to find '%s' in this function.\n",
pf->pvar->var);
@@ -718,26 +704,30 @@
return 0;
}
-/* Call probe_finder callback with real subprogram DIE */
-static int call_probe_finder(Dwarf_Die *sp_die, struct probe_finder *pf)
+/* Call probe_finder callback with scope DIE */
+static int call_probe_finder(Dwarf_Die *sc_die, struct probe_finder *pf)
{
- Dwarf_Die die_mem;
Dwarf_Attribute fb_attr;
size_t nops;
int ret;
- /* If no real subprogram, find a real one */
- if (!sp_die || dwarf_tag(sp_die) != DW_TAG_subprogram) {
- sp_die = die_find_realfunc(&pf->cu_die, pf->addr, &die_mem);
- if (!sp_die) {
+ if (!sc_die) {
+ pr_err("Caller must pass a scope DIE. Program error.\n");
+ return -EINVAL;
+ }
+
+ /* If not a real subprogram, find a real one */
+ if (dwarf_tag(sc_die) != DW_TAG_subprogram) {
+ if (!die_find_realfunc(&pf->cu_die, pf->addr, &pf->sp_die)) {
pr_warning("Failed to find probe point in any "
"functions.\n");
return -ENOENT;
}
- }
+ } else
+ memcpy(&pf->sp_die, sc_die, sizeof(Dwarf_Die));
- /* Get the frame base attribute/ops */
- dwarf_attr(sp_die, DW_AT_frame_base, &fb_attr);
+ /* Get the frame base attribute/ops from subprogram */
+ dwarf_attr(&pf->sp_die, DW_AT_frame_base, &fb_attr);
ret = dwarf_getlocation_addr(&fb_attr, pf->addr, &pf->fb_ops, &nops, 1);
if (ret <= 0 || nops == 0) {
pf->fb_ops = NULL;
@@ -755,7 +745,7 @@
}
/* Call finder's callback handler */
- ret = pf->callback(sp_die, pf);
+ ret = pf->callback(sc_die, pf);
/* *pf->fb_ops will be cached in libdw. Don't free it. */
pf->fb_ops = NULL;
@@ -763,17 +753,82 @@
return ret;
}
+struct find_scope_param {
+ const char *function;
+ const char *file;
+ int line;
+ int diff;
+ Dwarf_Die *die_mem;
+ bool found;
+};
+
+static int find_best_scope_cb(Dwarf_Die *fn_die, void *data)
+{
+ struct find_scope_param *fsp = data;
+ const char *file;
+ int lno;
+
+ /* Skip if declared file name does not match */
+ if (fsp->file) {
+ file = dwarf_decl_file(fn_die);
+ if (!file || strcmp(fsp->file, file) != 0)
+ return 0;
+ }
+ /* If the function name is given, that's what user expects */
+ if (fsp->function) {
+ if (die_compare_name(fn_die, fsp->function)) {
+ memcpy(fsp->die_mem, fn_die, sizeof(Dwarf_Die));
+ fsp->found = true;
+ return 1;
+ }
+ } else {
+ /* With the line number, find the nearest declared DIE */
+ dwarf_decl_line(fn_die, &lno);
+ if (lno < fsp->line && fsp->diff > fsp->line - lno) {
+ /* Keep a candidate and continue */
+ fsp->diff = fsp->line - lno;
+ memcpy(fsp->die_mem, fn_die, sizeof(Dwarf_Die));
+ fsp->found = true;
+ }
+ }
+ return 0;
+}
+
+/* Find an appropriate scope fits to given conditions */
+static Dwarf_Die *find_best_scope(struct probe_finder *pf, Dwarf_Die *die_mem)
+{
+ struct find_scope_param fsp = {
+ .function = pf->pev->point.function,
+ .file = pf->fname,
+ .line = pf->lno,
+ .diff = INT_MAX,
+ .die_mem = die_mem,
+ .found = false,
+ };
+
+ cu_walk_functions_at(&pf->cu_die, pf->addr, find_best_scope_cb, &fsp);
+
+ return fsp.found ? die_mem : NULL;
+}
+
static int probe_point_line_walker(const char *fname, int lineno,
Dwarf_Addr addr, void *data)
{
struct probe_finder *pf = data;
+ Dwarf_Die *sc_die, die_mem;
int ret;
if (lineno != pf->lno || strtailcmp(fname, pf->fname) != 0)
return 0;
pf->addr = addr;
- ret = call_probe_finder(NULL, pf);
+ sc_die = find_best_scope(pf, &die_mem);
+ if (!sc_die) {
+ pr_warning("Failed to find scope of probe point.\n");
+ return -ENOENT;
+ }
+
+ ret = call_probe_finder(sc_die, pf);
/* Continue if no error, because the line will be in inline function */
return ret < 0 ? ret : 0;
@@ -827,6 +882,7 @@
Dwarf_Addr addr, void *data)
{
struct probe_finder *pf = data;
+ Dwarf_Die *sc_die, die_mem;
int ret;
if (!line_list__has_line(&pf->lcache, lineno) ||
@@ -836,7 +892,14 @@
pr_debug("Probe line found: line:%d addr:0x%llx\n",
lineno, (unsigned long long)addr);
pf->addr = addr;
- ret = call_probe_finder(NULL, pf);
+ pf->lno = lineno;
+ sc_die = find_best_scope(pf, &die_mem);
+ if (!sc_die) {
+ pr_warning("Failed to find scope of probe point.\n");
+ return -ENOENT;
+ }
+
+ ret = call_probe_finder(sc_die, pf);
/*
* Continue if no error, because the lazy pattern will match
@@ -861,42 +924,39 @@
return die_walk_lines(sp_die, probe_point_lazy_walker, pf);
}
-/* Callback parameter with return value */
-struct dwarf_callback_param {
- void *data;
- int retval;
-};
-
static int probe_point_inline_cb(Dwarf_Die *in_die, void *data)
{
- struct dwarf_callback_param *param = data;
- struct probe_finder *pf = param->data;
+ struct probe_finder *pf = data;
struct perf_probe_point *pp = &pf->pev->point;
Dwarf_Addr addr;
+ int ret;
if (pp->lazy_line)
- param->retval = find_probe_point_lazy(in_die, pf);
+ ret = find_probe_point_lazy(in_die, pf);
else {
/* Get probe address */
if (dwarf_entrypc(in_die, &addr) != 0) {
pr_warning("Failed to get entry address of %s.\n",
dwarf_diename(in_die));
- param->retval = -ENOENT;
- return DWARF_CB_ABORT;
+ return -ENOENT;
}
pf->addr = addr;
pf->addr += pp->offset;
pr_debug("found inline addr: 0x%jx\n",
(uintmax_t)pf->addr);
- param->retval = call_probe_finder(in_die, pf);
- if (param->retval < 0)
- return DWARF_CB_ABORT;
+ ret = call_probe_finder(in_die, pf);
}
- return DWARF_CB_OK;
+ return ret;
}
+/* Callback parameter with return value for libdw */
+struct dwarf_callback_param {
+ void *data;
+ int retval;
+};
+
/* Search function from function name */
static int probe_point_search_cb(Dwarf_Die *sp_die, void *data)
{
@@ -933,14 +993,10 @@
/* TODO: Check the address in this function */
param->retval = call_probe_finder(sp_die, pf);
}
- } else {
- struct dwarf_callback_param _param = {.data = (void *)pf,
- .retval = 0};
+ } else
/* Inlined function: search instances */
- dwarf_func_inline_instances(sp_die, probe_point_inline_cb,
- &_param);
- param->retval = _param.retval;
- }
+ param->retval = die_walk_instances(sp_die,
+ probe_point_inline_cb, (void *)pf);
return DWARF_CB_ABORT; /* Exit; no same symbol in this CU. */
}
@@ -1060,7 +1116,7 @@
}
/* Add a found probe point into trace event list */
-static int add_probe_trace_event(Dwarf_Die *sp_die, struct probe_finder *pf)
+static int add_probe_trace_event(Dwarf_Die *sc_die, struct probe_finder *pf)
{
struct trace_event_finder *tf =
container_of(pf, struct trace_event_finder, pf);
@@ -1075,8 +1131,9 @@
}
tev = &tf->tevs[tf->ntevs++];
- ret = convert_to_trace_point(sp_die, pf->addr, pf->pev->point.retprobe,
- &tev->point);
+ /* Trace point should be converted from subprogram DIE */
+ ret = convert_to_trace_point(&pf->sp_die, pf->addr,
+ pf->pev->point.retprobe, &tev->point);
if (ret < 0)
return ret;
@@ -1091,7 +1148,8 @@
for (i = 0; i < pf->pev->nargs; i++) {
pf->pvar = &pf->pev->args[i];
pf->tvar = &tev->args[i];
- ret = find_variable(sp_die, pf);
+ /* Variable should be found from scope DIE */
+ ret = find_variable(sc_die, pf);
if (ret != 0)
return ret;
}
@@ -1159,13 +1217,13 @@
}
/* Add a found vars into available variables list */
-static int add_available_vars(Dwarf_Die *sp_die, struct probe_finder *pf)
+static int add_available_vars(Dwarf_Die *sc_die, struct probe_finder *pf)
{
struct available_var_finder *af =
container_of(pf, struct available_var_finder, pf);
struct variable_list *vl;
- Dwarf_Die die_mem, *scopes = NULL;
- int ret, nscopes;
+ Dwarf_Die die_mem;
+ int ret;
/* Check number of tevs */
if (af->nvls == af->max_vls) {
@@ -1174,8 +1232,9 @@
}
vl = &af->vls[af->nvls++];
- ret = convert_to_trace_point(sp_die, pf->addr, pf->pev->point.retprobe,
- &vl->point);
+ /* Trace point should be converted from subprogram DIE */
+ ret = convert_to_trace_point(&pf->sp_die, pf->addr,
+ pf->pev->point.retprobe, &vl->point);
if (ret < 0)
return ret;
@@ -1187,19 +1246,14 @@
if (vl->vars == NULL)
return -ENOMEM;
af->child = true;
- die_find_child(sp_die, collect_variables_cb, (void *)af, &die_mem);
+ die_find_child(sc_die, collect_variables_cb, (void *)af, &die_mem);
/* Find external variables */
if (!af->externs)
goto out;
/* Don't need to search child DIE for externs. */
af->child = false;
- nscopes = dwarf_getscopes_die(sp_die, &scopes);
- while (nscopes-- > 1)
- die_find_child(&scopes[nscopes], collect_variables_cb,
- (void *)af, &die_mem);
- if (scopes)
- free(scopes);
+ die_find_child(&pf->cu_die, collect_variables_cb, (void *)af, &die_mem);
out:
if (strlist__empty(vl->vars)) {
@@ -1391,10 +1445,14 @@
static int line_range_inline_cb(Dwarf_Die *in_die, void *data)
{
- struct dwarf_callback_param *param = data;
+ find_line_range_by_line(in_die, data);
- param->retval = find_line_range_by_line(in_die, param->data);
- return DWARF_CB_ABORT; /* No need to find other instances */
+ /*
+ * We have to check all instances of inlined function, because
+ * some execution paths can be optimized out depends on the
+ * function argument of instances
+ */
+ return 0;
}
/* Search function from function name */
@@ -1422,15 +1480,10 @@
pr_debug("New line range: %d to %d\n", lf->lno_s, lf->lno_e);
lr->start = lf->lno_s;
lr->end = lf->lno_e;
- if (dwarf_func_inline(sp_die)) {
- struct dwarf_callback_param _param;
- _param.data = (void *)lf;
- _param.retval = 0;
- dwarf_func_inline_instances(sp_die,
- line_range_inline_cb,
- &_param);
- param->retval = _param.retval;
- } else
+ if (dwarf_func_inline(sp_die))
+ param->retval = die_walk_instances(sp_die,
+ line_range_inline_cb, lf);
+ else
param->retval = find_line_range_by_line(sp_die, lf);
return DWARF_CB_ABORT;
}
diff --git a/tools/perf/util/probe-finder.h b/tools/perf/util/probe-finder.h
index c478b42..1132c8f 100644
--- a/tools/perf/util/probe-finder.h
+++ b/tools/perf/util/probe-finder.h
@@ -57,7 +57,7 @@
struct perf_probe_event *pev; /* Target probe event */
/* Callback when a probe point is found */
- int (*callback)(Dwarf_Die *sp_die, struct probe_finder *pf);
+ int (*callback)(Dwarf_Die *sc_die, struct probe_finder *pf);
/* For function searching */
int lno; /* Line number */
diff --git a/tools/perf/util/python.c b/tools/perf/util/python.c
index 8e0b5a3..cbc8f21 100644
--- a/tools/perf/util/python.c
+++ b/tools/perf/util/python.c
@@ -187,16 +187,119 @@
.tp_repr = (reprfunc)pyrf_throttle_event__repr,
};
+static char pyrf_lost_event__doc[] = PyDoc_STR("perf lost event object.");
+
+static PyMemberDef pyrf_lost_event__members[] = {
+ sample_members
+ member_def(lost_event, id, T_ULONGLONG, "event id"),
+ member_def(lost_event, lost, T_ULONGLONG, "number of lost events"),
+ { .name = NULL, },
+};
+
+static PyObject *pyrf_lost_event__repr(struct pyrf_event *pevent)
+{
+ PyObject *ret;
+ char *s;
+
+ if (asprintf(&s, "{ type: lost, id: %#" PRIx64 ", "
+ "lost: %#" PRIx64 " }",
+ pevent->event.lost.id, pevent->event.lost.lost) < 0) {
+ ret = PyErr_NoMemory();
+ } else {
+ ret = PyString_FromString(s);
+ free(s);
+ }
+ return ret;
+}
+
+static PyTypeObject pyrf_lost_event__type = {
+ PyVarObject_HEAD_INIT(NULL, 0)
+ .tp_name = "perf.lost_event",
+ .tp_basicsize = sizeof(struct pyrf_event),
+ .tp_flags = Py_TPFLAGS_DEFAULT|Py_TPFLAGS_BASETYPE,
+ .tp_doc = pyrf_lost_event__doc,
+ .tp_members = pyrf_lost_event__members,
+ .tp_repr = (reprfunc)pyrf_lost_event__repr,
+};
+
+static char pyrf_read_event__doc[] = PyDoc_STR("perf read event object.");
+
+static PyMemberDef pyrf_read_event__members[] = {
+ sample_members
+ member_def(read_event, pid, T_UINT, "event pid"),
+ member_def(read_event, tid, T_UINT, "event tid"),
+ { .name = NULL, },
+};
+
+static PyObject *pyrf_read_event__repr(struct pyrf_event *pevent)
+{
+ return PyString_FromFormat("{ type: read, pid: %u, tid: %u }",
+ pevent->event.read.pid,
+ pevent->event.read.tid);
+ /*
+ * FIXME: return the array of read values,
+ * making this method useful ;-)
+ */
+}
+
+static PyTypeObject pyrf_read_event__type = {
+ PyVarObject_HEAD_INIT(NULL, 0)
+ .tp_name = "perf.read_event",
+ .tp_basicsize = sizeof(struct pyrf_event),
+ .tp_flags = Py_TPFLAGS_DEFAULT|Py_TPFLAGS_BASETYPE,
+ .tp_doc = pyrf_read_event__doc,
+ .tp_members = pyrf_read_event__members,
+ .tp_repr = (reprfunc)pyrf_read_event__repr,
+};
+
+static char pyrf_sample_event__doc[] = PyDoc_STR("perf sample event object.");
+
+static PyMemberDef pyrf_sample_event__members[] = {
+ sample_members
+ member_def(perf_event_header, type, T_UINT, "event type"),
+ { .name = NULL, },
+};
+
+static PyObject *pyrf_sample_event__repr(struct pyrf_event *pevent)
+{
+ PyObject *ret;
+ char *s;
+
+ if (asprintf(&s, "{ type: sample }") < 0) {
+ ret = PyErr_NoMemory();
+ } else {
+ ret = PyString_FromString(s);
+ free(s);
+ }
+ return ret;
+}
+
+static PyTypeObject pyrf_sample_event__type = {
+ PyVarObject_HEAD_INIT(NULL, 0)
+ .tp_name = "perf.sample_event",
+ .tp_basicsize = sizeof(struct pyrf_event),
+ .tp_flags = Py_TPFLAGS_DEFAULT|Py_TPFLAGS_BASETYPE,
+ .tp_doc = pyrf_sample_event__doc,
+ .tp_members = pyrf_sample_event__members,
+ .tp_repr = (reprfunc)pyrf_sample_event__repr,
+};
+
static int pyrf_event__setup_types(void)
{
int err;
pyrf_mmap_event__type.tp_new =
pyrf_task_event__type.tp_new =
pyrf_comm_event__type.tp_new =
+ pyrf_lost_event__type.tp_new =
+ pyrf_read_event__type.tp_new =
+ pyrf_sample_event__type.tp_new =
pyrf_throttle_event__type.tp_new = PyType_GenericNew;
err = PyType_Ready(&pyrf_mmap_event__type);
if (err < 0)
goto out;
+ err = PyType_Ready(&pyrf_lost_event__type);
+ if (err < 0)
+ goto out;
err = PyType_Ready(&pyrf_task_event__type);
if (err < 0)
goto out;
@@ -206,20 +309,26 @@
err = PyType_Ready(&pyrf_throttle_event__type);
if (err < 0)
goto out;
+ err = PyType_Ready(&pyrf_read_event__type);
+ if (err < 0)
+ goto out;
+ err = PyType_Ready(&pyrf_sample_event__type);
+ if (err < 0)
+ goto out;
out:
return err;
}
static PyTypeObject *pyrf_event__type[] = {
[PERF_RECORD_MMAP] = &pyrf_mmap_event__type,
- [PERF_RECORD_LOST] = &pyrf_mmap_event__type,
+ [PERF_RECORD_LOST] = &pyrf_lost_event__type,
[PERF_RECORD_COMM] = &pyrf_comm_event__type,
[PERF_RECORD_EXIT] = &pyrf_task_event__type,
[PERF_RECORD_THROTTLE] = &pyrf_throttle_event__type,
[PERF_RECORD_UNTHROTTLE] = &pyrf_throttle_event__type,
[PERF_RECORD_FORK] = &pyrf_task_event__type,
- [PERF_RECORD_READ] = &pyrf_mmap_event__type,
- [PERF_RECORD_SAMPLE] = &pyrf_mmap_event__type,
+ [PERF_RECORD_READ] = &pyrf_read_event__type,
+ [PERF_RECORD_SAMPLE] = &pyrf_sample_event__type,
};
static PyObject *pyrf_event__new(union perf_event *event)
diff --git a/tools/perf/util/setup.py b/tools/perf/util/setup.py
index bbc982f..95d3700 100644
--- a/tools/perf/util/setup.py
+++ b/tools/perf/util/setup.py
@@ -3,9 +3,27 @@
from distutils.core import setup, Extension
from os import getenv
+from distutils.command.build_ext import build_ext as _build_ext
+from distutils.command.install_lib import install_lib as _install_lib
+
+class build_ext(_build_ext):
+ def finalize_options(self):
+ _build_ext.finalize_options(self)
+ self.build_lib = build_lib
+ self.build_temp = build_tmp
+
+class install_lib(_install_lib):
+ def finalize_options(self):
+ _install_lib.finalize_options(self)
+ self.build_dir = build_lib
+
+
cflags = ['-fno-strict-aliasing', '-Wno-write-strings']
cflags += getenv('CFLAGS', '').split()
+build_lib = getenv('PYTHON_EXTBUILD_LIB')
+build_tmp = getenv('PYTHON_EXTBUILD_TMP')
+
perf = Extension('perf',
sources = ['util/python.c', 'util/ctype.c', 'util/evlist.c',
'util/evsel.c', 'util/cpumap.c', 'util/thread_map.c',
@@ -21,4 +39,5 @@
author_email='acme@redhat.com',
license='GPLv2',
url='http://perf.wiki.kernel.org',
- ext_modules=[perf])
+ ext_modules=[perf],
+ cmdclass={'build_ext': build_ext, 'install_lib': install_lib})
diff --git a/tools/perf/util/symbol.c b/tools/perf/util/symbol.c
index eec1963..469c026 100644
--- a/tools/perf/util/symbol.c
+++ b/tools/perf/util/symbol.c
@@ -1504,6 +1504,17 @@
dso->adjust_symbols = 0;
if (strncmp(dso->name, "/tmp/perf-", 10) == 0) {
+ struct stat st;
+
+ if (lstat(dso->name, &st) < 0)
+ return -1;
+
+ if (st.st_uid && (st.st_uid != geteuid())) {
+ pr_warning("File %s not owned by current user or root, "
+ "ignoring it.\n", dso->name);
+ return -1;
+ }
+
ret = dso__load_perf_map(dso, map, filter);
dso->symtab_type = ret > 0 ? SYMTAB__JAVA_JIT :
SYMTAB__NOT_FOUND;
@@ -2170,27 +2181,22 @@
return ret;
}
-struct dso *dso__new_kernel(const char *name)
+static struct dso*
+dso__kernel_findnew(struct machine *machine, const char *name,
+ const char *short_name, int dso_type)
{
- struct dso *dso = dso__new(name ?: "[kernel.kallsyms]");
+ /*
+ * The kernel dso could be created by build_id processing.
+ */
+ struct dso *dso = __dsos__findnew(&machine->kernel_dsos, name);
+ /*
+ * We need to run this in all cases, since during the build_id
+ * processing we had no idea this was the kernel dso.
+ */
if (dso != NULL) {
- dso__set_short_name(dso, "[kernel]");
- dso->kernel = DSO_TYPE_KERNEL;
- }
-
- return dso;
-}
-
-static struct dso *dso__new_guest_kernel(struct machine *machine,
- const char *name)
-{
- char bf[PATH_MAX];
- struct dso *dso = dso__new(name ?: machine__mmap_name(machine, bf,
- sizeof(bf)));
- if (dso != NULL) {
- dso__set_short_name(dso, "[guest.kernel]");
- dso->kernel = DSO_TYPE_GUEST_KERNEL;
+ dso__set_short_name(dso, short_name);
+ dso->kernel = dso_type;
}
return dso;
@@ -2208,24 +2214,36 @@
dso->has_build_id = true;
}
-static struct dso *machine__create_kernel(struct machine *machine)
+static struct dso *machine__get_kernel(struct machine *machine)
{
const char *vmlinux_name = NULL;
struct dso *kernel;
if (machine__is_host(machine)) {
vmlinux_name = symbol_conf.vmlinux_name;
- kernel = dso__new_kernel(vmlinux_name);
+ if (!vmlinux_name)
+ vmlinux_name = "[kernel.kallsyms]";
+
+ kernel = dso__kernel_findnew(machine, vmlinux_name,
+ "[kernel]",
+ DSO_TYPE_KERNEL);
} else {
+ char bf[PATH_MAX];
+
if (machine__is_default_guest(machine))
vmlinux_name = symbol_conf.default_guest_vmlinux_name;
- kernel = dso__new_guest_kernel(machine, vmlinux_name);
+ if (!vmlinux_name)
+ vmlinux_name = machine__mmap_name(machine, bf,
+ sizeof(bf));
+
+ kernel = dso__kernel_findnew(machine, vmlinux_name,
+ "[guest.kernel]",
+ DSO_TYPE_GUEST_KERNEL);
}
- if (kernel != NULL) {
+ if (kernel != NULL && (!kernel->has_build_id))
dso__read_running_kernel_build_id(kernel, machine);
- dsos__add(&machine->kernel_dsos, kernel);
- }
+
return kernel;
}
@@ -2329,7 +2347,7 @@
int machine__create_kernel_maps(struct machine *machine)
{
- struct dso *kernel = machine__create_kernel(machine);
+ struct dso *kernel = machine__get_kernel(machine);
if (kernel == NULL ||
__machine__create_kernel_maps(machine, kernel) < 0)
diff --git a/tools/perf/util/symbol.h b/tools/perf/util/symbol.h
index 325ee36..4f377d9 100644
--- a/tools/perf/util/symbol.h
+++ b/tools/perf/util/symbol.h
@@ -155,7 +155,6 @@
};
struct dso *dso__new(const char *name);
-struct dso *dso__new_kernel(const char *name);
void dso__delete(struct dso *dso);
int dso__name_len(const struct dso *dso);
diff --git a/tools/perf/util/ui/browsers/top.c b/tools/perf/util/ui/browsers/top.c
index 5a06538..88403cf 100644
--- a/tools/perf/util/ui/browsers/top.c
+++ b/tools/perf/util/ui/browsers/top.c
@@ -208,6 +208,5 @@
},
};
- ui_helpline__push("Press <- or ESC to exit");
return perf_top_browser__run(&browser);
}
diff --git a/tools/power/x86/turbostat/turbostat.c b/tools/power/x86/turbostat/turbostat.c
index 6d8ef4a..8b2d37b 100644
--- a/tools/power/x86/turbostat/turbostat.c
+++ b/tools/power/x86/turbostat/turbostat.c
@@ -128,34 +128,34 @@
void print_header(void)
{
if (show_pkg)
- fprintf(stderr, "pkg ");
+ fprintf(stderr, "pk");
if (show_core)
- fprintf(stderr, "core");
+ fprintf(stderr, " cr");
if (show_cpu)
fprintf(stderr, " CPU");
if (do_nhm_cstates)
- fprintf(stderr, " %%c0 ");
+ fprintf(stderr, " %%c0 ");
if (has_aperf)
- fprintf(stderr, " GHz");
+ fprintf(stderr, " GHz");
fprintf(stderr, " TSC");
if (do_nhm_cstates)
- fprintf(stderr, " %%c1 ");
+ fprintf(stderr, " %%c1");
if (do_nhm_cstates)
- fprintf(stderr, " %%c3 ");
+ fprintf(stderr, " %%c3");
if (do_nhm_cstates)
- fprintf(stderr, " %%c6 ");
+ fprintf(stderr, " %%c6");
if (do_snb_cstates)
- fprintf(stderr, " %%c7 ");
+ fprintf(stderr, " %%c7");
if (do_snb_cstates)
- fprintf(stderr, " %%pc2 ");
+ fprintf(stderr, " %%pc2");
if (do_nhm_cstates)
- fprintf(stderr, " %%pc3 ");
+ fprintf(stderr, " %%pc3");
if (do_nhm_cstates)
- fprintf(stderr, " %%pc6 ");
+ fprintf(stderr, " %%pc6");
if (do_snb_cstates)
- fprintf(stderr, " %%pc7 ");
+ fprintf(stderr, " %%pc7");
if (extra_msr_offset)
- fprintf(stderr, " MSR 0x%x ", extra_msr_offset);
+ fprintf(stderr, " MSR 0x%x ", extra_msr_offset);
putc('\n', stderr);
}
@@ -194,14 +194,14 @@
/* topology columns, print blanks on 1st (average) line */
if (p == cnt_average) {
if (show_pkg)
- fprintf(stderr, " ");
+ fprintf(stderr, " ");
if (show_core)
fprintf(stderr, " ");
if (show_cpu)
fprintf(stderr, " ");
} else {
if (show_pkg)
- fprintf(stderr, "%4d", p->pkg);
+ fprintf(stderr, "%d", p->pkg);
if (show_core)
fprintf(stderr, "%4d", p->core);
if (show_cpu)
@@ -241,22 +241,22 @@
if (!skip_c1)
fprintf(stderr, "%7.2f", 100.0 * p->c1/p->tsc);
else
- fprintf(stderr, " ****");
+ fprintf(stderr, " ****");
}
if (do_nhm_cstates)
- fprintf(stderr, "%7.2f", 100.0 * p->c3/p->tsc);
+ fprintf(stderr, " %6.2f", 100.0 * p->c3/p->tsc);
if (do_nhm_cstates)
- fprintf(stderr, "%7.2f", 100.0 * p->c6/p->tsc);
+ fprintf(stderr, " %6.2f", 100.0 * p->c6/p->tsc);
if (do_snb_cstates)
- fprintf(stderr, "%7.2f", 100.0 * p->c7/p->tsc);
+ fprintf(stderr, " %6.2f", 100.0 * p->c7/p->tsc);
if (do_snb_cstates)
- fprintf(stderr, "%7.2f", 100.0 * p->pc2/p->tsc);
+ fprintf(stderr, " %5.2f", 100.0 * p->pc2/p->tsc);
if (do_nhm_cstates)
- fprintf(stderr, "%7.2f", 100.0 * p->pc3/p->tsc);
+ fprintf(stderr, " %5.2f", 100.0 * p->pc3/p->tsc);
if (do_nhm_cstates)
- fprintf(stderr, "%7.2f", 100.0 * p->pc6/p->tsc);
+ fprintf(stderr, " %5.2f", 100.0 * p->pc6/p->tsc);
if (do_snb_cstates)
- fprintf(stderr, "%7.2f", 100.0 * p->pc7/p->tsc);
+ fprintf(stderr, " %5.2f", 100.0 * p->pc7/p->tsc);
if (extra_msr_offset)
fprintf(stderr, " 0x%016llx", p->extra_msr);
putc('\n', stderr);
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
index 2618ef2..33c5c7e 100644
--- 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
@@ -137,7 +137,6 @@
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;
@@ -160,8 +159,8 @@
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,
+ printf("CPUID %d levels family:model:stepping "
+ "0x%x:%x:%x (%d:%d:%d)\n", max_level,
family, model, stepping, family, model, stepping);
if (!(edx & (1 << 5))) {